<commit_before>package main

import (
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"gopkg.in/lxc/go-lxc.v2"

	"github.com/lxc/lxd/shared"

	log "gopkg.in/inconshreveable/log15.v2"
)

var containersCmd = Command{
	name: "containers",
	get:  containersGet,
	post: containersPost,
}

var containerCmd = Command{
	name:   "containers/{name}",
	get:    containerGet,
	put:    containerPut,
	delete: containerDelete,
	post:   containerPost,
	patch:  containerPatch,
}

var containerStateCmd = Command{
	name: "containers/{name}/state",
	get:  containerState,
	put:  containerStatePut,
}

var containerFileCmd = Command{
	name: "containers/{name}/files",
	get:  containerFileHandler,
	post: containerFileHandler,
}

var containerSnapshotsCmd = Command{
	name: "containers/{name}/snapshots",
	get:  containerSnapshotsGet,
	post: containerSnapshotsPost,
}

var containerSnapshotCmd = Command{
	name:   "containers/{name}/snapshots/{snapshotName}",
	get:    snapshotHandler,
	post:   snapshotHandler,
	delete: snapshotHandler,
}

var containerExecCmd = Command{
	name: "containers/{name}/exec",
	post: containerExecPost,
}

type containerAutostartList []container

func (slice containerAutostartList) Len() int {
	return len(slice)
}

func (slice containerAutostartList) Less(i, j int) bool {
	iOrder := slice[i].ExpandedConfig()["boot.autostart.priority"]
	jOrder := slice[j].ExpandedConfig()["boot.autostart.priority"]

	if iOrder != jOrder {
		iOrderInt, _ := strconv.Atoi(iOrder)
		jOrderInt, _ := strconv.Atoi(jOrder)
		return iOrderInt > jOrderInt
	}

	return slice[i].Name() < slice[j].Name()
}

func (slice containerAutostartList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func containersRestart(d *Daemon) error {
	// Get all the containers
	result, err := dbContainersList(d.db, cTypeRegular)
	if err != nil {
		return err
	}

	containers := []container{}

	for _, name := range result {
		c, err := containerLoadByName(d, name)
		if err != nil {
			return err
		}

		containers = append(containers, c)
	}

	sort.Sort(containerAutostartList(containers))

	// Restart the containers
	for _, c := range containers {
		config := c.ExpandedConfig()
		lastState := config["volatile.last_state.power"]

		autoStart := config["boot.autostart"]
		autoStartDelay := config["boot.autostart.delay"]

		if shared.IsTrue(autoStart) || (autoStart == "" && lastState == "RUNNING") {
			if c.IsRunning() {
				continue
			}

			c.Start(false)

			autoStartDelayInt, err := strconv.Atoi(autoStartDelay)
			if err == nil {
				time.Sleep(time.Duration(autoStartDelayInt) * time.Second)
			}
		}
	}

	// Reset the recorded state (to ensure it's up to date)
	_, err = dbExec(d.db, "DELETE FROM containers_config WHERE key='volatile.last_state.power'")
	if err != nil {
		return err
	}

	for _, c := range containers {
		err = c.ConfigKeySet("volatile.last_state.power", c.State())
		if err != nil {
			return err
		}
	}

	return nil
}

func containersShutdown(d *Daemon) error {
	var wg sync.WaitGroup

	// Get all the containers
	results, err := dbContainersList(d.db, cTypeRegular)
	if err != nil {
		return err
	}

	// Reset all container states
	_, err = dbExec(d.db, "DELETE FROM containers_config WHERE key='volatile.last_state.power'")
	if err != nil {
		return err
	}

	for _, r := range results {
		// Load the container
		c, err := containerLoadByName(d, r)
		if err != nil {
			return err
		}

		// Record the current state
		err = c.ConfigKeySet("volatile.last_state.power", c.State())
		if err != nil {
			return err
		}

		// Stop the container
		if c.IsRunning() {
			// Determinate how long to wait for the container to shutdown cleanly
			var timeoutSeconds int
			value, ok := c.ExpandedConfig()["boot.host_shutdown_timeout"]
			if ok {
				timeoutSeconds, _ = strconv.Atoi(value)
			} else {
				timeoutSeconds = 30
			}

			// Stop the container
			wg.Add(1)
			go func() {
				c.Shutdown(time.Second * time.Duration(timeoutSeconds))
				c.Stop(false)
				wg.Done()
			}()
		}
	}
	wg.Wait()

	return nil
}

func containerDeleteSnapshots(d *Daemon, cname string) error {
	shared.LogDebug("containerDeleteSnapshots",
		log.Ctx{"container": cname})

	results, err := dbContainerGetSnapshots(d.db, cname)
	if err != nil {
		return err
	}

	for _, sname := range results {
		sc, err := containerLoadByName(d, sname)
		if err != nil {
			shared.LogError(
				"containerDeleteSnapshots: Failed to load the snapshotcontainer",
				log.Ctx{"container": cname, "snapshot": sname})

			continue
		}

		if err := sc.Delete(); err != nil {
			shared.LogError(
				"containerDeleteSnapshots: Failed to delete a snapshotcontainer",
				log.Ctx{"container": cname, "snapshot": sname, "err": err})
		}
	}

	return nil
}

/*
 * This is called by lxd when called as "lxd forkstart <container>"
 * 'forkstart' is used instead of just 'start' in the hopes that people
 * do not accidentally type 'lxd start' instead of 'lxc start'
 */
func startContainer(args []string) error {
	if len(args) != 4 {
		return fmt.Errorf("Bad arguments: %q", args)
	}

	name := args[1]
	lxcpath := args[2]
	configPath := args[3]

	c, err := lxc.NewContainer(name, lxcpath)
	if err != nil {
		return fmt.Errorf("Error initializing container for start: %q", err)
	}

	err = c.LoadConfigFile(configPath)
	if err != nil {
		return fmt.Errorf("Error opening startup config file: %q", err)
	}

	/* due to https://github.com/golang/go/issues/13155 and the
	 * CollectOutput call we make for the forkstart process, we need to
	 * close our stdin/stdout/stderr here. Collecting some of the logs is
	 * better than collecting no logs, though.
	 */
	os.Stdin.Close()
	os.Stderr.Close()
	os.Stdout.Close()

	// Redirect stdout and stderr to a log file
	logPath := shared.LogPath(name, "forkstart.log")
	if shared.PathExists(logPath) {
		os.Remove(logPath)
	}

	logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644)
	if err == nil {
		syscall.Dup3(int(logFile.Fd()), 1, 0)
		syscall.Dup3(int(logFile.Fd()), 2, 0)
	}

	return c.Start()
}

/*
 * This is called by lxd when called as "lxd forkexec <container>"
 */
func execContainer(args []string) (int, error) {
	if len(args) < 6 {
		return -1, fmt.Errorf("Bad arguments: %q", args)
	}

	name := args[1]
	lxcpath := args[2]
	configPath := args[3]

	c, err := lxc.NewContainer(name, lxcpath)
	if err != nil {
		return -1, fmt.Errorf("Error initializing container for start: %q", err)
	}

	err = c.LoadConfigFile(configPath)
	if err != nil {
		return -1, fmt.Errorf("Error opening startup config file: %q", err)
	}

	syscall.Dup3(int(os.Stdin.Fd()), 200, 0)
	syscall.Dup3(int(os.Stdout.Fd()), 201, 0)
	syscall.Dup3(int(os.Stderr.Fd()), 202, 0)

	syscall.Close(int(os.Stdin.Fd()))
	syscall.Close(int(os.Stdout.Fd()))
	syscall.Close(int(os.Stderr.Fd()))

	opts := lxc.DefaultAttachOptions
	opts.ClearEnv = true
	opts.StdinFd = 200
	opts.StdoutFd = 201
	opts.StderrFd = 202

	logPath := shared.LogPath(name, "forkexec.log")
	if shared.PathExists(logPath) {
		os.Remove(logPath)
	}

	logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644)
	if err == nil {
		syscall.Dup3(int(logFile.Fd()), 1, 0)
		syscall.Dup3(int(logFile.Fd()), 2, 0)
	}

	env := []string{}
	cmd := []string{}

	section := ""
	for _, arg := range args[5:len(args)] {
		// The "cmd" section must come last as it may contain a --
		if arg == "--" && section != "cmd" {
			section = ""
			continue
		}

		if section == "" {
			section = arg
			continue
		}

		if section == "env" {
			fields := strings.SplitN(arg, "=", 2)
			if len(fields) == 2 && fields[0] == "HOME" {
				opts.Cwd = fields[1]
			}
			env = append(env, arg)
		} else if section == "cmd" {
			cmd = append(cmd, arg)
		} else {
			return -1, fmt.Errorf("Invalid exec section: %s", section)
		}
	}

	opts.Env = env

	status, err := c.RunCommandStatus(cmd, opts)
	if err != nil {
		return -1, fmt.Errorf("Failed running command: %q", err)
	}

	return status >> 8, nil
}
<commit_msg>lxd/containers: call RunCommandNoWait()<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"gopkg.in/lxc/go-lxc.v2"

	"github.com/lxc/lxd/shared"

	log "gopkg.in/inconshreveable/log15.v2"
)

var containersCmd = Command{
	name: "containers",
	get:  containersGet,
	post: containersPost,
}

var containerCmd = Command{
	name:   "containers/{name}",
	get:    containerGet,
	put:    containerPut,
	delete: containerDelete,
	post:   containerPost,
	patch:  containerPatch,
}

var containerStateCmd = Command{
	name: "containers/{name}/state",
	get:  containerState,
	put:  containerStatePut,
}

var containerFileCmd = Command{
	name: "containers/{name}/files",
	get:  containerFileHandler,
	post: containerFileHandler,
}

var containerSnapshotsCmd = Command{
	name: "containers/{name}/snapshots",
	get:  containerSnapshotsGet,
	post: containerSnapshotsPost,
}

var containerSnapshotCmd = Command{
	name:   "containers/{name}/snapshots/{snapshotName}",
	get:    snapshotHandler,
	post:   snapshotHandler,
	delete: snapshotHandler,
}

var containerExecCmd = Command{
	name: "containers/{name}/exec",
	post: containerExecPost,
}

type containerAutostartList []container

func (slice containerAutostartList) Len() int {
	return len(slice)
}

func (slice containerAutostartList) Less(i, j int) bool {
	iOrder := slice[i].ExpandedConfig()["boot.autostart.priority"]
	jOrder := slice[j].ExpandedConfig()["boot.autostart.priority"]

	if iOrder != jOrder {
		iOrderInt, _ := strconv.Atoi(iOrder)
		jOrderInt, _ := strconv.Atoi(jOrder)
		return iOrderInt > jOrderInt
	}

	return slice[i].Name() < slice[j].Name()
}

func (slice containerAutostartList) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func containersRestart(d *Daemon) error {
	// Get all the containers
	result, err := dbContainersList(d.db, cTypeRegular)
	if err != nil {
		return err
	}

	containers := []container{}

	for _, name := range result {
		c, err := containerLoadByName(d, name)
		if err != nil {
			return err
		}

		containers = append(containers, c)
	}

	sort.Sort(containerAutostartList(containers))

	// Restart the containers
	for _, c := range containers {
		config := c.ExpandedConfig()
		lastState := config["volatile.last_state.power"]

		autoStart := config["boot.autostart"]
		autoStartDelay := config["boot.autostart.delay"]

		if shared.IsTrue(autoStart) || (autoStart == "" && lastState == "RUNNING") {
			if c.IsRunning() {
				continue
			}

			c.Start(false)

			autoStartDelayInt, err := strconv.Atoi(autoStartDelay)
			if err == nil {
				time.Sleep(time.Duration(autoStartDelayInt) * time.Second)
			}
		}
	}

	// Reset the recorded state (to ensure it's up to date)
	_, err = dbExec(d.db, "DELETE FROM containers_config WHERE key='volatile.last_state.power'")
	if err != nil {
		return err
	}

	for _, c := range containers {
		err = c.ConfigKeySet("volatile.last_state.power", c.State())
		if err != nil {
			return err
		}
	}

	return nil
}

func containersShutdown(d *Daemon) error {
	var wg sync.WaitGroup

	// Get all the containers
	results, err := dbContainersList(d.db, cTypeRegular)
	if err != nil {
		return err
	}

	// Reset all container states
	_, err = dbExec(d.db, "DELETE FROM containers_config WHERE key='volatile.last_state.power'")
	if err != nil {
		return err
	}

	for _, r := range results {
		// Load the container
		c, err := containerLoadByName(d, r)
		if err != nil {
			return err
		}

		// Record the current state
		err = c.ConfigKeySet("volatile.last_state.power", c.State())
		if err != nil {
			return err
		}

		// Stop the container
		if c.IsRunning() {
			// Determinate how long to wait for the container to shutdown cleanly
			var timeoutSeconds int
			value, ok := c.ExpandedConfig()["boot.host_shutdown_timeout"]
			if ok {
				timeoutSeconds, _ = strconv.Atoi(value)
			} else {
				timeoutSeconds = 30
			}

			// Stop the container
			wg.Add(1)
			go func() {
				c.Shutdown(time.Second * time.Duration(timeoutSeconds))
				c.Stop(false)
				wg.Done()
			}()
		}
	}
	wg.Wait()

	return nil
}

func containerDeleteSnapshots(d *Daemon, cname string) error {
	shared.LogDebug("containerDeleteSnapshots",
		log.Ctx{"container": cname})

	results, err := dbContainerGetSnapshots(d.db, cname)
	if err != nil {
		return err
	}

	for _, sname := range results {
		sc, err := containerLoadByName(d, sname)
		if err != nil {
			shared.LogError(
				"containerDeleteSnapshots: Failed to load the snapshotcontainer",
				log.Ctx{"container": cname, "snapshot": sname})

			continue
		}

		if err := sc.Delete(); err != nil {
			shared.LogError(
				"containerDeleteSnapshots: Failed to delete a snapshotcontainer",
				log.Ctx{"container": cname, "snapshot": sname, "err": err})
		}
	}

	return nil
}

/*
 * This is called by lxd when called as "lxd forkstart <container>"
 * 'forkstart' is used instead of just 'start' in the hopes that people
 * do not accidentally type 'lxd start' instead of 'lxc start'
 */
func startContainer(args []string) error {
	if len(args) != 4 {
		return fmt.Errorf("Bad arguments: %q", args)
	}

	name := args[1]
	lxcpath := args[2]
	configPath := args[3]

	c, err := lxc.NewContainer(name, lxcpath)
	if err != nil {
		return fmt.Errorf("Error initializing container for start: %q", err)
	}

	err = c.LoadConfigFile(configPath)
	if err != nil {
		return fmt.Errorf("Error opening startup config file: %q", err)
	}

	/* due to https://github.com/golang/go/issues/13155 and the
	 * CollectOutput call we make for the forkstart process, we need to
	 * close our stdin/stdout/stderr here. Collecting some of the logs is
	 * better than collecting no logs, though.
	 */
	os.Stdin.Close()
	os.Stderr.Close()
	os.Stdout.Close()

	// Redirect stdout and stderr to a log file
	logPath := shared.LogPath(name, "forkstart.log")
	if shared.PathExists(logPath) {
		os.Remove(logPath)
	}

	logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644)
	if err == nil {
		syscall.Dup3(int(logFile.Fd()), 1, 0)
		syscall.Dup3(int(logFile.Fd()), 2, 0)
	}

	return c.Start()
}

/*
 * This is called by lxd when called as "lxd forkexec <container>"
 */
func execContainer(args []string) (int, error) {
	if len(args) < 6 {
		return -1, fmt.Errorf("Bad arguments: %q", args)
	}

	wait := true
	if args[1] == "nowait" {
		wait = false
	}
	name := args[2]
	lxcpath := args[3]
	configPath := args[4]

	c, err := lxc.NewContainer(name, lxcpath)
	if err != nil {
		return -1, fmt.Errorf("Error initializing container for start: %q", err)
	}

	err = c.LoadConfigFile(configPath)
	if err != nil {
		return -1, fmt.Errorf("Error opening startup config file: %q", err)
	}

	syscall.Dup3(int(os.Stdin.Fd()), 200, 0)
	syscall.Dup3(int(os.Stdout.Fd()), 201, 0)
	syscall.Dup3(int(os.Stderr.Fd()), 202, 0)

	syscall.Close(int(os.Stdin.Fd()))
	syscall.Close(int(os.Stdout.Fd()))
	syscall.Close(int(os.Stderr.Fd()))

	opts := lxc.DefaultAttachOptions
	opts.ClearEnv = true
	opts.StdinFd = 200
	opts.StdoutFd = 201
	opts.StderrFd = 202

	logPath := shared.LogPath(name, "forkexec.log")
	if shared.PathExists(logPath) {
		os.Remove(logPath)
	}

	logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644)
	if err == nil {
		syscall.Dup3(int(logFile.Fd()), 1, 0)
		syscall.Dup3(int(logFile.Fd()), 2, 0)
	}

	env := []string{}
	cmd := []string{}

	section := ""
	for _, arg := range args[5:len(args)] {
		// The "cmd" section must come last as it may contain a --
		if arg == "--" && section != "cmd" {
			section = ""
			continue
		}

		if section == "" {
			section = arg
			continue
		}

		if section == "env" {
			fields := strings.SplitN(arg, "=", 2)
			if len(fields) == 2 && fields[0] == "HOME" {
				opts.Cwd = fields[1]
			}
			env = append(env, arg)
		} else if section == "cmd" {
			cmd = append(cmd, arg)
		} else {
			return -1, fmt.Errorf("Invalid exec section: %s", section)
		}
	}

	opts.Env = env

	var status int
	if wait {
		status, err = c.RunCommandStatus(cmd, opts)
		if err != nil {
			return -1, fmt.Errorf("Failed running command and waiting for it to exit: %q", err)
		}
	} else {
		status, err = c.RunCommandNoWait(cmd, opts)
		if err != nil {
			return -1, fmt.Errorf("Failed running command: %q", err)
		}
		// Send the PID of the executing process.
		w := os.NewFile(uintptr(3), "attachedPid")
		defer w.Close()

		err = json.NewEncoder(w).Encode(status)
		if err != nil {
			return -1, fmt.Errorf("Failed sending PID of executing command: %q", err)
		}

		proc, err := os.FindProcess(status)
		if err != nil {
			return -1, fmt.Errorf("Failed finding process: %q", err)
		}

		procState, err := proc.Wait()
		if err != nil {
			return -1, fmt.Errorf("Failed waiting on process %d: %q", status, err)
		}

		if procState.Success() {
			return 0, nil
		}

		status, ok := procState.Sys().(syscall.WaitStatus)
		if ok {
			if status.Exited() {
				return status.ExitStatus(), nil
			}
			// Backwards compatible behavior. Report success when we exited
			// due to a signal. Otherwise this may break Jenkins, e.g. when
			// lxc exec foo reboot receives SIGTERM and status.Exitstats()
			// would report -1.
			if status.Signaled() {
				return 0, nil
			}
		}

		return -1, fmt.Errorf("Command failed")
	}

	return status >> 8, nil
}
<|endoftext|>


<commit_before>package taggolib

import (
	"bytes"
	"errors"
	"io"
	"time"
)

const (
	// These constants represent the built-in tags
	tagAlbum       = "ALBUM"
	tagAlbumArtist = "ALBUMARTIST"
	tagArtist      = "ARTIST"
	tagComment     = "COMMENT"
	tagDate        = "DATE"
	tagDiscNumber  = "DISCNUMBER"
	tagGenre       = "GENRE"
	tagTitle       = "TITLE"
	tagTrackNumber = "TRACKNUMBER"
)

var (
	// ErrInvalidStream is returned when taggolib encounters a broken input stream
	ErrInvalidStream = errors.New("taggolib: invalid input stream")
	// ErrUnknownFormat is returned when taggolib cannot recognize the input stream format
	ErrUnknownFormat = errors.New("taggolib: unknown format")
)

// Parser represents an audio metadata tag parser
type Parser interface {
	Album() string
	AlbumArtist() string
	Artist() string
	BitDepth() int
	Bitrate() int
	Channels() int
	Comment() string
	Date() string
	DiscNumber() int
	Duration() time.Duration
	Encoder() string
	Format() string
	Genre() string
	SampleRate() int
	Tag(name string) string
	Title() string
	TrackNumber() int
}

// New creates a new Parser depending on the magic number detected in the input reader
func New(reader io.ReadSeeker) (Parser, error) {
	// Read first byte to begin checking magic number
	first := make([]byte, 1)
	if _, err := reader.Read(first); err != nil {
		return nil, err
	}

	// Check for FLAC magic number
	if bytes.Equal(first, []byte("f")) {
		// Read next 3 bytes for magic number
		magic := make([]byte, 3)
		if _, err := reader.Read(magic); err != nil {
			return nil, err
		}

		// Verify FLAC magic number
		if bytes.Equal(append(first, magic...), flacMagicNumber) {
			return newFLACParser(reader)
		}
	}

	// Check for MP3 magic number
	if bytes.Equal(first, []byte("I")) {
		// Read next 2 bytes for magic number
		magic := make([]byte, 2)
		if _, err := reader.Read(magic); err != nil {
			return nil, err
		}

		// Verify MP3 magic number
		if bytes.Equal(append(first, magic...), mp3MagicNumber) {
			return newMP3Parser(reader)
		}
	}

	// Unrecognized magic number
	return nil, ErrUnknownFormat
}
<commit_msg>Magic number parsing optimizations, 200ns speedup<commit_after>package taggolib

import (
	"bytes"
	"errors"
	"io"
	"time"
)

const (
	// These constants represent the built-in tags
	tagAlbum       = "ALBUM"
	tagAlbumArtist = "ALBUMARTIST"
	tagArtist      = "ARTIST"
	tagComment     = "COMMENT"
	tagDate        = "DATE"
	tagDiscNumber  = "DISCNUMBER"
	tagGenre       = "GENRE"
	tagTitle       = "TITLE"
	tagTrackNumber = "TRACKNUMBER"
)

var (
	// ErrInvalidStream is returned when taggolib encounters a broken input stream
	ErrInvalidStream = errors.New("taggolib: invalid input stream")
	// ErrUnknownFormat is returned when taggolib cannot recognize the input stream format
	ErrUnknownFormat = errors.New("taggolib: unknown format")
)

// Parser represents an audio metadata tag parser
type Parser interface {
	Album() string
	AlbumArtist() string
	Artist() string
	BitDepth() int
	Bitrate() int
	Channels() int
	Comment() string
	Date() string
	DiscNumber() int
	Duration() time.Duration
	Encoder() string
	Format() string
	Genre() string
	SampleRate() int
	Tag(name string) string
	Title() string
	TrackNumber() int
}

// New creates a new Parser depending on the magic number detected in the input reader
func New(reader io.ReadSeeker) (Parser, error) {
	// Check for magic numbers
	magicBuf := make([]byte, 8)

	// Read first byte to begin checking magic number
	if _, err := reader.Read(magicBuf[:1]); err != nil {
		return nil, err
	}

	// Check for FLAC magic number
	if magicBuf[0] == byte('f') {
		// Read next 3 bytes for magic number
		if _, err := reader.Read(magicBuf[1:len(flacMagicNumber)]); err != nil {
			return nil, err
		}

		// Verify FLAC magic number
		if bytes.Equal(magicBuf[:len(flacMagicNumber)], flacMagicNumber) {
			return newFLACParser(reader)
		}
	}

	// Check for MP3 magic number
	if magicBuf[0] == byte('I') {
		// Read next 2 bytes for magic number
		if _, err := reader.Read(magicBuf[1:len(mp3MagicNumber)]); err != nil {
			return nil, err
		}

		// Verify MP3 magic number
		if bytes.Equal(magicBuf[:len(mp3MagicNumber)], mp3MagicNumber) {
			return newMP3Parser(reader)
		}
	}

	// Unrecognized magic number
	return nil, ErrUnknownFormat
}
<|endoftext|>


<commit_before>package storage_test

import (
	"testing"
	"github.com/viant/toolbox/storage"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
)

func TestNewHttpStorageService(t *testing.T) {
	credentialFile := ""
	sourceService, err := storage.NewServiceForURL("https://github.com/viant/", credentialFile)
	assert.Nil(t, err)
	assert.NotNil(t, sourceService)
	objects, err := sourceService.List("https://github.com/viant/")
	assert.True(t, len(objects) > 0)

	reader, err := sourceService.Download(objects[0])
	assert.Nil(t, err)
	content, err := ioutil.ReadAll(reader)
	assert.Nil(t, err)
	assert.True(t, len(content) > 0)

}<commit_msg>added optional md5 computation<commit_after>package storage_test

import (
	"testing"
	"github.com/viant/toolbox/storage"
	"github.com/stretchr/testify/assert"

	"io/ioutil"
)

func TestNewHttpStorageService(t *testing.T) {
	credentialFile := ""
	sourceService, err := storage.NewServiceForURL("https://github.com/viant/", credentialFile)
	assert.Nil(t, err)
	assert.NotNil(t, sourceService)

	objects, err := sourceService.List("https://github.com/viant/")
	assert.True(t, len(objects) > 0)

	reader, err := sourceService.Download(objects[0])
	assert.Nil(t, err)
	content, err := ioutil.ReadAll(reader)
	assert.Nil(t, err)
	assert.True(t, len(content) > 0)

}<|endoftext|>


<commit_before>package rc

import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/ghodss/yaml"
)

type Config struct {
	Current string                 `yaml:"current"`
	Targets map[string]interface{} `yaml:"targets"`
	Aliases map[string]string      `yaml:"aliases"`
}

func saferc() string {
	return fmt.Sprintf("%s/.saferc", os.Getenv("HOME"))
}

func svtoken() string {
	return fmt.Sprintf("%s/.svtoken", os.Getenv("HOME"))
}

func (c *Config) credentials() (string, string, error) {
	if c.Current == "" {
		return "", "", nil
	}

	url, ok := c.Aliases[c.Current]
	if !ok {
		return "", "", fmt.Errorf("Current target vault '%s' not found in ~/.saferc", c.Current)
	}

	t, ok := c.Targets[url]
	if !ok {
		return "", "", fmt.Errorf("Current target vault '%s' not found in ~/.saferc", c.Current)
	}

	token := ""
	if t != nil {
		token = t.(string)
	}

	return url, token, nil
}

func Apply() Config {
	var c Config

	b, err := ioutil.ReadFile(saferc())
	if err != nil {
		return c
	}

	err = yaml.Unmarshal(b, &c)
	if err != nil {
		return c
	}

	c.Apply()
	return c
}

func (c *Config) Write() error {
	b, err := yaml.Marshal(c)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(saferc(), b, 0600)
	if err != nil {
		return err
	}

	url, token, err := c.credentials()
	if err != nil {
		return err
	}

	b, err = yaml.Marshal(
		struct {
			URL   string `json:"vault"`
			Token string `json:"token"`
		}{url, token})
	if err != nil {
		return err
	}

	return ioutil.WriteFile(svtoken(), b, 0600)
}

func (c *Config) Apply() error {
	url, token, err := c.credentials()
	if err != nil {
		return err
	}

	os.Setenv("VAULT_ADDR", url)
	os.Setenv("VAULT_TOKEN", token)
	return nil
}

func (c *Config) SetCurrent(alias string) error {
	if _, ok := c.Aliases[alias]; ok {
		c.Current = alias
		return nil
	}
	return fmt.Errorf("Unknown target '%s'", alias)
}

func (c *Config) SetTarget(alias, url string) error {
	if c.Aliases == nil {
		c.Aliases = make(map[string]string)
	}
	if c.Targets == nil {
		c.Targets = make(map[string]interface{})
	}
	c.Aliases[alias] = url
	c.Current = alias
	if _, ok := c.Targets[url]; !ok {
		c.Targets[url] = nil
	}
	return nil
}

func (c *Config) SetToken(token string) error {
	if c.Current == "" {
		return fmt.Errorf("No target selected")
	}
	url, ok := c.Aliases[c.Current]
	if !ok {
		return fmt.Errorf("Unknown target '%s'", c.Current)
	}
	c.Targets[url] = token
	return nil
}

func (c *Config) URL() string {
	if url, ok := c.Aliases[c.Current]; ok {
		return url
	}
	return ""
}
<commit_msg>Fallback to VAULT_ADDR and ~/.vault-token<commit_after>package rc

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/ghodss/yaml"
)

type Config struct {
	Current string                 `yaml:"current"`
	Targets map[string]interface{} `yaml:"targets"`
	Aliases map[string]string      `yaml:"aliases"`
}

func saferc() string {
	return fmt.Sprintf("%s/.saferc", os.Getenv("HOME"))
}

func svtoken() string {
	return fmt.Sprintf("%s/.svtoken", os.Getenv("HOME"))
}

func (c *Config) credentials() (string, string, error) {
	if c.Current == "" {
		return "", "", nil
	}

	url, ok := c.Aliases[c.Current]
	if !ok {
		return "", "", fmt.Errorf("Current target vault '%s' not found in ~/.saferc", c.Current)
	}

	t, ok := c.Targets[url]
	if !ok {
		return "", "", fmt.Errorf("Current target vault '%s' not found in ~/.saferc", c.Current)
	}

	token := ""
	if t != nil {
		token = t.(string)
	}

	return url, token, nil
}

func Apply() Config {
	var c Config

	b, err := ioutil.ReadFile(saferc())
	if err == nil {
		yaml.Unmarshal(b, &c)
	}

	c.Apply()
	return c
}

func (c *Config) Write() error {
	b, err := yaml.Marshal(c)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(saferc(), b, 0600)
	if err != nil {
		return err
	}

	url, token, err := c.credentials()
	if err != nil {
		return err
	}

	b, err = yaml.Marshal(
		struct {
			URL   string `json:"vault"`
			Token string `json:"token"`
		}{url, token})
	if err != nil {
		return err
	}

	return ioutil.WriteFile(svtoken(), b, 0600)
}

func (c *Config) Apply() error {
	url, token, err := c.credentials()
	if err != nil {
		return err
	}

	if url != "" {
		os.Setenv("VAULT_ADDR", url)
		os.Setenv("VAULT_TOKEN", token)
	} else {
		if os.Getenv("VAULT_TOKEN") == "" {
			tokenFile := fmt.Sprintf("%s/.vault-token", os.Getenv("HOME"))
			b, err := ioutil.ReadFile(tokenFile)
			if err == nil {
				os.Setenv("VAULT_TOKEN", strings.TrimSpace(string(b)))
			}
		}
	}
	return nil
}

func (c *Config) SetCurrent(alias string) error {
	if _, ok := c.Aliases[alias]; ok {
		c.Current = alias
		return nil
	}
	return fmt.Errorf("Unknown target '%s'", alias)
}

func (c *Config) SetTarget(alias, url string) error {
	if c.Aliases == nil {
		c.Aliases = make(map[string]string)
	}
	if c.Targets == nil {
		c.Targets = make(map[string]interface{})
	}
	c.Aliases[alias] = url
	c.Current = alias
	if _, ok := c.Targets[url]; !ok {
		c.Targets[url] = nil
	}
	return nil
}

func (c *Config) SetToken(token string) error {
	if c.Current == "" {
		return fmt.Errorf("No target selected")
	}
	url, ok := c.Aliases[c.Current]
	if !ok {
		return fmt.Errorf("Unknown target '%s'", c.Current)
	}
	c.Targets[url] = token
	return nil
}

func (c *Config) URL() string {
	if url, ok := c.Aliases[c.Current]; ok {
		return url
	}
	return ""
}
<|endoftext|>


<commit_before>// Copyright 2018 The CUE 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 build defines data types and utilities for defining CUE configuration
// instances.
//
// This package enforces the rules regarding packages and instances as defined
// in the spec, but it leaves any other details, as well as handling of modules,
// up to the implementation.
//
// A full implementation of instance loading can be found in the loader package.
//
// WARNING: this packages may change. It is fine to use load and cue, who both
// use this package.
package build

import (
	"context"

	"cuelang.org/go/cue/ast"
)

// A Context keeps track of state of building instances and caches work.
type Context struct {
	ctxt context.Context

	loader    LoadFunc
	parseFunc func(str string, src interface{}) (*ast.File, error)

	initialized bool

	imports map[string]*Instance
}

// NewInstance creates an instance for this Context.
func (c *Context) NewInstance(dir string, f LoadFunc) *Instance {
	if c == nil {
		c = &Context{}
	}
	if f == nil {
		f = c.loader
	}
	return &Instance{
		ctxt:     c,
		loadFunc: f,
		Dir:      dir,
	}
}

// Complete finishes the initialization of an instance. All files must have
// been added with AddFile before this call.
func (inst *Instance) Complete() error {
	if inst.done {
		return inst.Err
	}
	inst.done = true

	err := inst.complete()
	if err != nil {
		inst.ReportError(err)
	}
	if inst.Err != nil {
		inst.Incomplete = true
		return inst.Err
	}
	return nil
}

func (c *Context) init() {
	if !c.initialized {
		c.initialized = true
		c.ctxt = context.Background()
		c.initialized = true
		c.imports = map[string]*Instance{}
	}
}

// Options:
// - certain parse modes
// - parallellism
// - error handler (allows cancelling the context)
// - file set.

// NewContext creates a new build context.
//
// All instances must be created with a context.
func NewContext(opts ...Option) *Context {
	c := &Context{}
	for _, o := range opts {
		o(c)
	}
	c.init()
	return c
}

// Option define build options.
type Option func(c *Context)

// Loader sets parsing options.
func Loader(f LoadFunc) Option {
	return func(c *Context) { c.loader = f }
}

// ParseFile is called to read and parse each file
// when building syntax tree.
// It must be safe to call ParseFile simultaneously from multiple goroutines.
// If ParseFile is nil, the loader will uses parser.ParseFile.
//
// ParseFile should parse the source from src and use filename only for
// recording position information.
//
// An application may supply a custom implementation of ParseFile
// to change the effective file contents or the behavior of the parser,
// or to modify the syntax tree. For example, changing the backwards
// compatibility.
func ParseFile(f func(filename string, src interface{}) (*ast.File, error)) Option {
	return func(c *Context) { c.parseFunc = f }
}
<commit_msg>cue/build: remove unnecessary duplicate initialized<commit_after>// Copyright 2018 The CUE 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 build defines data types and utilities for defining CUE configuration
// instances.
//
// This package enforces the rules regarding packages and instances as defined
// in the spec, but it leaves any other details, as well as handling of modules,
// up to the implementation.
//
// A full implementation of instance loading can be found in the loader package.
//
// WARNING: this packages may change. It is fine to use load and cue, who both
// use this package.
package build

import (
	"context"

	"cuelang.org/go/cue/ast"
)

// A Context keeps track of state of building instances and caches work.
type Context struct {
	ctxt context.Context

	loader    LoadFunc
	parseFunc func(str string, src interface{}) (*ast.File, error)

	initialized bool

	imports map[string]*Instance
}

// NewInstance creates an instance for this Context.
func (c *Context) NewInstance(dir string, f LoadFunc) *Instance {
	if c == nil {
		c = &Context{}
	}
	if f == nil {
		f = c.loader
	}
	return &Instance{
		ctxt:     c,
		loadFunc: f,
		Dir:      dir,
	}
}

// Complete finishes the initialization of an instance. All files must have
// been added with AddFile before this call.
func (inst *Instance) Complete() error {
	if inst.done {
		return inst.Err
	}
	inst.done = true

	err := inst.complete()
	if err != nil {
		inst.ReportError(err)
	}
	if inst.Err != nil {
		inst.Incomplete = true
		return inst.Err
	}
	return nil
}

func (c *Context) init() {
	if !c.initialized {
		c.initialized = true
		c.ctxt = context.Background()
		c.imports = map[string]*Instance{}
	}
}

// Options:
// - certain parse modes
// - parallellism
// - error handler (allows cancelling the context)
// - file set.

// NewContext creates a new build context.
//
// All instances must be created with a context.
func NewContext(opts ...Option) *Context {
	c := &Context{}
	for _, o := range opts {
		o(c)
	}
	c.init()
	return c
}

// Option define build options.
type Option func(c *Context)

// Loader sets parsing options.
func Loader(f LoadFunc) Option {
	return func(c *Context) { c.loader = f }
}

// ParseFile is called to read and parse each file
// when building syntax tree.
// It must be safe to call ParseFile simultaneously from multiple goroutines.
// If ParseFile is nil, the loader will uses parser.ParseFile.
//
// ParseFile should parse the source from src and use filename only for
// recording position information.
//
// An application may supply a custom implementation of ParseFile
// to change the effective file contents or the behavior of the parser,
// or to modify the syntax tree. For example, changing the backwards
// compatibility.
func ParseFile(f func(filename string, src interface{}) (*ast.File, error)) Option {
	return func(c *Context) { c.parseFunc = f }
}
<|endoftext|>


<commit_before>package bslack

import (
	"fmt"
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/matterhook"
	log "github.com/Sirupsen/logrus"
	"github.com/nlopes/slack"
	"html"
	"regexp"
	"strings"
	"time"
)

type MMMessage struct {
	Text     string
	Channel  string
	Username string
	UserID   string
	Raw      *slack.MessageEvent
}

type Bslack struct {
	mh       *matterhook.Client
	sc       *slack.Client
	Config   *config.Protocol
	rtm      *slack.RTM
	Plus     bool
	Remote   chan config.Message
	Users    []slack.User
	Account  string
	si       *slack.Info
	channels []slack.Channel
}

var flog *log.Entry
var protocol = "slack"

func init() {
	flog = log.WithFields(log.Fields{"module": protocol})
}

func New(cfg config.Protocol, account string, c chan config.Message) *Bslack {
	b := &Bslack{}
	b.Config = &cfg
	b.Remote = c
	b.Account = account
	return b
}

func (b *Bslack) Command(cmd string) string {
	return ""
}

func (b *Bslack) Connect() error {
	if b.Config.WebhookURL != "" && b.Config.WebhookBindAddress != "" {
		flog.Info("Connecting using webhookurl and webhookbindaddress")
		b.mh = matterhook.New(b.Config.WebhookURL,
			matterhook.Config{BindAddress: b.Config.WebhookBindAddress})
	} else if b.Config.WebhookURL != "" {
		flog.Info("Connecting using webhookurl (for posting) and token")
		b.mh = matterhook.New(b.Config.WebhookURL,
			matterhook.Config{DisableServer: true})
	} else {
		flog.Info("Connecting using token")
		b.sc = slack.New(b.Config.Token)
		b.rtm = b.sc.NewRTM()
		go b.rtm.ManageConnection()
	}
	flog.Info("Connection succeeded")
	go b.handleSlack()
	return nil
}

func (b *Bslack) Disconnect() error {
	return nil

}

func (b *Bslack) JoinChannel(channel string) error {
	// we can only join channels using the API
	if b.Config.WebhookURL == "" || b.Config.WebhookBindAddress == "" {
		if strings.HasPrefix(b.Config.Token, "xoxb") {
			// TODO check if bot has already joined channel
			return nil
		}
		_, err := b.sc.JoinChannel(channel)
		if err != nil {
			if err.Error() != "name_taken" {
				return err
			}
		}
	}
	return nil
}

func (b *Bslack) Send(msg config.Message) error {
	flog.Debugf("Receiving %#v", msg)
	nick := msg.Username
	message := msg.Text
	channel := msg.Channel
	if b.Config.PrefixMessagesWithNick {
		message = nick + " " + message
	}
	if b.Config.WebhookURL != "" {
		matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
		matterMessage.Channel = channel
		matterMessage.UserName = nick
		matterMessage.Type = ""
		matterMessage.Text = message
		err := b.mh.Send(matterMessage)
		if err != nil {
			flog.Info(err)
			return err
		}
		return nil
	}
	schannel, err := b.getChannelByName(channel)
	if err != nil {
		return err
	}
	np := slack.NewPostMessageParameters()
	if b.Config.PrefixMessagesWithNick == true {
		np.AsUser = true
	}
	np.Username = nick
	np.IconURL = config.GetIconURL(&msg, b.Config)
	if msg.Avatar != "" {
		np.IconURL = msg.Avatar
	}
	b.sc.PostMessage(schannel.ID, message, np)

	/*
	   newmsg := b.rtm.NewOutgoingMessage(message, schannel.ID)
	   b.rtm.SendMessage(newmsg)
	*/

	return nil
}

func (b *Bslack) getAvatar(user string) string {
	var avatar string
	if b.Users != nil {
		for _, u := range b.Users {
			if user == u.Name {
				return u.Profile.Image48
			}
		}
	}
	return avatar
}

func (b *Bslack) getChannelByName(name string) (*slack.Channel, error) {
	if b.channels == nil {
		return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.Account, name)
	}
	for _, channel := range b.channels {
		if channel.Name == name {
			return &channel, nil
		}
	}
	return nil, fmt.Errorf("%s: channel %s not found", b.Account, name)
}

func (b *Bslack) getChannelByID(ID string) (*slack.Channel, error) {
	if b.channels == nil {
		return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.Account, ID)
	}
	for _, channel := range b.channels {
		if channel.ID == ID {
			return &channel, nil
		}
	}
	return nil, fmt.Errorf("%s: channel %s not found", b.Account, ID)
}

func (b *Bslack) handleSlack() {
	mchan := make(chan *MMMessage)
	if b.Config.WebhookBindAddress != "" && b.Config.WebhookURL != "" {
		flog.Debugf("Choosing webhooks based receiving")
		go b.handleMatterHook(mchan)
	} else {
		flog.Debugf("Choosing token based receiving")
		go b.handleSlackClient(mchan)
	}
	time.Sleep(time.Second)
	flog.Debug("Start listening for Slack messages")
	for message := range mchan {
		// do not send messages from ourself
		if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" && message.Username == b.si.User.Name {
			continue
		}
		texts := strings.Split(message.Text, "\n")
		for _, text := range texts {
			text = b.replaceURL(text)
			text = html.UnescapeString(text)
			flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account)
			b.Remote <- config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username), UserID: message.UserID}
		}
	}
}

func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
	count := 0
	for msg := range b.rtm.IncomingEvents {
		switch ev := msg.Data.(type) {
		case *slack.MessageEvent:
			// ignore first message
			if count > 0 {
				flog.Debugf("Receiving from slackclient %#v", ev)
				if !b.Config.EditDisable && ev.SubMessage != nil {
					flog.Debugf("SubMessage %#v", ev.SubMessage)
					ev.User = ev.SubMessage.User
					ev.Text = ev.SubMessage.Text + b.Config.EditSuffix
				}
				// use our own func because rtm.GetChannelInfo doesn't work for private channels
				channel, err := b.getChannelByID(ev.Channel)
				if err != nil {
					continue
				}
				user, err := b.rtm.GetUserInfo(ev.User)
				if err != nil {
					continue
				}
				m := &MMMessage{}
				m.UserID = user.ID
				m.Username = user.Name
				m.Channel = channel.Name
				m.Text = ev.Text
				m.Raw = ev
				m.Text = b.replaceMention(m.Text)
				mchan <- m
			}
			count++
		case *slack.OutgoingErrorEvent:
			flog.Debugf("%#v", ev.Error())
		case *slack.ChannelJoinedEvent:
			b.Users, _ = b.sc.GetUsers()
		case *slack.ConnectedEvent:
			b.channels = ev.Info.Channels
			b.si = ev.Info
			b.Users, _ = b.sc.GetUsers()
			// add private channels
			groups, _ := b.sc.GetGroups(true)
			for _, g := range groups {
				channel := new(slack.Channel)
				channel.ID = g.ID
				channel.Name = g.Name
				b.channels = append(b.channels, *channel)
			}
		case *slack.InvalidAuthEvent:
			flog.Fatalf("Invalid Token %#v", ev)
		default:
		}
	}
}

func (b *Bslack) handleMatterHook(mchan chan *MMMessage) {
	for {
		message := b.mh.Receive()
		flog.Debugf("receiving from matterhook (slack) %#v", message)
		m := &MMMessage{}
		m.Username = message.UserName
		m.Text = message.Text
		m.Text = b.replaceMention(m.Text)
		m.Channel = message.ChannelName
		if m.Username == "slackbot" {
			continue
		}
		mchan <- m
	}
}

func (b *Bslack) userName(id string) string {
	for _, u := range b.Users {
		if u.ID == id {
			return u.Name
		}
	}
	return ""
}

func (b *Bslack) replaceMention(text string) string {
	results := regexp.MustCompile(`<@([a-zA-z0-9]+)>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		text = strings.Replace(text, "<@"+r[1]+">", "@"+b.userName(r[1]), -1)

	}
	return text
}

func (b *Bslack) replaceURL(text string) string {
	results := regexp.MustCompile(`<(.*?)\|.*?>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		text = strings.Replace(text, r[0], r[1], -1)
	}
	return text
}
<commit_msg>Lookup bot username (slack). #213<commit_after>package bslack

import (
	"fmt"
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/matterhook"
	log "github.com/Sirupsen/logrus"
	"github.com/nlopes/slack"
	"html"
	"regexp"
	"strings"
	"time"
)

type MMMessage struct {
	Text     string
	Channel  string
	Username string
	UserID   string
	Raw      *slack.MessageEvent
}

type Bslack struct {
	mh       *matterhook.Client
	sc       *slack.Client
	Config   *config.Protocol
	rtm      *slack.RTM
	Plus     bool
	Remote   chan config.Message
	Users    []slack.User
	Account  string
	si       *slack.Info
	channels []slack.Channel
}

var flog *log.Entry
var protocol = "slack"

func init() {
	flog = log.WithFields(log.Fields{"module": protocol})
}

func New(cfg config.Protocol, account string, c chan config.Message) *Bslack {
	b := &Bslack{}
	b.Config = &cfg
	b.Remote = c
	b.Account = account
	return b
}

func (b *Bslack) Command(cmd string) string {
	return ""
}

func (b *Bslack) Connect() error {
	if b.Config.WebhookURL != "" && b.Config.WebhookBindAddress != "" {
		flog.Info("Connecting using webhookurl and webhookbindaddress")
		b.mh = matterhook.New(b.Config.WebhookURL,
			matterhook.Config{BindAddress: b.Config.WebhookBindAddress})
	} else if b.Config.WebhookURL != "" {
		flog.Info("Connecting using webhookurl (for posting) and token")
		b.mh = matterhook.New(b.Config.WebhookURL,
			matterhook.Config{DisableServer: true})
	} else {
		flog.Info("Connecting using token")
		b.sc = slack.New(b.Config.Token)
		b.rtm = b.sc.NewRTM()
		go b.rtm.ManageConnection()
	}
	flog.Info("Connection succeeded")
	go b.handleSlack()
	return nil
}

func (b *Bslack) Disconnect() error {
	return nil

}

func (b *Bslack) JoinChannel(channel string) error {
	// we can only join channels using the API
	if b.Config.WebhookURL == "" || b.Config.WebhookBindAddress == "" {
		if strings.HasPrefix(b.Config.Token, "xoxb") {
			// TODO check if bot has already joined channel
			return nil
		}
		_, err := b.sc.JoinChannel(channel)
		if err != nil {
			if err.Error() != "name_taken" {
				return err
			}
		}
	}
	return nil
}

func (b *Bslack) Send(msg config.Message) error {
	flog.Debugf("Receiving %#v", msg)
	nick := msg.Username
	message := msg.Text
	channel := msg.Channel
	if b.Config.PrefixMessagesWithNick {
		message = nick + " " + message
	}
	if b.Config.WebhookURL != "" {
		matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
		matterMessage.Channel = channel
		matterMessage.UserName = nick
		matterMessage.Type = ""
		matterMessage.Text = message
		err := b.mh.Send(matterMessage)
		if err != nil {
			flog.Info(err)
			return err
		}
		return nil
	}
	schannel, err := b.getChannelByName(channel)
	if err != nil {
		return err
	}
	np := slack.NewPostMessageParameters()
	if b.Config.PrefixMessagesWithNick == true {
		np.AsUser = true
	}
	np.Username = nick
	np.IconURL = config.GetIconURL(&msg, b.Config)
	if msg.Avatar != "" {
		np.IconURL = msg.Avatar
	}
	b.sc.PostMessage(schannel.ID, message, np)

	/*
	   newmsg := b.rtm.NewOutgoingMessage(message, schannel.ID)
	   b.rtm.SendMessage(newmsg)
	*/

	return nil
}

func (b *Bslack) getAvatar(user string) string {
	var avatar string
	if b.Users != nil {
		for _, u := range b.Users {
			if user == u.Name {
				return u.Profile.Image48
			}
		}
	}
	return avatar
}

func (b *Bslack) getChannelByName(name string) (*slack.Channel, error) {
	if b.channels == nil {
		return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.Account, name)
	}
	for _, channel := range b.channels {
		if channel.Name == name {
			return &channel, nil
		}
	}
	return nil, fmt.Errorf("%s: channel %s not found", b.Account, name)
}

func (b *Bslack) getChannelByID(ID string) (*slack.Channel, error) {
	if b.channels == nil {
		return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.Account, ID)
	}
	for _, channel := range b.channels {
		if channel.ID == ID {
			return &channel, nil
		}
	}
	return nil, fmt.Errorf("%s: channel %s not found", b.Account, ID)
}

func (b *Bslack) handleSlack() {
	mchan := make(chan *MMMessage)
	if b.Config.WebhookBindAddress != "" && b.Config.WebhookURL != "" {
		flog.Debugf("Choosing webhooks based receiving")
		go b.handleMatterHook(mchan)
	} else {
		flog.Debugf("Choosing token based receiving")
		go b.handleSlackClient(mchan)
	}
	time.Sleep(time.Second)
	flog.Debug("Start listening for Slack messages")
	for message := range mchan {
		// do not send messages from ourself
		if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" && message.Username == b.si.User.Name {
			continue
		}
		texts := strings.Split(message.Text, "\n")
		for _, text := range texts {
			text = b.replaceURL(text)
			text = html.UnescapeString(text)
			flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account)
			b.Remote <- config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username), UserID: message.UserID}
		}
	}
}

func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
	count := 0
	for msg := range b.rtm.IncomingEvents {
		switch ev := msg.Data.(type) {
		case *slack.MessageEvent:
			// ignore first message
			if count > 0 {
				flog.Debugf("Receiving from slackclient %#v", ev)
				if !b.Config.EditDisable && ev.SubMessage != nil {
					flog.Debugf("SubMessage %#v", ev.SubMessage)
					ev.User = ev.SubMessage.User
					ev.Text = ev.SubMessage.Text + b.Config.EditSuffix
				}
				// use our own func because rtm.GetChannelInfo doesn't work for private channels
				channel, err := b.getChannelByID(ev.Channel)
				if err != nil {
					continue
				}
				user, err := b.rtm.GetUserInfo(ev.User)
				if err != nil {
					continue
				}
				m := &MMMessage{}
				m.UserID = user.ID
				m.Username = user.Name
				m.Channel = channel.Name
				m.Text = ev.Text
				m.Raw = ev
				m.Text = b.replaceMention(m.Text)
				if ev.BotID != "" && user.Name == "" {
					bot, err := b.rtm.GetBotInfo(ev.BotID)
					if err != nil {
						continue
					}
					if bot.Name != "" {
						m.Username = bot.Name
					}
				}
				mchan <- m
			}
			count++
		case *slack.OutgoingErrorEvent:
			flog.Debugf("%#v", ev.Error())
		case *slack.ChannelJoinedEvent:
			b.Users, _ = b.sc.GetUsers()
		case *slack.ConnectedEvent:
			b.channels = ev.Info.Channels
			b.si = ev.Info
			b.Users, _ = b.sc.GetUsers()
			// add private channels
			groups, _ := b.sc.GetGroups(true)
			for _, g := range groups {
				channel := new(slack.Channel)
				channel.ID = g.ID
				channel.Name = g.Name
				b.channels = append(b.channels, *channel)
			}
		case *slack.InvalidAuthEvent:
			flog.Fatalf("Invalid Token %#v", ev)
		default:
		}
	}
}

func (b *Bslack) handleMatterHook(mchan chan *MMMessage) {
	for {
		message := b.mh.Receive()
		flog.Debugf("receiving from matterhook (slack) %#v", message)
		m := &MMMessage{}
		m.Username = message.UserName
		m.Text = message.Text
		m.Text = b.replaceMention(m.Text)
		m.Channel = message.ChannelName
		if m.Username == "slackbot" {
			continue
		}
		mchan <- m
	}
}

func (b *Bslack) userName(id string) string {
	for _, u := range b.Users {
		if u.ID == id {
			return u.Name
		}
	}
	return ""
}

func (b *Bslack) replaceMention(text string) string {
	results := regexp.MustCompile(`<@([a-zA-z0-9]+)>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		text = strings.Replace(text, "<@"+r[1]+">", "@"+b.userName(r[1]), -1)

	}
	return text
}

func (b *Bslack) replaceURL(text string) string {
	results := regexp.MustCompile(`<(.*?)\|.*?>`).FindAllStringSubmatch(text, -1)
	for _, r := range results {
		text = strings.Replace(text, r[0], r[1], -1)
	}
	return text
}
<|endoftext|>


<commit_before>package spark

import (
	"time"

	"github.com/256dpi/fire"
	"github.com/256dpi/fire/coal"

	"github.com/globalsign/mgo"
	"github.com/globalsign/mgo/bson"
)

// TODO: How to close a watcher?

// Watcher will watch multiple collections and serve watch requests by clients.
type Watcher struct {
	store  *coal.Store
	hub    *hub
	policy *Policy

	// The function gets invoked by the watcher with critical errors.
	Reporter func(error)
}

// NewWatch creates and returns a new watcher.
func NewWatcher(store *coal.Store, policy *Policy) *Watcher {
	// prepare watcher
	w := &Watcher{
		store:  store,
		policy: policy,
	}

	// create and add hub
	w.hub = newHub(policy, func(err error) {
		if w.Reporter != nil {
			w.Reporter(err)
		}
	})

	return w
}

// Watch will run the watcher in the background for the specified models.
//
// Note: This method should only called once when booting the application.
func (w *Watcher) Watch(models ...coal.Model) {
	// copy store
	store := w.store.Copy()

	// run watch goroutines
	for _, m := range models {
		go w.watcher(store, m)
	}
}

func (w *Watcher) watcher(store *coal.SubStore, model coal.Model) {
	for {
		// watch forever and call reporter with eventual error
		err := w.watch(store, model)
		if err != nil {
			w.Reporter(err)
		}
	}
}

func (w *Watcher) watch(store *coal.SubStore, model coal.Model) error {
	// start pipeline
	cs, err := store.C(model).Watch([]bson.M{}, mgo.ChangeStreamOptions{
		FullDocument: mgo.UpdateLookup,
	})
	if err != nil {
		return err
	}

	// iterate on elements forever
	var ch change
	for cs.Next(&ch) {
		// parse change
		op, id, ok := ch.parse()
		if !ok {
			continue
		}

		// create event
		evt := event{
			name: model.Meta().PluralName,
			op:   op,
			id:   id.Hex(),
			doc:  ch.FullDocument,
		}

		// broadcast change
		w.hub.broadcast(evt)
	}

	// close stream and check error
	if err := cs.Close(); err != nil {
		return err
	}

	return nil
}

// Collection will return an action that generates watch tokens for the current
// collection with filters returned by the specified callback.
func (w *Watcher) Collection(cb func(ctx *fire.Context) map[string]interface{}) *fire.Action {
	return &fire.Action{
		Methods: []string{"GET"},
		Callback: fire.C("spark/Watcher.Collection", fire.All(), func(ctx *fire.Context) error {
			// get filters if available
			var filters bson.M
			if cb != nil {
				filters = cb(ctx)
			}

			// get now
			now := time.Now()

			// get name
			name := ctx.Controller.Model.Meta().PluralName

			// generate token
			token, err := w.policy.GenerateToken(name, "", now, now.Add(w.policy.TokenLifespan), filters)
			if err != nil {
				return err
			}

			// write token
			_, err = ctx.ResponseWriter.Write([]byte(token))
			if err != nil {
				return err
			}

			return nil
		}),
	}
}

// Resource will return an action that generates watch tokens for the current
// resource.
func (w *Watcher) Resource() *fire.Action {
	return &fire.Action{
		Methods: []string{"GET"},
		Callback: fire.C("spark/Watcher.Resource", fire.All(), func(ctx *fire.Context) error {
			// get id
			id := ctx.Model.ID()

			// get now
			now := time.Now()

			// get name
			name := ctx.Controller.Model.Meta().PluralName

			// generate token
			token, err := w.policy.GenerateToken(name, id.Hex(), now, now.Add(w.policy.TokenLifespan), nil)
			if err != nil {
				return err
			}

			// write token
			_, err = ctx.ResponseWriter.Write([]byte(token))
			if err != nil {
				return err
			}

			return nil
		}),
	}
}

// GroupAction returns an action that should be registered in the group under
// the "watch" name.
func (w *Watcher) GroupAction() *fire.Action {
	return &fire.Action{
		Methods: []string{"GET"},
		Callback: fire.C("spark/Watcher.GroupAction", fire.All(), func(ctx *fire.Context) error {
			// handle connection
			w.hub.handle(ctx.ResponseWriter, ctx.HTTPRequest)

			return nil
		}),
	}
}

type change struct {
	OperationType string `bson:"operationType"`
	DocumentKey   struct {
		ID bson.ObjectId `bson:"_id"`
	} `bson:"documentKey"`
	FullDocument bson.M `bson:"fullDocument"`
}

func (c change) parse() (string, bson.ObjectId, bool) {
	// check operation type
	if c.OperationType == "insert" {
		return "create", c.DocumentKey.ID, true
	} else if c.OperationType == "replace" || c.OperationType == "update" {
		return "update", c.DocumentKey.ID, true
	} else if c.OperationType == "delete" {
		return "delete", c.DocumentKey.ID, true
	}

	return "", "", false
}
<commit_msg>prevent nil map<commit_after>package spark

import (
	"time"

	"github.com/256dpi/fire"
	"github.com/256dpi/fire/coal"

	"github.com/globalsign/mgo"
	"github.com/globalsign/mgo/bson"
)

// TODO: How to close a watcher?

// Watcher will watch multiple collections and serve watch requests by clients.
type Watcher struct {
	store  *coal.Store
	hub    *hub
	policy *Policy

	// The function gets invoked by the watcher with critical errors.
	Reporter func(error)
}

// NewWatch creates and returns a new watcher.
func NewWatcher(store *coal.Store, policy *Policy) *Watcher {
	// prepare watcher
	w := &Watcher{
		store:  store,
		policy: policy,
	}

	// create and add hub
	w.hub = newHub(policy, func(err error) {
		if w.Reporter != nil {
			w.Reporter(err)
		}
	})

	return w
}

// Watch will run the watcher in the background for the specified models.
//
// Note: This method should only called once when booting the application.
func (w *Watcher) Watch(models ...coal.Model) {
	// copy store
	store := w.store.Copy()

	// run watch goroutines
	for _, m := range models {
		go w.watcher(store, m)
	}
}

func (w *Watcher) watcher(store *coal.SubStore, model coal.Model) {
	for {
		// watch forever and call reporter with eventual error
		err := w.watch(store, model)
		if err != nil {
			w.Reporter(err)
		}
	}
}

func (w *Watcher) watch(store *coal.SubStore, model coal.Model) error {
	// start pipeline
	cs, err := store.C(model).Watch([]bson.M{}, mgo.ChangeStreamOptions{
		FullDocument: mgo.UpdateLookup,
	})
	if err != nil {
		return err
	}

	// iterate on elements forever
	var ch change
	for cs.Next(&ch) {
		// parse change
		op, id, ok := ch.parse()
		if !ok {
			continue
		}

		// create event
		evt := event{
			name: model.Meta().PluralName,
			op:   op,
			id:   id.Hex(),
			doc:  ch.FullDocument,
		}

		// broadcast change
		w.hub.broadcast(evt)
	}

	// close stream and check error
	if err := cs.Close(); err != nil {
		return err
	}

	return nil
}

// Collection will return an action that generates watch tokens for the current
// collection with filters returned by the specified callback.
func (w *Watcher) Collection(cb func(ctx *fire.Context) map[string]interface{}) *fire.Action {
	return &fire.Action{
		Methods: []string{"GET"},
		Callback: fire.C("spark/Watcher.Collection", fire.All(), func(ctx *fire.Context) error {
			// get filters if available
			var filters bson.M
			if cb != nil {
				filters = cb(ctx)
			}

			// check nil map
			if filters == nil {
				filters = bson.M{}
			}

			// get now
			now := time.Now()

			// get name
			name := ctx.Controller.Model.Meta().PluralName

			// generate token
			token, err := w.policy.GenerateToken(name, "", now, now.Add(w.policy.TokenLifespan), filters)
			if err != nil {
				return err
			}

			// write token
			_, err = ctx.ResponseWriter.Write([]byte(token))
			if err != nil {
				return err
			}

			return nil
		}),
	}
}

// Resource will return an action that generates watch tokens for the current
// resource.
func (w *Watcher) Resource() *fire.Action {
	return &fire.Action{
		Methods: []string{"GET"},
		Callback: fire.C("spark/Watcher.Resource", fire.All(), func(ctx *fire.Context) error {
			// get id
			id := ctx.Model.ID()

			// get now
			now := time.Now()

			// get name
			name := ctx.Controller.Model.Meta().PluralName

			// generate token
			token, err := w.policy.GenerateToken(name, id.Hex(), now, now.Add(w.policy.TokenLifespan), nil)
			if err != nil {
				return err
			}

			// write token
			_, err = ctx.ResponseWriter.Write([]byte(token))
			if err != nil {
				return err
			}

			return nil
		}),
	}
}

// GroupAction returns an action that should be registered in the group under
// the "watch" name.
func (w *Watcher) GroupAction() *fire.Action {
	return &fire.Action{
		Methods: []string{"GET"},
		Callback: fire.C("spark/Watcher.GroupAction", fire.All(), func(ctx *fire.Context) error {
			// handle connection
			w.hub.handle(ctx.ResponseWriter, ctx.HTTPRequest)

			return nil
		}),
	}
}

type change struct {
	OperationType string `bson:"operationType"`
	DocumentKey   struct {
		ID bson.ObjectId `bson:"_id"`
	} `bson:"documentKey"`
	FullDocument bson.M `bson:"fullDocument"`
}

func (c change) parse() (string, bson.ObjectId, bool) {
	// check operation type
	if c.OperationType == "insert" {
		return "create", c.DocumentKey.ID, true
	} else if c.OperationType == "replace" || c.OperationType == "update" {
		return "update", c.DocumentKey.ID, true
	} else if c.OperationType == "delete" {
		return "delete", c.DocumentKey.ID, true
	}

	return "", "", false
}
<|endoftext|>


<commit_before>package stackutil

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"sort"
)

type StackSummary struct {
	CreationTime        string
	StackId             string
	StackName           string
	StackStatus         string
	TemplateDescription string
}

type StackResults struct {
	StackSummaries []StackSummary
}

func ActiveStacks(region string) ([]string, error) {
	var stack_names []string

	svc := cloudformation.New(session.New(), &aws.Config{Region: aws.String(region)})

	params := &cloudformation.ListStacksInput{
		StackStatusFilter: []*string{
			aws.String("CREATE_COMPLETE"),
			aws.String("UPDATE_COMPLETE"),
			aws.String("UPDATE_ROLLBACK_COMPLETE"),
		},
	}

	resp, err := svc.ListStacks(params)

	if err != nil {
		return nil, err
	}

	for _, element := range resp.StackSummaries {
		stack_names = append(stack_names, *element.StackName)
	}

	sort.Strings(stack_names)

	return stack_names, nil
}
<commit_msg>Fixing linting issue<commit_after>package stackutil

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"sort"
)

type StackSummary struct {
	CreationTime        string
	StackId             string
	StackName           string
	StackStatus         string
	TemplateDescription string
}

type StackResults struct {
	StackSummaries []StackSummary
}

func ActiveStacks(region string) ([]string, error) {
	var stackNames []string

	svc := cloudformation.New(session.New(), &aws.Config{Region: aws.String(region)})

	params := &cloudformation.ListStacksInput{
		StackStatusFilter: []*string{
			aws.String("CREATE_COMPLETE"),
			aws.String("UPDATE_COMPLETE"),
			aws.String("UPDATE_ROLLBACK_COMPLETE"),
		},
	}

	resp, err := svc.ListStacks(params)

	if err != nil {
		return nil, err
	}

	for _, element := range resp.StackSummaries {
		stackNames = append(stackNames, *element.StackName)
	}

	sort.Strings(stackNames)

	return stackNames, nil
}
<|endoftext|>


<commit_before>package watch

import etcd "github.com/coreos/etcd/clientv3"

// OpOption is a simple typedef for etcd.OpOption.
type OpOption struct {
	Get   etcd.OpOption
	Watch etcd.OpOption
}

// WithFilterPut discards PUT events from the watcher.
func WithFilterPut() OpOption {
	return OpOption{Watch: etcd.WithFilterPut(), Get: nil}
}

// WithSort specifies the sort to use for the watcher
func WithSort(sortBy etcd.SortTarget, sortOrder etcd.SortOrder) OpOption {
	return OpOption{Get: etcd.WithSort(sortBy, sortOrder), Watch: nil}
}
<commit_msg>Add delete filter to watch package<commit_after>package watch

import etcd "github.com/coreos/etcd/clientv3"

// OpOption is a simple typedef for etcd.OpOption.
type OpOption struct {
	Get   etcd.OpOption
	Watch etcd.OpOption
}

// WithFilterPut discards PUT events from the watcher.
func WithFilterPut() OpOption {
	return OpOption{Watch: etcd.WithFilterPut(), Get: nil}
}

// WithSort specifies the sort to use for the watcher
func WithSort(sortBy etcd.SortTarget, sortOrder etcd.SortOrder) OpOption {
	return OpOption{Get: etcd.WithSort(sortBy, sortOrder), Watch: nil}
}

// WithFilterDelete discards DELETE events from the watcher.
func WithFilterDelete() OpOption {
	return OpOption(etcd.WithFilterDelete())
}
<|endoftext|>


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

package command

import (
	"fmt"
	"os"
	"strings"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/client"
)

func NewMemberCommand() cli.Command {
	return cli.Command{
		Name:  "member",
		Usage: "member add, remove and list subcommands",
		Subcommands: []cli.Command{
			cli.Command{
				Name:   "list",
				Usage:  "enumerate existing cluster members",
				Action: actionMemberList,
			},
			cli.Command{
				Name:   "add",
				Usage:  "add a new member to the etcd cluster",
				Action: actionMemberAdd,
			},
			cli.Command{
				Name:   "remove",
				Usage:  "remove an existing member from the etcd cluster",
				Action: actionMemberRemove,
			},
		},
	}
}

func mustNewMembersAPI(c *cli.Context) client.MembersAPI {
	eps, err := getEndpoints(c)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	tr, err := getTransport(c)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	cfg := client.Config{
		Transport: tr,
		Endpoints: eps,
	}

	hc, err := client.New(cfg)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if !c.GlobalBool("no-sync") {
		ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
		err := hc.Sync(ctx)
		cancel()
		if err != nil {
			fmt.Fprintln(os.Stderr, err.Error())
			os.Exit(1)
		}
	}

	if c.GlobalBool("debug") {
		fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
	}

	return client.NewMembersAPI(hc)
}

func actionMemberList(c *cli.Context) {
	if len(c.Args()) != 0 {
		fmt.Fprintln(os.Stderr, "No arguments accepted")
		os.Exit(1)
	}
	mAPI := mustNewMembersAPI(c)
	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
	members, err := mAPI.List(ctx)
	cancel()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	for _, m := range members {
		fmt.Printf("%s: name=%s peerURLs=%s clientURLs=%s\n", m.ID, m.Name, strings.Join(m.PeerURLs, ","), strings.Join(m.ClientURLs, ","))
	}
}

func actionMemberAdd(c *cli.Context) {
	args := c.Args()
	if len(args) != 2 {
		fmt.Fprintln(os.Stderr, "Provide a name and a single member peerURL")
		os.Exit(1)
	}

	mAPI := mustNewMembersAPI(c)

	url := args[1]
	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
	m, err := mAPI.Add(ctx, url)
	cancel()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	newID := m.ID
	newName := args[0]
	fmt.Printf("Added member named %s with ID %s to cluster\n", newName, newID)

	ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
	members, err := mAPI.List(ctx)
	cancel()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	conf := []string{}
	for _, memb := range members {
		for _, u := range memb.PeerURLs {
			n := memb.Name
			if memb.ID == newID {
				n = newName
			}
			conf = append(conf, fmt.Sprintf("%s=%s", n, u))
		}
	}

	fmt.Print("\n")
	fmt.Printf("ETCD_NAME=%q\n", newName)
	fmt.Printf("ETCD_INITIAL_CLUSTER=%q\n", strings.Join(conf, ","))
	fmt.Printf("ETCD_INITIAL_CLUSTER_STATE=\"existing\"\n")
}

func actionMemberRemove(c *cli.Context) {
	args := c.Args()
	if len(args) != 1 {
		fmt.Fprintln(os.Stderr, "Provide a single member ID")
		os.Exit(1)
	}
	removalID := args[0]

	mAPI := mustNewMembersAPI(c)
	// Get the list of members.
	listctx, listCancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
	members, err := mAPI.List(listctx)
	listCancel()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error while verifying ID against known members:", err.Error())
		os.Exit(1)
	}
	// Sanity check the input.
	foundID := false
	for _, m := range members {
		if m.ID == removalID {
			foundID = true
		}
		if m.Name == removalID {
			// Note that, so long as it's not ambiguous, we *could* do the right thing by name here.
			fmt.Fprintf(os.Stderr, "Found a member named %s; if this is correct, please use its ID, eg:\n\tetcdctl member remove %s\n", m.Name, m.ID)
			fmt.Fprintf(os.Stderr, "For more details, read the documentation at https://github.com/coreos/etcd/blob/master/Documentation/runtime-configuration.md#remove-a-member\n\n")
		}
	}
	if !foundID {
		fmt.Fprintf(os.Stderr, "Couldn't find a member in the cluster with an ID of %s.\n", removalID)
		os.Exit(1)
	}

	// Actually attempt to remove the member.
	ctx, removeCancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
	err = mAPI.Remove(ctx, removalID)
	removeCancel()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Recieved an error trying to remove member %s: %s", removalID, err.Error())
		os.Exit(1)
	}

	fmt.Printf("Removed member %s from cluster\n", removalID)
}
<commit_msg>etcdctl: mark unstarted member<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package command

import (
	"fmt"
	"os"
	"strings"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/client"
)

func NewMemberCommand() cli.Command {
	return cli.Command{
		Name:  "member",
		Usage: "member add, remove and list subcommands",
		Subcommands: []cli.Command{
			cli.Command{
				Name:   "list",
				Usage:  "enumerate existing cluster members",
				Action: actionMemberList,
			},
			cli.Command{
				Name:   "add",
				Usage:  "add a new member to the etcd cluster",
				Action: actionMemberAdd,
			},
			cli.Command{
				Name:   "remove",
				Usage:  "remove an existing member from the etcd cluster",
				Action: actionMemberRemove,
			},
		},
	}
}

func mustNewMembersAPI(c *cli.Context) client.MembersAPI {
	eps, err := getEndpoints(c)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	tr, err := getTransport(c)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	cfg := client.Config{
		Transport: tr,
		Endpoints: eps,
	}

	hc, err := client.New(cfg)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if !c.GlobalBool("no-sync") {
		ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
		err := hc.Sync(ctx)
		cancel()
		if err != nil {
			fmt.Fprintln(os.Stderr, err.Error())
			os.Exit(1)
		}
	}

	if c.GlobalBool("debug") {
		fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
	}

	return client.NewMembersAPI(hc)
}

func actionMemberList(c *cli.Context) {
	if len(c.Args()) != 0 {
		fmt.Fprintln(os.Stderr, "No arguments accepted")
		os.Exit(1)
	}
	mAPI := mustNewMembersAPI(c)
	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
	members, err := mAPI.List(ctx)
	cancel()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	for _, m := range members {
		if len(m.Name) == 0 {
			fmt.Printf("%s[unstarted]: peerURLs=%s\n", m.ID, strings.Join(m.PeerURLs, ","))
		} else {
			fmt.Printf("%s: name=%s peerURLs=%s clientURLs=%s\n", m.ID, m.Name, strings.Join(m.PeerURLs, ","), strings.Join(m.ClientURLs, ","))
		}
	}
}

func actionMemberAdd(c *cli.Context) {
	args := c.Args()
	if len(args) != 2 {
		fmt.Fprintln(os.Stderr, "Provide a name and a single member peerURL")
		os.Exit(1)
	}

	mAPI := mustNewMembersAPI(c)

	url := args[1]
	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
	m, err := mAPI.Add(ctx, url)
	cancel()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	newID := m.ID
	newName := args[0]
	fmt.Printf("Added member named %s with ID %s to cluster\n", newName, newID)

	ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
	members, err := mAPI.List(ctx)
	cancel()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	conf := []string{}
	for _, memb := range members {
		for _, u := range memb.PeerURLs {
			n := memb.Name
			if memb.ID == newID {
				n = newName
			}
			conf = append(conf, fmt.Sprintf("%s=%s", n, u))
		}
	}

	fmt.Print("\n")
	fmt.Printf("ETCD_NAME=%q\n", newName)
	fmt.Printf("ETCD_INITIAL_CLUSTER=%q\n", strings.Join(conf, ","))
	fmt.Printf("ETCD_INITIAL_CLUSTER_STATE=\"existing\"\n")
}

func actionMemberRemove(c *cli.Context) {
	args := c.Args()
	if len(args) != 1 {
		fmt.Fprintln(os.Stderr, "Provide a single member ID")
		os.Exit(1)
	}
	removalID := args[0]

	mAPI := mustNewMembersAPI(c)
	// Get the list of members.
	listctx, listCancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
	members, err := mAPI.List(listctx)
	listCancel()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error while verifying ID against known members:", err.Error())
		os.Exit(1)
	}
	// Sanity check the input.
	foundID := false
	for _, m := range members {
		if m.ID == removalID {
			foundID = true
		}
		if m.Name == removalID {
			// Note that, so long as it's not ambiguous, we *could* do the right thing by name here.
			fmt.Fprintf(os.Stderr, "Found a member named %s; if this is correct, please use its ID, eg:\n\tetcdctl member remove %s\n", m.Name, m.ID)
			fmt.Fprintf(os.Stderr, "For more details, read the documentation at https://github.com/coreos/etcd/blob/master/Documentation/runtime-configuration.md#remove-a-member\n\n")
		}
	}
	if !foundID {
		fmt.Fprintf(os.Stderr, "Couldn't find a member in the cluster with an ID of %s.\n", removalID)
		os.Exit(1)
	}

	// Actually attempt to remove the member.
	ctx, removeCancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
	err = mAPI.Remove(ctx, removalID)
	removeCancel()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Recieved an error trying to remove member %s: %s", removalID, err.Error())
		os.Exit(1)
	}

	fmt.Printf("Removed member %s from cluster\n", removalID)
}
<|endoftext|>


<commit_before>package gotwilio

import (
	"net/url"
	"time"

	"github.com/gorilla/schema"
)

var formDecoder *schema.Decoder

func init() {
	formDecoder = schema.NewDecoder()
	formDecoder.SetAliasTag("form")
}

func DecodeWebhook(data url.Values, out interface{}) error {
	return formDecoder.Decode(out, data)
}

// https://www.twilio.com/docs/proxy/api/proxy-webhooks

// https://www.twilio.com/docs/proxy/api/proxy-webhooks#callbackurl
// These webhooks are fired for each new interaction and are informational only.
type ProxyCallbackWebhook struct {
	OutboundResourceStatus string    `form:"outboundResourceStatus"`
	OutboundResourceType   string    `form:"outboundResourceType"`
	InteractionDateUpdated time.Time `form:"interactionDateUpdated"`
	InteractionData        string    `form:"interactionData"`
	InteractionDateCreated time.Time `form:"interactionDateCreated"`
	InboundResourceURL     string    `form:"inboundResourceUrl"`
	InteractionServiceSid  string    `form:"interactionServiceSid"`
	OutboundParticipantSid string    `form:"outboundParticipantSid"`
	InteractionType        string    `form:"interactionType"`
	InteractionAccountSid  string    `form:"interactionAccountSid"`
	InboundParticipantSid  string    `form:"inboundParticipantSid"`
	InboundResourceStatus  string    `form:"inboundResourceStatus"`
	OutboundResourceSid    string    `form:"outboundResourceSid"`
	OutboundResourceURL    string    `form:"outboundResourceUrl"`
	InboundResourceType    string    `form:"inboundResourceType"`
	InboundResourceSid     string    `form:"inboundResourceSid"`
	InteractionSessionSid  string    `form:"interactionSessionSid"`
	InteractionSid         string    `form:"interactionSid"`
}

// https://www.twilio.com/docs/proxy/api/proxy-webhooks#interceptcallbackurl
// Fires on each interaction. If responded to with a 403 to this webhook we
// will abort/block the interaction. Any other status or timeout the interaction continues
type ProxyInterceptCallbackWebhook struct {
	InteractionDateUpdated time.Time `form:"interactionDateUpdated"`
	InteractionData        string    `form:"interactionData"`
	InteractionDateCreated time.Time `form:"interactionDateCreated"`
	InboundResourceURL     string    `form:"inboundResourceUrl"`
	InteractionServiceSid  string    `form:"interactionServiceSid"`
	InteractionType        string    `form:"interactionType"`
	InteractionAccountSid  string    `form:"interactionAccountSid"`
	InboundParticipantSid  string    `form:"inboundParticipantSid"`
	InboundResourceStatus  string    `form:"inboundResourceStatus"`
	InboundResourceType    string    `form:"inboundResourceType"`
	InboundResourceSid     string    `form:"inboundResourceSid"`
	InteractionSessionSid  string    `form:"interactionSessionSid"`
	InteractionSid         string    `form:"interactionSid"`
}

// https://www.twilio.com/docs/proxy/api/proxy-webhooks#outofsessioncallbackurl
// A URL to send webhooks to when an action (inbound call or SMS) occurs where
// there is no session or a closed session. If your server (or a Twilio function)
// responds with valid TwiML, this will be processed.
// This means it is possible to e.g. play a message for a call, send an automated
// text message response, or redirect a call to another number.
type ProxyOutOfSessionCallbackWebhook struct {
	AccountSid          string    `form:"AccountSid"`
	Body                string    `form:"Body"`
	SessionUniqueName   string    `form:"sessionUniqueName"`
	SessionAccountSid   string    `form:"sessionAccountSid"`
	SessionServiceSid   string    `form:"sessionServiceSid"`
	SessionSid          string    `form:"sessionSid"`
	SessionStatus       string    `form:"sessionStatus"`
	SessionMode         string    `form:"sessionMode"`
	SessionDateCreated  time.Time `form:"sessionDateCreated"`
	SessionDateUpdated  time.Time `form:"sessionDateUpdated"`
	SessionDateEnded    time.Time `form:"sessionDateEnded"`
	SessionClosedReason string    `form:"sessionClosedReason"`

	To          string `form:"To"`
	ToCity      string `form:"ToCity"`
	ToState     string `form:"ToState"`
	ToZip       string `form:"ToZip"`
	ToCountry   string `form:"ToCountry"`
	From        string `form:"From"`
	FromCity    string `form:"FromCity"`
	FromState   string `form:"FromState"`
	FromZip     string `form:"FromZip"`
	FromCountry string `form:"FromCountry"`

	InboundParticipantSid                string    `form:"inboundParticipantSid"`
	InboundParticipantIdentifier         string    `form:"inboundParticipantIdentifier"`
	InboundParticipantFriendlyName       string    `form:"inboundParticipantFriendlyName"`
	InboundParticipantProxyIdentifier    string    `form:"inboundParticipantProxyIdentifier"`
	InboundParticipantProxyIdentifierSid string    `form:"inboundParticipantProxyIdentifierSid"`
	InboundParticipantAccountSid         string    `form:"inboundParticipantAccountSid"`
	InboundParticipantServiceSid         string    `form:"inboundParticipantServiceSid"`
	InboundParticipantSessionSid         string    `form:"inboundParticipantSessionSid"`
	InboundParticipantDateCreated        time.Time `form:"inboundParticipantDateCreated"`
	InboundParticipantDateUpdated        time.Time `form:"inboundParticipantDateUpdated"`

	OutboundParticipantSid                string    `form:"outboundParticipantSid"`
	OutboundParticipantIdentifier         string    `form:"outboundParticipantIdentifier"`
	OutboundParticipantFriendlyName       string    `form:"outboundParticipantFriendlyName"`
	OutboundParticipantProxyIdentifier    string    `form:"outboundParticipantProxyIdentifier"`
	OutboundParticipantProxyIdentifierSid string    `form:"outboundParticipantProxyIdentifierSid"`
	OutboundParticipantAccountSid         string    `form:"outboundParticipantAccountSid"`
	OutboundParticipantServiceSid         string    `form:"outboundParticipantServiceSid"`
	OutboundParticipantSessionSid         string    `form:"outboundParticipantSessionSid"`
	OutboundParticipantDateCreated        time.Time `form:"outboundParticipantDateCreated"`
	OutboundParticipantDateUpdated        time.Time `form:"outboundParticipantDateUpdated"`

	CallSid    string `form:"CallSid"`
	CallStatus string `form:"CallStatus"`

	Caller        string `form:"Caller"`
	CallerCity    string `form:"CallerCity"`
	CallerState   string `form:"CallerState"`
	CallerZip     string `form:"CallerZip"`
	CallerCountry string `form:"CallerCountry"`

	Called        string `form:"Called"`
	CalledCity    string `form:"CalledCity"`
	CalledState   string `form:"CalledState"`
	CalledZip     string `form:"CalledZip"`
	CalledCountry string `form:"CalledCountry"`

	Direction  string `form:"Direction"`
	AddOns     string `form:"AddOns"`
	APIVersion string `form:"ApiVersion"`
}
<commit_msg>Handle InteractionData properly<commit_after>package gotwilio

import (
	"encoding/json"
	"net/url"
	"time"

	"github.com/gorilla/schema"
)

var formDecoder *schema.Decoder

func init() {
	formDecoder = schema.NewDecoder()
	formDecoder.SetAliasTag("form")
}

func DecodeWebhook(data url.Values, out interface{}) error {
	return formDecoder.Decode(out, data)
}

type InteractionData struct {
	Body string `json:"body"`
}

// https://www.twilio.com/docs/proxy/api/proxy-webhooks

// https://www.twilio.com/docs/proxy/api/proxy-webhooks#callbackurl
// These webhooks are fired for each new interaction and are informational only.
type ProxyCallbackWebhook struct {
	OutboundResourceStatus string    `form:"outboundResourceStatus"`
	OutboundResourceType   string    `form:"outboundResourceType"`
	InteractionDateUpdated time.Time `form:"interactionDateUpdated"`
	InteractionData        string    `form:"interactionData"`
	InteractionDateCreated time.Time `form:"interactionDateCreated"`
	InboundResourceURL     string    `form:"inboundResourceUrl"`
	InteractionServiceSid  string    `form:"interactionServiceSid"`
	OutboundParticipantSid string    `form:"outboundParticipantSid"`
	InteractionType        string    `form:"interactionType"`
	InteractionAccountSid  string    `form:"interactionAccountSid"`
	InboundParticipantSid  string    `form:"inboundParticipantSid"`
	InboundResourceStatus  string    `form:"inboundResourceStatus"`
	OutboundResourceSid    string    `form:"outboundResourceSid"`
	OutboundResourceURL    string    `form:"outboundResourceUrl"`
	InboundResourceType    string    `form:"inboundResourceType"`
	InboundResourceSid     string    `form:"inboundResourceSid"`
	InteractionSessionSid  string    `form:"interactionSessionSid"`
	InteractionSid         string    `form:"interactionSid"`
}

func (p ProxyCallbackWebhook) GetInteractionData() (InteractionData, error) {
	var out InteractionData
	err := json.Unmarshal([]byte(p.InteractionData), &out)
	return out, err
}

// https://www.twilio.com/docs/proxy/api/proxy-webhooks#interceptcallbackurl
// Fires on each interaction. If responded to with a 403 to this webhook we
// will abort/block the interaction. Any other status or timeout the interaction continues
type ProxyInterceptCallbackWebhook struct {
	InteractionDateUpdated time.Time `form:"interactionDateUpdated"`
	InteractionData        string    `form:"interactionData"`
	InteractionDateCreated time.Time `form:"interactionDateCreated"`
	InboundResourceURL     string    `form:"inboundResourceUrl"`
	InteractionServiceSid  string    `form:"interactionServiceSid"`
	InteractionType        string    `form:"interactionType"`
	InteractionAccountSid  string    `form:"interactionAccountSid"`
	InboundParticipantSid  string    `form:"inboundParticipantSid"`
	InboundResourceStatus  string    `form:"inboundResourceStatus"`
	InboundResourceType    string    `form:"inboundResourceType"`
	InboundResourceSid     string    `form:"inboundResourceSid"`
	InteractionSessionSid  string    `form:"interactionSessionSid"`
	InteractionSid         string    `form:"interactionSid"`
}

func (p ProxyInterceptCallbackWebhook) GetInteractionData() (InteractionData, error) {
	var out InteractionData
	err := json.Unmarshal([]byte(p.InteractionData), &out)
	return out, err
}

// https://www.twilio.com/docs/proxy/api/proxy-webhooks#outofsessioncallbackurl
// A URL to send webhooks to when an action (inbound call or SMS) occurs where
// there is no session or a closed session. If your server (or a Twilio function)
// responds with valid TwiML, this will be processed.
// This means it is possible to e.g. play a message for a call, send an automated
// text message response, or redirect a call to another number.
type ProxyOutOfSessionCallbackWebhook struct {
	AccountSid          string    `form:"AccountSid"`
	Body                string    `form:"Body"`
	SessionUniqueName   string    `form:"sessionUniqueName"`
	SessionAccountSid   string    `form:"sessionAccountSid"`
	SessionServiceSid   string    `form:"sessionServiceSid"`
	SessionSid          string    `form:"sessionSid"`
	SessionStatus       string    `form:"sessionStatus"`
	SessionMode         string    `form:"sessionMode"`
	SessionDateCreated  time.Time `form:"sessionDateCreated"`
	SessionDateUpdated  time.Time `form:"sessionDateUpdated"`
	SessionDateEnded    time.Time `form:"sessionDateEnded"`
	SessionClosedReason string    `form:"sessionClosedReason"`

	To          string `form:"To"`
	ToCity      string `form:"ToCity"`
	ToState     string `form:"ToState"`
	ToZip       string `form:"ToZip"`
	ToCountry   string `form:"ToCountry"`
	From        string `form:"From"`
	FromCity    string `form:"FromCity"`
	FromState   string `form:"FromState"`
	FromZip     string `form:"FromZip"`
	FromCountry string `form:"FromCountry"`

	InboundParticipantSid                string    `form:"inboundParticipantSid"`
	InboundParticipantIdentifier         string    `form:"inboundParticipantIdentifier"`
	InboundParticipantFriendlyName       string    `form:"inboundParticipantFriendlyName"`
	InboundParticipantProxyIdentifier    string    `form:"inboundParticipantProxyIdentifier"`
	InboundParticipantProxyIdentifierSid string    `form:"inboundParticipantProxyIdentifierSid"`
	InboundParticipantAccountSid         string    `form:"inboundParticipantAccountSid"`
	InboundParticipantServiceSid         string    `form:"inboundParticipantServiceSid"`
	InboundParticipantSessionSid         string    `form:"inboundParticipantSessionSid"`
	InboundParticipantDateCreated        time.Time `form:"inboundParticipantDateCreated"`
	InboundParticipantDateUpdated        time.Time `form:"inboundParticipantDateUpdated"`

	OutboundParticipantSid                string    `form:"outboundParticipantSid"`
	OutboundParticipantIdentifier         string    `form:"outboundParticipantIdentifier"`
	OutboundParticipantFriendlyName       string    `form:"outboundParticipantFriendlyName"`
	OutboundParticipantProxyIdentifier    string    `form:"outboundParticipantProxyIdentifier"`
	OutboundParticipantProxyIdentifierSid string    `form:"outboundParticipantProxyIdentifierSid"`
	OutboundParticipantAccountSid         string    `form:"outboundParticipantAccountSid"`
	OutboundParticipantServiceSid         string    `form:"outboundParticipantServiceSid"`
	OutboundParticipantSessionSid         string    `form:"outboundParticipantSessionSid"`
	OutboundParticipantDateCreated        time.Time `form:"outboundParticipantDateCreated"`
	OutboundParticipantDateUpdated        time.Time `form:"outboundParticipantDateUpdated"`

	CallSid    string `form:"CallSid"`
	CallStatus string `form:"CallStatus"`

	Caller        string `form:"Caller"`
	CallerCity    string `form:"CallerCity"`
	CallerState   string `form:"CallerState"`
	CallerZip     string `form:"CallerZip"`
	CallerCountry string `form:"CallerCountry"`

	Called        string `form:"Called"`
	CalledCity    string `form:"CalledCity"`
	CalledState   string `form:"CalledState"`
	CalledZip     string `form:"CalledZip"`
	CalledCountry string `form:"CalledCountry"`

	Direction  string `form:"Direction"`
	AddOns     string `form:"AddOns"`
	APIVersion string `form:"ApiVersion"`
}
<|endoftext|>


<commit_before>package paypal

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

// VerifyWebhookSignature - Use this to verify the signature of a webhook recieved from paypal.
// Endpoint: POST /v1/notifications/verify-webhook-signature
func (c *Client) VerifyWebhookSignature(httpReq *http.Request, webhookID string) (*VerifyWebhookResponse, error) {
	type verifyWebhookSignatureRequest struct {
		AuthAlgo         string          `json:"auth_algo,omitempty"`
		CertURL          string          `json:"cert_url,omitempty"`
		TransmissionID   string          `json:"transmission_id,omitempty"`
		TransmissionSig  string          `json:"transmission_sig,omitempty"`
		TransmissionTime string          `json:"transmission_time,omitempty"`
		WebhookID        string          `json:"webhook_id,omitempty"`
		WebhookEvent     json.RawMessage `json:"webhook_event"`
	}
	getBody := httpReq.GetBody
	bodyReadCloser, err := getBody()
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(bodyReadCloser)
	if err != nil {
		return nil, err
	}

	verifyRequest := verifyWebhookSignatureRequest{
		AuthAlgo:         httpReq.Header.Get("PAYPAL-AUTH-ALGO"),
		CertURL:          httpReq.Header.Get("PAYPAL-CERT-URL"),
		TransmissionID:   httpReq.Header.Get("PAYPAL-TRANSMISSION-ID"),
		TransmissionSig:  httpReq.Header.Get("PAYPAL-TRANSMISSION-SIG"),
		TransmissionTime: httpReq.Header.Get("PAYPAL-TRANSMISSION-TIME"),
		WebhookID:        webhookID,
		WebhookEvent:     json.RawMessage(body),
	}

	response := &VerifyWebhookResponse{}

	req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/verify-webhook-signature"), verifyRequest)
	if err != nil {
		return nil, err
	}

	if err = c.SendWithAuth(req, response); err != nil {
		return nil, err
	}

	return response, nil
}
<commit_msg>Reset after reading (#118)<commit_after>package paypal

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

// VerifyWebhookSignature - Use this to verify the signature of a webhook recieved from paypal.
// Endpoint: POST /v1/notifications/verify-webhook-signature
func (c *Client) VerifyWebhookSignature(httpReq *http.Request, webhookID string) (*VerifyWebhookResponse, error) {
	type verifyWebhookSignatureRequest struct {
		AuthAlgo         string          `json:"auth_algo,omitempty"`
		CertURL          string          `json:"cert_url,omitempty"`
		TransmissionID   string          `json:"transmission_id,omitempty"`
		TransmissionSig  string          `json:"transmission_sig,omitempty"`
		TransmissionTime string          `json:"transmission_time,omitempty"`
		WebhookID        string          `json:"webhook_id,omitempty"`
		WebhookEvent     json.RawMessage `json:"webhook_event"`
	}

	// Read the content
	var bodyBytes []byte
	if httpReq.Body != nil {
		bodyBytes, _ = ioutil.ReadAll(httpReq.Body)
	}
	// Restore the io.ReadCloser to its original state
	httpReq.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

	verifyRequest := verifyWebhookSignatureRequest{
		AuthAlgo:         httpReq.Header.Get("PAYPAL-AUTH-ALGO"),
		CertURL:          httpReq.Header.Get("PAYPAL-CERT-URL"),
		TransmissionID:   httpReq.Header.Get("PAYPAL-TRANSMISSION-ID"),
		TransmissionSig:  httpReq.Header.Get("PAYPAL-TRANSMISSION-SIG"),
		TransmissionTime: httpReq.Header.Get("PAYPAL-TRANSMISSION-TIME"),
		WebhookID:        webhookID,
		WebhookEvent:     json.RawMessage(bodyBytes),
	}

	response := &VerifyWebhookResponse{}

	req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/verify-webhook-signature"), verifyRequest)
	if err != nil {
		return nil, err
	}

	if err = c.SendWithAuth(req, response); err != nil {
		return nil, err
	}

	return response, nil
}
<|endoftext|>