<commit_before>package commands

import (
	"errors"
	"fmt"
	"os"
	fp "path/filepath"
	"strings"
	"syscall"

	"github.com/codegangsta/cli"

	"github.com/yuuki1/droot/log"
	"github.com/yuuki1/droot/osutil"
)

var CommandArgRun = "--root ROOT_DIR [--user USER] [--group GROUP] [--bind SRC-PATH[:DEST-PATH]] [--robind SRC-PATH[:DEST-PATH]] -- COMMAND"
var CommandRun = cli.Command{
	Name:   "run",
	Usage:  "Run an extracted docker image from s3",
	Action: fatalOnError(doRun),
	Flags: []cli.Flag{
		cli.StringFlag{Name: "root, r", Usage: "Root directory path for chrooting"},
		cli.StringFlag{Name: "user, u", Usage: "User (ID or name) to switch before running the program"},
		cli.StringFlag{Name: "group, g", Usage: "Group (ID or name) to switch to"},
		cli.StringSliceFlag{
			Name:  "bind, b",
			Value: &cli.StringSlice{},
			Usage: "Bind mount directory (can be specifies multiple times)",
		},
		cli.StringSliceFlag{
			Name:  "robind",
			Value: &cli.StringSlice{},
			Usage: "Readonly bind mount directory (can be specifies multiple times)",
		},
		cli.BoolFlag{
			Name:  "copy-files, cp",
			Usage: "Copy host from containersuch as /etc/hosts, /etc/group, /etc/passwd, /etc/hosts",
		},
		cli.BoolFlag{Name: "no-capability", Usage: "Provide COMMAND's process in chroot with root permission (dangerous)"},
	},
}

var copyFiles = []string{
	"etc/group",
	"etc/passwd",
	"etc/resolv.conf",
	"etc/hosts",
}

var keepCaps = map[uint]bool{
	0:  true, // CAP_CHOWN
	1:  true, // CAP_DAC_OVERRIDE
	2:  true, // CAP_DAC_READ_SEARCH
	3:  true, // CAP_FOWNER
	6:  true, // CAP_SETGID
	7:  true, // CAP_SETUID
	10: true, // CAP_NET_BIND_SERVICE
}

func doRun(c *cli.Context) error {
	command := c.Args()
	if len(command) < 1 {
		cli.ShowCommandHelp(c, "run")
		return errors.New("command required")
	}

	rootDir := c.String("root")
	if rootDir == "" {
		cli.ShowCommandHelp(c, "run")
		return errors.New("--root option required")
	}

	if !osutil.ExistsDir(rootDir) {
		return fmt.Errorf("No such directory %s", rootDir)
	}

	// copy files
	if c.Bool("copy-files") {
		for _, f := range copyFiles {
			if err := osutil.Cp(fp.Join("/", f), fp.Join(rootDir, f)); err != nil {
				return err
			}
		}
	}

	// bind the directories
	if err := bindSystemMount(rootDir); err != nil {
		return err
	}

	for _, dir := range c.StringSlice("bind") {
		if err := bindMount(dir, rootDir, false); err != nil {
			return err
		}
	}
	for _, dir := range c.StringSlice("robind") {
		if err := bindMount(dir, rootDir, true); err != nil {
			return err
		}
	}

	// create symlinks
	if err := osutil.Symlink("../run/lock", fp.Join(rootDir, "/var/lock")); err != nil {
		return err
	}

	if err := createDevices(rootDir); err != nil {
		return err
	}

	log.Debug("chroot", rootDir, command)

	if err := syscall.Chroot(rootDir); err != nil {
		return err
	}
	if err := syscall.Chdir("/"); err != nil {
		return err
	}

	if !c.Bool("no-capability") {
		log.Debug("drop capabilities")
		if err := osutil.DropCapabilities(keepCaps); err != nil {
			return err
		}
	}

	if group := c.String("group"); group != "" {
		log.Debug("setgid", group)
		if err := osutil.SetGroup(group); err != nil {
			return err
		}
	}
	if user := c.String("user"); user != "" {
		log.Debug("setuid", user)
		if err := osutil.SetUser(user); err != nil {
			return err
		}
	}

	return osutil.Execv(command[0], command[0:], os.Environ())
}

func bindMount(bindDir string, rootDir string, readonly bool) error {
	var srcDir, destDir string

	d := strings.SplitN(bindDir, ":", 2)
	if len(d) < 2 {
		srcDir = d[0]
	} else {
		srcDir, destDir = d[0], d[1]
	}
	if destDir == "" {
		destDir = srcDir
	}

	ok, err := osutil.IsDirEmpty(srcDir)
	if err != nil {
		return err
	}
	if ok {
		if _, err := os.Create(fp.Join(srcDir, ".droot.keep")); err != nil {
			return err
		}
	}

	containerDir := fp.Join(rootDir, destDir)

	if err := os.MkdirAll(containerDir, os.FileMode(0755)); err != nil {
		return err
	}

	ok, err = osutil.IsDirEmpty(containerDir)
	if err != nil {
		return err
	}
	if ok {
		if err := osutil.BindMount(srcDir, containerDir); err != nil {
			return err
		}
		log.Debug("bind mount", bindDir, "to", containerDir)

		if readonly {
			if err := osutil.RObindMount(srcDir, containerDir); err != nil {
				return err
			}
			log.Debug("robind mount", bindDir, "to", containerDir)
		}
	}

	return nil
}

func bindSystemMount(rootDir string) error {
	procDir := fp.Join(rootDir, "/proc")
	if ok, err := osutil.Mounted(procDir); !ok && err == nil {
		if err := osutil.RunCmd("mount", "-t", "proc", "none", procDir); err != nil {
			return err
		}
	}

	sysDir := fp.Join(rootDir, "/sys")
	if ok, err := osutil.Mounted(sysDir); !ok && err == nil {
		if err := osutil.RunCmd("mount", "--rbind", "/sys", fp.Join(rootDir, "/sys")); err != nil {
			return err
		}
	}

	return nil
}

func createDevices(rootDir string) error {
	if err := osutil.Mknod(fp.Join(rootDir, os.DevNull), syscall.S_IFCHR|uint32(os.FileMode(0666)), 1*256+3); err != nil {
		return err
	}

	if err := osutil.Mknod(fp.Join(rootDir, "/dev/zero"), syscall.S_IFCHR|uint32(os.FileMode(0666)), 1*256+3); err != nil {
		return err
	}

	for _, f := range []string{"/dev/random", "/dev/urandom"} {
		if err := osutil.Mknod(fp.Join(rootDir, f), syscall.S_IFCHR|uint32(os.FileMode(0666)), 1*256+9); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Rename --no-capability to --no-dropcaps<commit_after>package commands

import (
	"errors"
	"fmt"
	"os"
	fp "path/filepath"
	"strings"
	"syscall"

	"github.com/codegangsta/cli"

	"github.com/yuuki1/droot/log"
	"github.com/yuuki1/droot/osutil"
)

var CommandArgRun = "--root ROOT_DIR [--user USER] [--group GROUP] [--bind SRC-PATH[:DEST-PATH]] [--robind SRC-PATH[:DEST-PATH]] [--no-dropcaps] -- COMMAND"
var CommandRun = cli.Command{
	Name:   "run",
	Usage:  "Run an extracted docker image from s3",
	Action: fatalOnError(doRun),
	Flags: []cli.Flag{
		cli.StringFlag{Name: "root, r", Usage: "Root directory path for chrooting"},
		cli.StringFlag{Name: "user, u", Usage: "User (ID or name) to switch before running the program"},
		cli.StringFlag{Name: "group, g", Usage: "Group (ID or name) to switch to"},
		cli.StringSliceFlag{
			Name:  "bind, b",
			Value: &cli.StringSlice{},
			Usage: "Bind mount directory (can be specifies multiple times)",
		},
		cli.StringSliceFlag{
			Name:  "robind",
			Value: &cli.StringSlice{},
			Usage: "Readonly bind mount directory (can be specifies multiple times)",
		},
		cli.BoolFlag{
			Name:  "copy-files, cp",
			Usage: "Copy host from containersuch as /etc/hosts, /etc/group, /etc/passwd, /etc/hosts",
		},
		cli.BoolFlag{Name: "no-dropcaps", Usage: "Provide COMMAND's process in chroot with root permission (dangerous)"},
	},
}

var copyFiles = []string{
	"etc/group",
	"etc/passwd",
	"etc/resolv.conf",
	"etc/hosts",
}

var keepCaps = map[uint]bool{
	0:  true, // CAP_CHOWN
	1:  true, // CAP_DAC_OVERRIDE
	2:  true, // CAP_DAC_READ_SEARCH
	3:  true, // CAP_FOWNER
	6:  true, // CAP_SETGID
	7:  true, // CAP_SETUID
	10: true, // CAP_NET_BIND_SERVICE
}

func doRun(c *cli.Context) error {
	command := c.Args()
	if len(command) < 1 {
		cli.ShowCommandHelp(c, "run")
		return errors.New("command required")
	}

	rootDir := c.String("root")
	if rootDir == "" {
		cli.ShowCommandHelp(c, "run")
		return errors.New("--root option required")
	}

	if !osutil.ExistsDir(rootDir) {
		return fmt.Errorf("No such directory %s", rootDir)
	}

	// copy files
	if c.Bool("copy-files") {
		for _, f := range copyFiles {
			if err := osutil.Cp(fp.Join("/", f), fp.Join(rootDir, f)); err != nil {
				return err
			}
		}
	}

	// bind the directories
	if err := bindSystemMount(rootDir); err != nil {
		return err
	}

	for _, dir := range c.StringSlice("bind") {
		if err := bindMount(dir, rootDir, false); err != nil {
			return err
		}
	}
	for _, dir := range c.StringSlice("robind") {
		if err := bindMount(dir, rootDir, true); err != nil {
			return err
		}
	}

	// create symlinks
	if err := osutil.Symlink("../run/lock", fp.Join(rootDir, "/var/lock")); err != nil {
		return err
	}

	if err := createDevices(rootDir); err != nil {
		return err
	}

	log.Debug("chroot", rootDir, command)

	if err := syscall.Chroot(rootDir); err != nil {
		return err
	}
	if err := syscall.Chdir("/"); err != nil {
		return err
	}

	if !c.Bool("no-dropcaps") {
		log.Debug("drop capabilities")
		if err := osutil.DropCapabilities(keepCaps); err != nil {
			return err
		}
	}

	if group := c.String("group"); group != "" {
		log.Debug("setgid", group)
		if err := osutil.SetGroup(group); err != nil {
			return err
		}
	}
	if user := c.String("user"); user != "" {
		log.Debug("setuid", user)
		if err := osutil.SetUser(user); err != nil {
			return err
		}
	}

	return osutil.Execv(command[0], command[0:], os.Environ())
}

func bindMount(bindDir string, rootDir string, readonly bool) error {
	var srcDir, destDir string

	d := strings.SplitN(bindDir, ":", 2)
	if len(d) < 2 {
		srcDir = d[0]
	} else {
		srcDir, destDir = d[0], d[1]
	}
	if destDir == "" {
		destDir = srcDir
	}

	ok, err := osutil.IsDirEmpty(srcDir)
	if err != nil {
		return err
	}
	if ok {
		if _, err := os.Create(fp.Join(srcDir, ".droot.keep")); err != nil {
			return err
		}
	}

	containerDir := fp.Join(rootDir, destDir)

	if err := os.MkdirAll(containerDir, os.FileMode(0755)); err != nil {
		return err
	}

	ok, err = osutil.IsDirEmpty(containerDir)
	if err != nil {
		return err
	}
	if ok {
		if err := osutil.BindMount(srcDir, containerDir); err != nil {
			return err
		}
		log.Debug("bind mount", bindDir, "to", containerDir)

		if readonly {
			if err := osutil.RObindMount(srcDir, containerDir); err != nil {
				return err
			}
			log.Debug("robind mount", bindDir, "to", containerDir)
		}
	}

	return nil
}

func bindSystemMount(rootDir string) error {
	procDir := fp.Join(rootDir, "/proc")
	if ok, err := osutil.Mounted(procDir); !ok && err == nil {
		if err := osutil.RunCmd("mount", "-t", "proc", "none", procDir); err != nil {
			return err
		}
	}

	sysDir := fp.Join(rootDir, "/sys")
	if ok, err := osutil.Mounted(sysDir); !ok && err == nil {
		if err := osutil.RunCmd("mount", "--rbind", "/sys", fp.Join(rootDir, "/sys")); err != nil {
			return err
		}
	}

	return nil
}

func createDevices(rootDir string) error {
	if err := osutil.Mknod(fp.Join(rootDir, os.DevNull), syscall.S_IFCHR|uint32(os.FileMode(0666)), 1*256+3); err != nil {
		return err
	}

	if err := osutil.Mknod(fp.Join(rootDir, "/dev/zero"), syscall.S_IFCHR|uint32(os.FileMode(0666)), 1*256+3); err != nil {
		return err
	}

	for _, f := range []string{"/dev/random", "/dev/urandom"} {
		if err := osutil.Mknod(fp.Join(rootDir, f), syscall.S_IFCHR|uint32(os.FileMode(0666)), 1*256+9); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package snowball

import (
	"fmt"
	"strings"
	"testing"
)

func TestParametersValid(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      1,
		BetaRogue:         1,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err != nil {
		t.Fatal(err)
	}
}

func TestParametersAnotherValid(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      28,
		BetaRogue:         30,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err != nil {
		t.Fatal(err)
	}
}

func TestParametersYetAnotherValid(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      3,
		BetaRogue:         3,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err != nil {
		t.Fatal(err)
	}
}

func TestParametersInvalidK(t *testing.T) {
	p := Parameters{
		K:                 0,
		Alpha:             1,
		BetaVirtuous:      1,
		BetaRogue:         1,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err == nil {
		t.Fatalf("Should have failed due to invalid k")
	}
}

func TestParametersInvalidAlpha(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             0,
		BetaVirtuous:      1,
		BetaRogue:         1,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err == nil {
		t.Fatalf("Should have failed due to invalid alpha")
	}
}

func TestParametersInvalidBetaVirtuous(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      0,
		BetaRogue:         1,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err == nil {
		t.Fatalf("Should have failed due to invalid beta virtuous")
	}
}

func TestParametersInvalidBetaRogue(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      1,
		BetaRogue:         0,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err == nil {
		t.Fatalf("Should have failed due to invalid beta rogue")
	}
}

func TestParametersAnotherInvalidBetaRogue(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      28,
		BetaRogue:         3,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err == nil {
		t.Fatalf("Should have failed due to invalid beta rogue")
	} else if !strings.Contains(err.Error(), "\n") {
		t.Fatalf("Should have described the extensive error")
	}
}

func TestParametersInvalidConcurrentRepolls(t *testing.T) {
	tests := []Parameters{
		Parameters{
			K:                 1,
			Alpha:             1,
			BetaVirtuous:      1,
			BetaRogue:         1,
			ConcurrentRepolls: 2,
		},
		Parameters{
			K:                 1,
			Alpha:             1,
			BetaVirtuous:      1,
			BetaRogue:         1,
			ConcurrentRepolls: 0,
		},
	}
	for _, p := range tests {
		label := fmt.Sprintf("ConcurrentRepolls=%d", p.ConcurrentRepolls)
		t.Run(label, func(t *testing.T) {
			if err := p.Valid(); err == nil {
				t.Error("Should have failed due to invalid concurrent repolls")
			}
		})
	}
}
<commit_msg>formatting update<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package snowball

import (
	"fmt"
	"strings"
	"testing"
)

func TestParametersValid(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      1,
		BetaRogue:         1,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err != nil {
		t.Fatal(err)
	}
}

func TestParametersAnotherValid(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      28,
		BetaRogue:         30,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err != nil {
		t.Fatal(err)
	}
}

func TestParametersYetAnotherValid(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      3,
		BetaRogue:         3,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err != nil {
		t.Fatal(err)
	}
}

func TestParametersInvalidK(t *testing.T) {
	p := Parameters{
		K:                 0,
		Alpha:             1,
		BetaVirtuous:      1,
		BetaRogue:         1,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err == nil {
		t.Fatalf("Should have failed due to invalid k")
	}
}

func TestParametersInvalidAlpha(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             0,
		BetaVirtuous:      1,
		BetaRogue:         1,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err == nil {
		t.Fatalf("Should have failed due to invalid alpha")
	}
}

func TestParametersInvalidBetaVirtuous(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      0,
		BetaRogue:         1,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err == nil {
		t.Fatalf("Should have failed due to invalid beta virtuous")
	}
}

func TestParametersInvalidBetaRogue(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      1,
		BetaRogue:         0,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err == nil {
		t.Fatalf("Should have failed due to invalid beta rogue")
	}
}

func TestParametersAnotherInvalidBetaRogue(t *testing.T) {
	p := Parameters{
		K:                 1,
		Alpha:             1,
		BetaVirtuous:      28,
		BetaRogue:         3,
		ConcurrentRepolls: 1,
	}

	if err := p.Valid(); err == nil {
		t.Fatalf("Should have failed due to invalid beta rogue")
	} else if !strings.Contains(err.Error(), "\n") {
		t.Fatalf("Should have described the extensive error")
	}
}

func TestParametersInvalidConcurrentRepolls(t *testing.T) {
	tests := []Parameters{
		{
			K:                 1,
			Alpha:             1,
			BetaVirtuous:      1,
			BetaRogue:         1,
			ConcurrentRepolls: 2,
		},
		{
			K:                 1,
			Alpha:             1,
			BetaVirtuous:      1,
			BetaRogue:         1,
			ConcurrentRepolls: 0,
		},
	}
	for _, p := range tests {
		label := fmt.Sprintf("ConcurrentRepolls=%d", p.ConcurrentRepolls)
		t.Run(label, func(t *testing.T) {
			if err := p.Valid(); err == nil {
				t.Error("Should have failed due to invalid concurrent repolls")
			}
		})
	}
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package state

import (
	"testing"

	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/snow/consensus/snowstorm"
)

func TestVertexVerify(t *testing.T) {
	conflictingInputID := ids.NewID([32]byte{'i', 'n'})
	inputs := ids.Set{}
	inputs.Add(conflictingInputID)
	tx0 := &snowstorm.TestTx{
		Identifier: ids.NewID([32]byte{'t', 'x', '0'}),
		Deps:       nil,
		Ins:        inputs,
	}
	validVertex := &vertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{2})},
		txs:       []snowstorm.Tx{tx0},
	}

	if err := validVertex.Verify(); err != nil {
		t.Fatalf("Valid vertex failed verification due to: %w", err)
	}

	nonUniqueParentsVtx := &vertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{'d', 'u', 'p'}), ids.NewID([32]byte{'d', 'u', 'p'})},
		txs:       []snowstorm.Tx{tx0},
	}

	if err := nonUniqueParentsVtx.Verify(); err == nil {
		t.Fatal("Vertex with non unique parents should not have passed verification")
	}

	parent0 := ids.NewID([32]byte{0})
	parent1 := ids.NewID([32]byte{1})
	sortedParents := []ids.ID{parent0, parent1}
	ids.SortIDs(sortedParents)
	nonSortedParentsVtx := &vertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{sortedParents[1], sortedParents[0]},
		txs:       []snowstorm.Tx{tx0},
	}

	if err := nonSortedParentsVtx.Verify(); err == nil {
		t.Fatal("Vertex with non-sorted parents should not have passed verification")
	}

	noTxsVertex := &vertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{2})},
		txs:       []snowstorm.Tx{},
	}

	if err := noTxsVertex.Verify(); err == nil {
		t.Fatal("Vertex with no txs should not have passed verification")
	}

	tx1 := &snowstorm.TestTx{
		Identifier: ids.NewID([32]byte{'t', 'x', '1'}),
		Deps:       nil,
		Ins:        nil,
	}
	sortedTxs := []snowstorm.Tx{tx0, tx1}
	sortTxs(sortedTxs)
	unsortedTxsVertex := &vertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{2})},
		txs:       []snowstorm.Tx{sortedTxs[1], sortedTxs[0]},
	}

	if err := unsortedTxsVertex.Verify(); err == nil {
		t.Fatal("Vertex with unsorted transactions should not have passed verification")
	}

	nonUniqueTxsVertex := &vertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{2})},
		txs:       []snowstorm.Tx{tx0, tx0},
	}

	if err := nonUniqueTxsVertex.Verify(); err == nil {
		t.Fatal("Vertex with non-unique transactions should not have passed verification")
	}

	inputs.Add(ids.NewID([32]byte{'e', 'x', 't', 'r', 'a'}))
	conflictingTx := &snowstorm.TestTx{
		Identifier: ids.NewID([32]byte{'c', 'o', 'n', 'f', 'l', 'i', 'c', 't'}),
		Deps:       nil,
		Ins:        inputs,
	}

	conflictingTxs := []snowstorm.Tx{tx0, conflictingTx}
	sortTxs(conflictingTxs)

	conflictingTxsVertex := &vertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{2})},
		txs:       conflictingTxs,
	}

	if err := conflictingTxsVertex.Verify(); err == nil {
		t.Fatal("Vertex with conflicting transactions should not have passed verification")
	}
}
<commit_msg>Fix inner vertex test<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package state

import (
	"testing"

	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/snow/choices"
	"github.com/ava-labs/gecko/snow/consensus/snowstorm"
)

func TestVertexVerify(t *testing.T) {
	conflictingInputID := ids.NewID([32]byte{'i', 'n'})
	inputs := ids.Set{}
	inputs.Add(conflictingInputID)
	tx0 := &snowstorm.TestTx{
		TestDecidable: choices.TestDecidable{
			IDV: ids.NewID([32]byte{'t', 'x', '0'}),
		},
		DependenciesV: nil,
		InputIDsV:     inputs,
	}
	validVertex := &innerVertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{2})},
		txs:       []snowstorm.Tx{tx0},
	}

	if err := validVertex.Verify(); err != nil {
		t.Fatalf("Valid vertex failed verification due to: %w", err)
	}

	nonUniqueParentsVtx := &innerVertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{'d', 'u', 'p'}), ids.NewID([32]byte{'d', 'u', 'p'})},
		txs:       []snowstorm.Tx{tx0},
	}

	if err := nonUniqueParentsVtx.Verify(); err == nil {
		t.Fatal("Vertex with non unique parents should not have passed verification")
	}

	parent0 := ids.NewID([32]byte{0})
	parent1 := ids.NewID([32]byte{1})
	sortedParents := []ids.ID{parent0, parent1}
	ids.SortIDs(sortedParents)
	nonSortedParentsVtx := &innerVertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{sortedParents[1], sortedParents[0]},
		txs:       []snowstorm.Tx{tx0},
	}

	if err := nonSortedParentsVtx.Verify(); err == nil {
		t.Fatal("Vertex with non-sorted parents should not have passed verification")
	}

	noTxsVertex := &innerVertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{2})},
		txs:       []snowstorm.Tx{},
	}

	if err := noTxsVertex.Verify(); err == nil {
		t.Fatal("Vertex with no txs should not have passed verification")
	}

	tx1 := &snowstorm.TestTx{
		TestDecidable: choices.TestDecidable{
			IDV: ids.NewID([32]byte{'t', 'x', '1'}),
		},
		DependenciesV: nil,
		InputIDsV:     nil,
	}
	sortedTxs := []snowstorm.Tx{tx0, tx1}
	sortTxs(sortedTxs)
	unsortedTxsVertex := &innerVertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{2})},
		txs:       []snowstorm.Tx{sortedTxs[1], sortedTxs[0]},
	}

	if err := unsortedTxsVertex.Verify(); err == nil {
		t.Fatal("Vertex with unsorted transactions should not have passed verification")
	}

	nonUniqueTxsVertex := &innerVertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{2})},
		txs:       []snowstorm.Tx{tx0, tx0},
	}

	if err := nonUniqueTxsVertex.Verify(); err == nil {
		t.Fatal("Vertex with non-unique transactions should not have passed verification")
	}

	inputs.Add(ids.NewID([32]byte{'e', 'x', 't', 'r', 'a'}))
	conflictingTx := &snowstorm.TestTx{
		TestDecidable: choices.TestDecidable{
			IDV: ids.NewID([32]byte{'c', 'o', 'n', 'f', 'l', 'i', 'c', 't'}),
		},
		DependenciesV: nil,
		InputIDsV:     inputs,
	}

	conflictingTxs := []snowstorm.Tx{tx0, conflictingTx}
	sortTxs(conflictingTxs)

	conflictingTxsVertex := &innerVertex{
		id:        ids.NewID([32]byte{}),
		chainID:   ids.NewID([32]byte{1}),
		height:    1,
		parentIDs: []ids.ID{ids.NewID([32]byte{2})},
		txs:       conflictingTxs,
	}

	if err := conflictingTxsVertex.Verify(); err == nil {
		t.Fatal("Vertex with conflicting transactions should not have passed verification")
	}
}
<|endoftext|>


<commit_before>package common

import (
	"bytes"
	"fmt"

	"github.com/urakozz/go-emoji"
)

var parser = emoji.NewEmojiParser()

// ParseEmojis prases emojis from the message Text
func ParseEmojis(msg *Msg) {
	emoteCount := make(map[string]*Emote)
	_ = parser.ReplaceAllStringFunc(msg.Text, func(s string) string {
		byteArray := []byte(s)
		log.Debug(byteArray)
		log.Debug(bytes.Runes(byteArray))
		if emote, ok := emoteCount[s]; ok {
			emote.Count++
		} else {
			emoteCount[s] = &Emote{
				ID:    fmt.Sprintf("%x", bytes.Runes(byteArray)[0]),
				Type:  "emoji",
				SizeX: 28,
				SizeY: 28,
				IsGif: false,
				Count: 1,
			}
		}
		return ""
	})

	for _, emote := range emoteCount {
		msg.Emotes = append(msg.Emotes, *emote)
	}
}
<commit_msg>cleaned up output<commit_after>package common

import (
	"bytes"
	"fmt"

	"github.com/urakozz/go-emoji"
)

var parser = emoji.NewEmojiParser()

// ParseEmojis prases emojis from the message Text
func ParseEmojis(msg *Msg) {
	emoteCount := make(map[string]*Emote)
	_ = parser.ReplaceAllStringFunc(msg.Text, func(s string) string {
		byteArray := []byte(s)
		if emote, ok := emoteCount[s]; ok {
			emote.Count++
		} else {
			emoteCount[s] = &Emote{
				ID:    fmt.Sprintf("%x", bytes.Runes(byteArray)[0]),
				Type:  "emoji",
				SizeX: 28,
				SizeY: 28,
				IsGif: false,
				Count: 1,
			}
		}
		return ""
	})

	for _, emote := range emoteCount {
		msg.Emotes = append(msg.Emotes, *emote)
	}
}
<|endoftext|>


<commit_before>package vault

import (
	"bytes"
	"context"
	"crypto/ecdsa"
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	math "math"
	"net/http"
	"net/url"
	"sync"
	"time"

	log "github.com/hashicorp/go-hclog"
	"github.com/hashicorp/vault/helper/forwarding"

	"github.com/hashicorp/vault/sdk/helper/consts"
	"github.com/hashicorp/vault/vault/cluster"
	"github.com/hashicorp/vault/vault/replication"
	"golang.org/x/net/http2"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
)

type requestForwardingHandler struct {
	fws         *http2.Server
	fwRPCServer *grpc.Server
	logger      log.Logger
	ha          bool
	core        *Core
	stopCh      chan struct{}
}

type requestForwardingClusterClient struct {
	core *Core
}

// NewRequestForwardingHandler creates a cluster handler for use with request
// forwarding.
func NewRequestForwardingHandler(c *Core, fws *http2.Server, perfStandbySlots chan struct{}, perfStandbyRepCluster *replication.Cluster) (*requestForwardingHandler, error) {
	// Resolve locally to avoid races
	ha := c.ha != nil

	fwRPCServer := grpc.NewServer(
		grpc.KeepaliveParams(keepalive.ServerParameters{
			Time: 2 * cluster.HeartbeatInterval,
		}),
		grpc.MaxRecvMsgSize(math.MaxInt32),
		grpc.MaxSendMsgSize(math.MaxInt32),
	)

	if ha && c.clusterHandler != nil {
		RegisterRequestForwardingServer(fwRPCServer, &forwardedRequestRPCServer{
			core:                  c,
			handler:               c.clusterHandler,
			perfStandbySlots:      perfStandbySlots,
			perfStandbyRepCluster: perfStandbyRepCluster,
			raftFollowerStates:    c.raftFollowerStates,
		})
	}

	return &requestForwardingHandler{
		fws:         fws,
		fwRPCServer: fwRPCServer,
		ha:          ha,
		logger:      c.logger.Named("request-forward"),
		core:        c,
		stopCh:      make(chan struct{}),
	}, nil
}

// ClientLookup satisfies the ClusterClient interface and returns the ha tls
// client certs.
func (c *requestForwardingClusterClient) ClientLookup(ctx context.Context, requestInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) {
	parsedCert := c.core.localClusterParsedCert.Load().(*x509.Certificate)
	if parsedCert == nil {
		return nil, nil
	}
	currCert := c.core.localClusterCert.Load().([]byte)
	if len(currCert) == 0 {
		return nil, nil
	}
	localCert := make([]byte, len(currCert))
	copy(localCert, currCert)

	for _, subj := range requestInfo.AcceptableCAs {
		if bytes.Equal(subj, parsedCert.RawIssuer) {
			return &tls.Certificate{
				Certificate: [][]byte{localCert},
				PrivateKey:  c.core.localClusterPrivateKey.Load().(*ecdsa.PrivateKey),
				Leaf:        c.core.localClusterParsedCert.Load().(*x509.Certificate),
			}, nil
		}
	}

	return nil, nil
}

func (c *requestForwardingClusterClient) ServerName() string {
	parsedCert := c.core.localClusterParsedCert.Load().(*x509.Certificate)
	if parsedCert == nil {
		return ""
	}

	return parsedCert.Subject.CommonName
}

func (c *requestForwardingClusterClient) CACert(ctx context.Context) *x509.Certificate {
	return c.core.localClusterParsedCert.Load().(*x509.Certificate)
}

// ServerLookup satisfies the ClusterHandler interface and returns the server's
// tls certs.
func (rf *requestForwardingHandler) ServerLookup(ctx context.Context, clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	currCert := rf.core.localClusterCert.Load().([]byte)
	if len(currCert) == 0 {
		return nil, fmt.Errorf("got forwarding connection but no local cert")
	}

	localCert := make([]byte, len(currCert))
	copy(localCert, currCert)

	return &tls.Certificate{
		Certificate: [][]byte{localCert},
		PrivateKey:  rf.core.localClusterPrivateKey.Load().(*ecdsa.PrivateKey),
		Leaf:        rf.core.localClusterParsedCert.Load().(*x509.Certificate),
	}, nil
}

// CALookup satisfies the ClusterHandler interface and returns the ha ca cert.
func (rf *requestForwardingHandler) CALookup(ctx context.Context) ([]*x509.Certificate, error) {
	parsedCert := rf.core.localClusterParsedCert.Load().(*x509.Certificate)

	if parsedCert == nil {
		return nil, fmt.Errorf("forwarding connection client but no local cert")
	}

	return []*x509.Certificate{parsedCert}, nil
}

// Handoff serves a request forwarding connection.
func (rf *requestForwardingHandler) Handoff(ctx context.Context, shutdownWg *sync.WaitGroup, closeCh chan struct{}, tlsConn *tls.Conn) error {
	if !rf.ha {
		tlsConn.Close()
		return nil
	}

	rf.logger.Debug("got request forwarding connection")

	shutdownWg.Add(2)
	// quitCh is used to close the connection and the second
	// goroutine if the server closes before closeCh.
	quitCh := make(chan struct{})
	go func() {
		select {
		case <-quitCh:
		case <-closeCh:
		case <-rf.stopCh:
		}
		tlsConn.Close()
		shutdownWg.Done()
	}()

	go func() {
		rf.fws.ServeConn(tlsConn, &http2.ServeConnOpts{
			Handler: rf.fwRPCServer,
			BaseConfig: &http.Server{
				ErrorLog: rf.logger.StandardLogger(nil),
			},
		})

		// close the quitCh which will close the connection and
		// the other goroutine.
		close(quitCh)
		shutdownWg.Done()
	}()

	return nil
}

// Stop stops the request forwarding server and closes connections.
func (rf *requestForwardingHandler) Stop() error {
	// Give some time for existing RPCs to drain.
	time.Sleep(cluster.ListenerAcceptDeadline)
	close(rf.stopCh)
	rf.fwRPCServer.Stop()
	return nil
}

// Starts the listeners and servers necessary to handle forwarded requests
func (c *Core) startForwarding(ctx context.Context) error {
	c.logger.Debug("request forwarding setup function")
	defer c.logger.Debug("leaving request forwarding setup function")

	// Clean up in case we have transitioned from a client to a server
	c.requestForwardingConnectionLock.Lock()
	c.clearForwardingClients()
	c.requestForwardingConnectionLock.Unlock()

	if c.ha == nil || c.getClusterListener() == nil {
		c.logger.Debug("request forwarding not setup")
		return nil
	}

	perfStandbyRepCluster, perfStandbySlots, err := c.perfStandbyClusterHandler()
	if err != nil {
		return err
	}

	handler, err := NewRequestForwardingHandler(c, c.getClusterListener().Server(), perfStandbySlots, perfStandbyRepCluster)
	if err != nil {
		return err
	}

	c.getClusterListener().AddHandler(consts.RequestForwardingALPN, handler)

	return nil
}

func (c *Core) stopForwarding() {
	if c.getClusterListener() != nil {
		c.getClusterListener().StopHandler(consts.RequestForwardingALPN)
		c.getClusterListener().StopHandler(consts.PerfStandbyALPN)
	}
	c.removeAllPerfStandbySecondaries()
}

// refreshRequestForwardingConnection ensures that the client/transport are
// alive and that the current active address value matches the most
// recently-known address.
func (c *Core) refreshRequestForwardingConnection(ctx context.Context, clusterAddr string) error {
	c.logger.Debug("refreshing forwarding connection")
	defer c.logger.Debug("done refreshing forwarding connection")

	c.requestForwardingConnectionLock.Lock()
	defer c.requestForwardingConnectionLock.Unlock()

	// Clean things up first
	c.clearForwardingClients()

	// If we don't have anything to connect to, just return
	if clusterAddr == "" {
		return nil
	}

	clusterURL, err := url.Parse(clusterAddr)
	if err != nil {
		c.logger.Error("error parsing cluster address attempting to refresh forwarding connection", "error", err)
		return err
	}

	parsedCert := c.localClusterParsedCert.Load().(*x509.Certificate)
	if parsedCert == nil {
		c.logger.Error("no request forwarding cluster certificate found")
		return errors.New("no request forwarding cluster certificate found")
	}

	clusterListener := c.getClusterListener()
	if clusterListener == nil {
		c.logger.Error("no cluster listener configured")
		return nil
	}

	clusterListener.AddClient(consts.RequestForwardingALPN, &requestForwardingClusterClient{
		core: c,
	})

	// Set up grpc forwarding handling
	// It's not really insecure, but we have to dial manually to get the
	// ALPN header right. It's just "insecure" because GRPC isn't managing
	// the TLS state.
	dctx, cancelFunc := context.WithCancel(ctx)
	c.rpcClientConn, err = grpc.DialContext(dctx, clusterURL.Host,
		grpc.WithDialer(clusterListener.GetDialerFunc(ctx, consts.RequestForwardingALPN)),
		grpc.WithInsecure(), // it's not, we handle it in the dialer
		grpc.WithKeepaliveParams(keepalive.ClientParameters{
			Time: 2 * cluster.HeartbeatInterval,
		}),
		grpc.WithDefaultCallOptions(
			grpc.MaxCallRecvMsgSize(math.MaxInt32),
			grpc.MaxCallSendMsgSize(math.MaxInt32),
		))
	if err != nil {
		cancelFunc()
		c.logger.Error("err setting up forwarding rpc client", "error", err)
		return err
	}
	c.rpcClientConnContext = dctx
	c.rpcClientConnCancelFunc = cancelFunc
	c.rpcForwardingClient = &forwardingClient{
		RequestForwardingClient: NewRequestForwardingClient(c.rpcClientConn),
		core:                    c,
		echoTicker:              time.NewTicker(cluster.HeartbeatInterval),
		echoContext:             dctx,
	}
	c.rpcForwardingClient.startHeartbeat()

	return nil
}

func (c *Core) clearForwardingClients() {
	c.logger.Debug("clearing forwarding clients")
	defer c.logger.Debug("done clearing forwarding clients")

	if c.rpcClientConnCancelFunc != nil {
		c.rpcClientConnCancelFunc()
		c.rpcClientConnCancelFunc = nil
	}
	if c.rpcClientConn != nil {
		c.rpcClientConn.Close()
		c.rpcClientConn = nil
	}

	c.rpcClientConnContext = nil
	c.rpcForwardingClient = nil

	clusterListener := c.getClusterListener()
	if clusterListener != nil {
		clusterListener.RemoveClient(consts.RequestForwardingALPN)
	}
	c.clusterLeaderParams.Store((*ClusterLeaderParams)(nil))
}

// ForwardRequest forwards a given request to the active node and returns the
// response.
func (c *Core) ForwardRequest(req *http.Request) (int, http.Header, []byte, error) {
	c.requestForwardingConnectionLock.RLock()
	defer c.requestForwardingConnectionLock.RUnlock()

	if c.rpcForwardingClient == nil {
		return 0, nil, nil, ErrCannotForward
	}

	origPath := req.URL.Path
	defer func() {
		req.URL.Path = origPath
	}()

	req.URL.Path = req.Context().Value("original_request_path").(string)

	freq, err := forwarding.GenerateForwardedRequest(req)
	if err != nil {
		c.logger.Error("error creating forwarding RPC request", "error", err)
		return 0, nil, nil, fmt.Errorf("error creating forwarding RPC request")
	}
	if freq == nil {
		c.logger.Error("got nil forwarding RPC request")
		return 0, nil, nil, fmt.Errorf("got nil forwarding RPC request")
	}
	resp, err := c.rpcForwardingClient.ForwardRequest(c.rpcClientConnContext, freq)
	if err != nil {
		c.logger.Error("error during forwarded RPC request", "error", err)
		return 0, nil, nil, fmt.Errorf("error during forwarding RPC request")
	}

	var header http.Header
	if resp.HeaderEntries != nil {
		header = make(http.Header)
		for k, v := range resp.HeaderEntries {
			header[k] = v.Values
		}
	}

	// If we are a perf standby and the request was forwarded to the active node
	// we should attempt to wait for the WAL to ship to offer best effort read after
	// write guarantees
	if c.perfStandby && resp.LastRemoteWal > 0 {
		WaitUntilWALShipped(req.Context(), c, resp.LastRemoteWal)
	}

	return int(resp.StatusCode), header, resp.Body, nil
}
<commit_msg>Fix a test failure I observed on ent re cluster listener (#8647)<commit_after>package vault

import (
	"bytes"
	"context"
	"crypto/ecdsa"
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	math "math"
	"net/http"
	"net/url"
	"sync"
	"time"

	log "github.com/hashicorp/go-hclog"
	"github.com/hashicorp/vault/helper/forwarding"

	"github.com/hashicorp/vault/sdk/helper/consts"
	"github.com/hashicorp/vault/vault/cluster"
	"github.com/hashicorp/vault/vault/replication"
	"golang.org/x/net/http2"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
)

type requestForwardingHandler struct {
	fws         *http2.Server
	fwRPCServer *grpc.Server
	logger      log.Logger
	ha          bool
	core        *Core
	stopCh      chan struct{}
}

type requestForwardingClusterClient struct {
	core *Core
}

// NewRequestForwardingHandler creates a cluster handler for use with request
// forwarding.
func NewRequestForwardingHandler(c *Core, fws *http2.Server, perfStandbySlots chan struct{}, perfStandbyRepCluster *replication.Cluster) (*requestForwardingHandler, error) {
	// Resolve locally to avoid races
	ha := c.ha != nil

	fwRPCServer := grpc.NewServer(
		grpc.KeepaliveParams(keepalive.ServerParameters{
			Time: 2 * cluster.HeartbeatInterval,
		}),
		grpc.MaxRecvMsgSize(math.MaxInt32),
		grpc.MaxSendMsgSize(math.MaxInt32),
	)

	if ha && c.clusterHandler != nil {
		RegisterRequestForwardingServer(fwRPCServer, &forwardedRequestRPCServer{
			core:                  c,
			handler:               c.clusterHandler,
			perfStandbySlots:      perfStandbySlots,
			perfStandbyRepCluster: perfStandbyRepCluster,
			raftFollowerStates:    c.raftFollowerStates,
		})
	}

	return &requestForwardingHandler{
		fws:         fws,
		fwRPCServer: fwRPCServer,
		ha:          ha,
		logger:      c.logger.Named("request-forward"),
		core:        c,
		stopCh:      make(chan struct{}),
	}, nil
}

// ClientLookup satisfies the ClusterClient interface and returns the ha tls
// client certs.
func (c *requestForwardingClusterClient) ClientLookup(ctx context.Context, requestInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) {
	parsedCert := c.core.localClusterParsedCert.Load().(*x509.Certificate)
	if parsedCert == nil {
		return nil, nil
	}
	currCert := c.core.localClusterCert.Load().([]byte)
	if len(currCert) == 0 {
		return nil, nil
	}
	localCert := make([]byte, len(currCert))
	copy(localCert, currCert)

	for _, subj := range requestInfo.AcceptableCAs {
		if bytes.Equal(subj, parsedCert.RawIssuer) {
			return &tls.Certificate{
				Certificate: [][]byte{localCert},
				PrivateKey:  c.core.localClusterPrivateKey.Load().(*ecdsa.PrivateKey),
				Leaf:        c.core.localClusterParsedCert.Load().(*x509.Certificate),
			}, nil
		}
	}

	return nil, nil
}

func (c *requestForwardingClusterClient) ServerName() string {
	parsedCert := c.core.localClusterParsedCert.Load().(*x509.Certificate)
	if parsedCert == nil {
		return ""
	}

	return parsedCert.Subject.CommonName
}

func (c *requestForwardingClusterClient) CACert(ctx context.Context) *x509.Certificate {
	return c.core.localClusterParsedCert.Load().(*x509.Certificate)
}

// ServerLookup satisfies the ClusterHandler interface and returns the server's
// tls certs.
func (rf *requestForwardingHandler) ServerLookup(ctx context.Context, clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	currCert := rf.core.localClusterCert.Load().([]byte)
	if len(currCert) == 0 {
		return nil, fmt.Errorf("got forwarding connection but no local cert")
	}

	localCert := make([]byte, len(currCert))
	copy(localCert, currCert)

	return &tls.Certificate{
		Certificate: [][]byte{localCert},
		PrivateKey:  rf.core.localClusterPrivateKey.Load().(*ecdsa.PrivateKey),
		Leaf:        rf.core.localClusterParsedCert.Load().(*x509.Certificate),
	}, nil
}

// CALookup satisfies the ClusterHandler interface and returns the ha ca cert.
func (rf *requestForwardingHandler) CALookup(ctx context.Context) ([]*x509.Certificate, error) {
	parsedCert := rf.core.localClusterParsedCert.Load().(*x509.Certificate)

	if parsedCert == nil {
		return nil, fmt.Errorf("forwarding connection client but no local cert")
	}

	return []*x509.Certificate{parsedCert}, nil
}

// Handoff serves a request forwarding connection.
func (rf *requestForwardingHandler) Handoff(ctx context.Context, shutdownWg *sync.WaitGroup, closeCh chan struct{}, tlsConn *tls.Conn) error {
	if !rf.ha {
		tlsConn.Close()
		return nil
	}

	rf.logger.Debug("got request forwarding connection")

	shutdownWg.Add(2)
	// quitCh is used to close the connection and the second
	// goroutine if the server closes before closeCh.
	quitCh := make(chan struct{})
	go func() {
		select {
		case <-quitCh:
		case <-closeCh:
		case <-rf.stopCh:
		}
		tlsConn.Close()
		shutdownWg.Done()
	}()

	go func() {
		rf.fws.ServeConn(tlsConn, &http2.ServeConnOpts{
			Handler: rf.fwRPCServer,
			BaseConfig: &http.Server{
				ErrorLog: rf.logger.StandardLogger(nil),
			},
		})

		// close the quitCh which will close the connection and
		// the other goroutine.
		close(quitCh)
		shutdownWg.Done()
	}()

	return nil
}

// Stop stops the request forwarding server and closes connections.
func (rf *requestForwardingHandler) Stop() error {
	// Give some time for existing RPCs to drain.
	time.Sleep(cluster.ListenerAcceptDeadline)
	close(rf.stopCh)
	rf.fwRPCServer.Stop()
	return nil
}

// Starts the listeners and servers necessary to handle forwarded requests
func (c *Core) startForwarding(ctx context.Context) error {
	c.logger.Debug("request forwarding setup function")
	defer c.logger.Debug("leaving request forwarding setup function")

	// Clean up in case we have transitioned from a client to a server
	c.requestForwardingConnectionLock.Lock()
	c.clearForwardingClients()
	c.requestForwardingConnectionLock.Unlock()

	clusterListener := c.getClusterListener()
	if c.ha == nil || clusterListener == nil {
		c.logger.Debug("request forwarding not setup")
		return nil
	}

	perfStandbyRepCluster, perfStandbySlots, err := c.perfStandbyClusterHandler()
	if err != nil {
		return err
	}

	handler, err := NewRequestForwardingHandler(c, clusterListener.Server(), perfStandbySlots, perfStandbyRepCluster)
	if err != nil {
		return err
	}

	clusterListener.AddHandler(consts.RequestForwardingALPN, handler)

	return nil
}

func (c *Core) stopForwarding() {
	clusterListener := c.getClusterListener()
	if clusterListener != nil {
		clusterListener.StopHandler(consts.RequestForwardingALPN)
		clusterListener.StopHandler(consts.PerfStandbyALPN)
	}
	c.removeAllPerfStandbySecondaries()
}

// refreshRequestForwardingConnection ensures that the client/transport are
// alive and that the current active address value matches the most
// recently-known address.
func (c *Core) refreshRequestForwardingConnection(ctx context.Context, clusterAddr string) error {
	c.logger.Debug("refreshing forwarding connection")
	defer c.logger.Debug("done refreshing forwarding connection")

	c.requestForwardingConnectionLock.Lock()
	defer c.requestForwardingConnectionLock.Unlock()

	// Clean things up first
	c.clearForwardingClients()

	// If we don't have anything to connect to, just return
	if clusterAddr == "" {
		return nil
	}

	clusterURL, err := url.Parse(clusterAddr)
	if err != nil {
		c.logger.Error("error parsing cluster address attempting to refresh forwarding connection", "error", err)
		return err
	}

	parsedCert := c.localClusterParsedCert.Load().(*x509.Certificate)
	if parsedCert == nil {
		c.logger.Error("no request forwarding cluster certificate found")
		return errors.New("no request forwarding cluster certificate found")
	}

	clusterListener := c.getClusterListener()
	if clusterListener == nil {
		c.logger.Error("no cluster listener configured")
		return nil
	}

	clusterListener.AddClient(consts.RequestForwardingALPN, &requestForwardingClusterClient{
		core: c,
	})

	// Set up grpc forwarding handling
	// It's not really insecure, but we have to dial manually to get the
	// ALPN header right. It's just "insecure" because GRPC isn't managing
	// the TLS state.
	dctx, cancelFunc := context.WithCancel(ctx)
	c.rpcClientConn, err = grpc.DialContext(dctx, clusterURL.Host,
		grpc.WithDialer(clusterListener.GetDialerFunc(ctx, consts.RequestForwardingALPN)),
		grpc.WithInsecure(), // it's not, we handle it in the dialer
		grpc.WithKeepaliveParams(keepalive.ClientParameters{
			Time: 2 * cluster.HeartbeatInterval,
		}),
		grpc.WithDefaultCallOptions(
			grpc.MaxCallRecvMsgSize(math.MaxInt32),
			grpc.MaxCallSendMsgSize(math.MaxInt32),
		))
	if err != nil {
		cancelFunc()
		c.logger.Error("err setting up forwarding rpc client", "error", err)
		return err
	}
	c.rpcClientConnContext = dctx
	c.rpcClientConnCancelFunc = cancelFunc
	c.rpcForwardingClient = &forwardingClient{
		RequestForwardingClient: NewRequestForwardingClient(c.rpcClientConn),
		core:                    c,
		echoTicker:              time.NewTicker(cluster.HeartbeatInterval),
		echoContext:             dctx,
	}
	c.rpcForwardingClient.startHeartbeat()

	return nil
}

func (c *Core) clearForwardingClients() {
	c.logger.Debug("clearing forwarding clients")
	defer c.logger.Debug("done clearing forwarding clients")

	if c.rpcClientConnCancelFunc != nil {
		c.rpcClientConnCancelFunc()
		c.rpcClientConnCancelFunc = nil
	}
	if c.rpcClientConn != nil {
		c.rpcClientConn.Close()
		c.rpcClientConn = nil
	}

	c.rpcClientConnContext = nil
	c.rpcForwardingClient = nil

	clusterListener := c.getClusterListener()
	if clusterListener != nil {
		clusterListener.RemoveClient(consts.RequestForwardingALPN)
	}
	c.clusterLeaderParams.Store((*ClusterLeaderParams)(nil))
}

// ForwardRequest forwards a given request to the active node and returns the
// response.
func (c *Core) ForwardRequest(req *http.Request) (int, http.Header, []byte, error) {
	c.requestForwardingConnectionLock.RLock()
	defer c.requestForwardingConnectionLock.RUnlock()

	if c.rpcForwardingClient == nil {
		return 0, nil, nil, ErrCannotForward
	}

	origPath := req.URL.Path
	defer func() {
		req.URL.Path = origPath
	}()

	req.URL.Path = req.Context().Value("original_request_path").(string)

	freq, err := forwarding.GenerateForwardedRequest(req)
	if err != nil {
		c.logger.Error("error creating forwarding RPC request", "error", err)
		return 0, nil, nil, fmt.Errorf("error creating forwarding RPC request")
	}
	if freq == nil {
		c.logger.Error("got nil forwarding RPC request")
		return 0, nil, nil, fmt.Errorf("got nil forwarding RPC request")
	}
	resp, err := c.rpcForwardingClient.ForwardRequest(c.rpcClientConnContext, freq)
	if err != nil {
		c.logger.Error("error during forwarded RPC request", "error", err)
		return 0, nil, nil, fmt.Errorf("error during forwarding RPC request")
	}

	var header http.Header
	if resp.HeaderEntries != nil {
		header = make(http.Header)
		for k, v := range resp.HeaderEntries {
			header[k] = v.Values
		}
	}

	// If we are a perf standby and the request was forwarded to the active node
	// we should attempt to wait for the WAL to ship to offer best effort read after
	// write guarantees
	if c.perfStandby && resp.LastRemoteWal > 0 {
		WaitUntilWALShipped(req.Context(), c, resp.LastRemoteWal)
	}

	return int(resp.StatusCode), header, resp.Body, nil
}
<|endoftext|>


<commit_before>package params

import (
	"fmt"
	"html/template"
	"math"
	"time"

	"github.com/ostrost/ostent/flags"
)

func (f ParamsFuncs) HrefT(p *Params, num *Num) (template.HTMLAttr, error) {
	href, err := p.EncodeT(num)
	return SprintfAttr(" href=%q", href), err
}

func (f ParamsFuncs) LessD(p *Params, d *Delay, bclass string) (ALink, error) {
	return f.LinkD(p, d, bclass, f.DelayLess(*d, p.MinDelay.Duration), "-")
}

func (f ParamsFuncs) MoreD(p *Params, d *Delay, bclass string) (ALink, error) {
	return f.LinkD(p, d, bclass, f.DelayMore(*d, p.MinDelay.Duration), "+")
}

func (f ParamsFuncs) LessN(p *Params, num *Num, bclass string) (ALink, error) {
	return f.LinkN(p, num, bclass, f.Pow2Less(num.Absolute), "-")
}

func (f ParamsFuncs) MoreN(p *Params, num *Num, bclass string) (ALink, error) {
	return f.LinkN(p, num, bclass, f.Pow2More(num.Absolute), "+")
}

func (f ParamsFuncs) Vlink(p *Params, num *Num, absolute int, text, alignClass string) (VLink, error) {
	// f is unused
	vl := VLink{LinkText: text, LinkClass: "state"}
	negative := new(bool) // EncodeN will use .Negative being false by default
	if num.Absolute == absolute {
		vl.CaretClass = "caret"
		vl.LinkClass += " current"
		if (num.Alpha && !num.Negative) || (!num.Alpha && num.Negative) {
			vl.LinkClass += " dropup"
		}
		*negative = !num.Negative
	}
	qs, err := p.EncodeN(num, absolute, negative)
	if err != nil {
		return VLink{}, err
	}
	vl.LinkHref = qs
	vl.AlignClass = alignClass
	return vl, nil
}

func (f ParamsFuncs) DelayMore(d Delay, step time.Duration) time.Duration {
	// f is unused
	const s = time.Second
	const m = time.Second * 60
	var table = map[time.Duration]time.Duration{
		s:      2 * s,
		2 * s:  5 * s,
		5 * s:  10 * s,
		10 * s: 30 * s,
		30 * s: m,
		m:      2 * m,
		2 * m:  5 * m,
		5 * m:  10 * m,
		10 * m: 30 * m,
		30 * m: 60 * m,
	}
	if more, ok := table[d.D]; ok {
		return more
	}
	return d.D + step
}

func (f ParamsFuncs) DelayLess(d Delay, step time.Duration) time.Duration {
	// f is unused
	const s = time.Second
	const m = time.Second * 60
	var table = map[time.Duration]time.Duration{
		s:      s,
		2 * s:  s,
		5 * s:  2 * s,
		10 * s: 5 * s,
		30 * s: 10 * s,
		m:      30 * s,
		2 * m:  m,
		5 * m:  2 * m,
		10 * m: 5 * m,
		30 * m: 10 * m,
		60 * m: 30 * m,
	}
	if less, ok := table[d.D]; ok {
		return less
	}
	return d.D - step
}

func (f ParamsFuncs) LinkD(p *Params, d *Delay, bclass string, set time.Duration, badge string) (ALink, error) {
	// f is unused
	href, err := p.EncodeD(d, set)
	if err != nil {
		return ALink{}, err
	}
	var eclass string
	if badge == "-" && d.D == p.MinDelay.Duration {
		eclass, href = " disabled", "?"
	}
	if badge == "+" && d.D == p.MaxDelay.Duration {
		eclass, href = " disabled", "?"
	}
	return ALink{
		Href:       href,
		Text:       flags.DurationString(set),
		Badge:      badge,
		Class:      bclass + " " + eclass,
		ExtraClass: eclass,
	}, nil
}

func (f ParamsFuncs) Pow2Less(v int) int {
	// f is unused
	switch v {
	case 0:
		return 0
	case 1:
		return 0
	case 2:
		return 1
	}
	g := math.Log2(float64(v))
	n := math.Floor(g)
	if n == g {
		n--
	}
	return int(math.Pow(2, n))
}

func (f ParamsFuncs) Pow2More(v int) int {
	// f is unused
	switch v {
	case 0:
		return 1
	case 1:
		return 2
	case 2:
		return 4
	}
	if v <= 32768 { // up to 65536
		v = int(math.Pow(2, 1+math.Floor(math.Log2(float64(v)))))
	}
	return v
}

func (f ParamsFuncs) LinkN(p *Params, num *Num, bclass string, absolute int, badge string) (ALink, error) {
	// f is unused
	href, err := p.EncodeN(num, absolute, nil)
	if err != nil {
		return ALink{}, err
	}
	var eclass string
	if badge == "+" && num.Absolute >= num.Limit && absolute > num.Limit {
		eclass, href = " disabled", "?"
	}
	if badge == "-" && absolute == 0 {
		eclass, href = " disabled", "?"
	}
	return ALink{
		Href:       href,
		Text:       fmt.Sprintf("%d", absolute),
		Badge:      badge,
		Class:      bclass + " " + eclass,
		ExtraClass: eclass,
	}, nil
}

type ParamsFuncs struct{}

func SprintfAttr(format string, args ...interface{}) template.HTMLAttr {
	return template.HTMLAttr(fmt.Sprintf(format, args...))
}
<commit_msg>Init values for ALink are as if buttons are disabled<commit_after>package params

import (
	"fmt"
	"html/template"
	"math"
	"time"

	"github.com/ostrost/ostent/flags"
)

func (f ParamsFuncs) HrefT(p *Params, num *Num) (template.HTMLAttr, error) {
	href, err := p.EncodeT(num)
	return SprintfAttr(" href=%q", href), err
}

func (f ParamsFuncs) LessD(p *Params, d *Delay, bclass string) (ALink, error) {
	return f.LinkD(p, d, bclass, f.DelayLess(*d, p.MinDelay.Duration), "-")
}

func (f ParamsFuncs) MoreD(p *Params, d *Delay, bclass string) (ALink, error) {
	return f.LinkD(p, d, bclass, f.DelayMore(*d, p.MinDelay.Duration), "+")
}

func (f ParamsFuncs) LessN(p *Params, num *Num, bclass string) (ALink, error) {
	return f.LinkN(p, num, bclass, f.Pow2Less(num.Absolute), "-")
}

func (f ParamsFuncs) MoreN(p *Params, num *Num, bclass string) (ALink, error) {
	return f.LinkN(p, num, bclass, f.Pow2More(num.Absolute), "+")
}

func (f ParamsFuncs) Vlink(p *Params, num *Num, absolute int, text, alignClass string) (VLink, error) {
	// f is unused
	vl := VLink{LinkText: text, LinkClass: "state"}
	negative := new(bool) // EncodeN will use .Negative being false by default
	if num.Absolute == absolute {
		vl.CaretClass = "caret"
		vl.LinkClass += " current"
		if (num.Alpha && !num.Negative) || (!num.Alpha && num.Negative) {
			vl.LinkClass += " dropup"
		}
		*negative = !num.Negative
	}
	qs, err := p.EncodeN(num, absolute, negative)
	if err != nil {
		return VLink{}, err
	}
	vl.LinkHref = qs
	vl.AlignClass = alignClass
	return vl, nil
}

func (f ParamsFuncs) DelayMore(d Delay, step time.Duration) time.Duration {
	// f is unused
	const s = time.Second
	const m = time.Second * 60
	var table = map[time.Duration]time.Duration{
		s:      2 * s,
		2 * s:  5 * s,
		5 * s:  10 * s,
		10 * s: 30 * s,
		30 * s: m,
		m:      2 * m,
		2 * m:  5 * m,
		5 * m:  10 * m,
		10 * m: 30 * m,
		30 * m: 60 * m,
	}
	if more, ok := table[d.D]; ok {
		return more
	}
	return d.D + step
}

func (f ParamsFuncs) DelayLess(d Delay, step time.Duration) time.Duration {
	// f is unused
	const s = time.Second
	const m = time.Second * 60
	var table = map[time.Duration]time.Duration{
		s:      s,
		2 * s:  s,
		5 * s:  2 * s,
		10 * s: 5 * s,
		30 * s: 10 * s,
		m:      30 * s,
		2 * m:  m,
		5 * m:  2 * m,
		10 * m: 5 * m,
		30 * m: 10 * m,
		60 * m: 30 * m,
	}
	if less, ok := table[d.D]; ok {
		return less
	}
	return d.D - step
}

func (f ParamsFuncs) LinkD(p *Params, d *Delay, bclass string, set time.Duration, badge string) (ALink, error) {
	// f is unused
	al := ALink{
		Href:       "?",                   // Default
		ExtraClass: " disabled ",          //         Disabled
		Class:      " disabled " + bclass, //                  Values

		Text:  flags.DurationString(set), // Final
		Badge: badge,                     //       Values
	}
	href, err := p.EncodeD(d, set)
	if err != nil {
		return al, err
	}
	switch badge {
	case "-":
		if d.D > p.MinDelay.Duration {
			al.Href, al.ExtraClass = href, ""
		}
	case "+":
		if d.D < p.MaxDelay.Duration {
			al.Href, al.ExtraClass = href, ""
		}
	}
	al.Class = al.ExtraClass + " " + bclass // Eventually
	return al, nil
}

func (f ParamsFuncs) Pow2Less(v int) int {
	// f is unused
	switch v {
	case 0:
		return 0
	case 1:
		return 0
	case 2:
		return 1
	}
	g := math.Log2(float64(v))
	n := math.Floor(g)
	if n == g {
		n--
	}
	return int(math.Pow(2, n))
}

func (f ParamsFuncs) Pow2More(v int) int {
	// f is unused
	switch v {
	case 0:
		return 1
	case 1:
		return 2
	case 2:
		return 4
	}
	if v <= 32768 { // up to 65536
		v = int(math.Pow(2, 1+math.Floor(math.Log2(float64(v)))))
	}
	return v
}

func (f ParamsFuncs) LinkN(p *Params, num *Num, bclass string, absolute int, badge string) (ALink, error) {
	// f is unused
	al := ALink{ // defaults
		Href:       "?",                   // Default
		ExtraClass: " disabled ",          //         Disabled
		Class:      " disabled " + bclass, //                  Values

		Text:  fmt.Sprintf("%d", absolute), // Final
		Badge: badge,                       //       Values
	}
	href, err := p.EncodeN(num, absolute, nil)
	if err != nil {
		return al, err
	}
	switch badge {
	case "+":
		// when num.Limit is 0, it's unknown, so enable the button
		if num.Limit == 0 || num.Absolute < num.Limit || absolute <= num.Limit {
			al.Href, al.ExtraClass = href, ""
		}
	case "-":
		if absolute > 0 || num.Absolute > 0 {
			al.Href, al.ExtraClass = href, ""
		}
	}
	al.Class = al.ExtraClass + " " + bclass // Eventually
	return al, nil
}

type ParamsFuncs struct{}

func SprintfAttr(format string, args ...interface{}) template.HTMLAttr {
	return template.HTMLAttr(fmt.Sprintf(format, args...))
}
<|endoftext|>


<commit_before>package edsm

import (
	"regexp"
)

const (
	distance_command_regex		= `^(.*)\s?\/\s?(.*)`
	sphere_command_regex		= `^(.*)\s+?(\d*\.?\d*)`
)

// Regex that matches commands like:
// "sothis / bava".
// Two capture groups are returned  (.*), (sothis), (bava)
func RegexMatchDistanceCommand(msg string) []string {
	placesPattern := regexp.MustCompile(distance_command_regex)
	return placesPattern.FindStringSubmatch(msg)
}

// Regex that matches commands like:
// "sothis 14.33ly".
// Two capture groups are returned  (.*), (sothis), (14.33)
func RegexMatchSphereCommand(msg string) []string {
	placesPattern := regexp.MustCompile(sphere_command_regex)
	return placesPattern.FindStringSubmatch(msg)
}
<commit_msg>updated go to 1.6.3 and casing fix<commit_after>package edsm

import (
	"regexp"
)

const (
	distance_command_regex = `^(.*)\s?\/\s?(.*)`
	sphere_command_regex   = `^(.*)\s+?(\d*\.?\d*)`
)

// Regex that matches commands like:
// "sothis / bava".
// Two capture groups are returned  (.*), (sothis), (bava)
func RegexMatchDistanceCommand(msg string) []string {
	placesPattern := regexp.MustCompile(distance_command_regex)
	return placesPattern.FindStringSubmatch(msg)
}

// Regex that matches commands like:
// "sothis 14.33ly".
// Two capture groups are returned  (.*), (sothis), (14.33)
func RegexMatchSphereCommand(msg string) []string {
	placesPattern := regexp.MustCompile(sphere_command_regex)
	return placesPattern.FindStringSubmatch(msg)
}
<|endoftext|>


<commit_before>package extra

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"

	seabird "github.com/belak/go-seabird"
	"github.com/belak/go-seabird/plugins/utils"
	irc "gopkg.in/irc.v3"
)

var runescapeOldSchoolSkillNames = []string{
	"total",
	"attack",
	"defence",
	"strength",
	"hitpoints",
	"ranged",
	"prayer",
	"magic",
	"cooking",
	"woodcutting",
	"fletching",
	"fishing",
	"firemaking",
	"crafting",
	"smithing",
	"mining",
	"herblore",
	"agility",
	"thieving",
	"slayer",
	"farming",
	"runecraft",
	"hunter",
	"construction",
}

type runescapeLevelMetadata struct {
	Rank  int
	Level int
	Exp   int

	Player string
	Skill  string
}

type runescapePlugin struct{}

func init() {
	seabird.RegisterPlugin("runescape", newRunescapePlugin)
}

func newRunescapeLevelMetadata(name, player, line string) (*runescapeLevelMetadata, error) {
	levelData := strings.Split(line, ",")
	if len(levelData) < 3 {
		return nil, fmt.Errorf("Invalid data")
	}
	rank, err := strconv.Atoi(levelData[0])
	if err != nil {
		return nil, err
	}
	level, err := strconv.Atoi(levelData[1])
	if err != nil {
		return nil, err
	}
	exp, err := strconv.Atoi(levelData[2])
	if err != nil {
		return nil, err
	}
	return &runescapeLevelMetadata{
		Rank:   rank,
		Level:  level,
		Exp:    exp,
		Player: player,
		Skill:  name,
	}, nil
}

func newRunescapePlugin(b *seabird.Bot, cm *seabird.CommandMux) error {
	p := &runescapePlugin{}

	cm.Event("rlvl", p.levelCallback, &seabird.HelpInfo{
		Usage:       "<player> <skill>",
		Description: "Returns a player's Old-School Runescape skill level",
	})
	cm.Event("rexp", p.expCallback, &seabird.HelpInfo{
		Usage:       "<player> <skill>",
		Description: "Returns a player's Old-School Runescape skill exp",
	})
	cm.Event("rrank", p.rankCallback, &seabird.HelpInfo{
		Usage:       "<player> <skill>",
		Description: "Returns a player's Old-School Runescape skill rank",
	})

	return nil
}

func (p *runescapePlugin) getPlayerSkills(search string) (*runescapeLevelMetadata, error) {
	args := strings.SplitN(search, " ", 2)
	if len(args) != 2 {
		return nil, errors.New("Wrong number of args")
	}
	player := args[0]
	skill := args[1]

	resp, err := http.Get("https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=" + player)
	if err != nil {
		return nil, err
	}

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	data := strings.Split(strings.TrimSpace(string(bytes)), "\n")

	// It's not strictly needed to build all this up, but it may be useful later.
	var ret = make(map[string]*runescapeLevelMetadata)
	if len(data) < len(runescapeOldSchoolSkillNames) {
		return nil, fmt.Errorf("Invalid data")
	}

	for i, name := range runescapeOldSchoolSkillNames {
		md, err := newRunescapeLevelMetadata(name, player, data[i])
		if err != nil {
			return nil, err
		}

		ret[md.Skill] = md
	}

	// Pull out the proper data
	md := ret[skill]
	if md == nil {
		return nil, fmt.Errorf("Unknown skill %q", skill)
	}

	return md, nil
}

func (p *runescapePlugin) levelCallback(b *seabird.Bot, m *irc.Message) {
	trailing := m.Trailing()
	go func() {
		data, err := p.getPlayerSkills(trailing)
		if err != nil {
			b.MentionReply(m, "%s", err)
			return
		}

		b.MentionReply(m, "%s has level %s %s", data.Player, utils.PrettifyNumber(data.Level), data.Skill)
	}()
}

func (p *runescapePlugin) expCallback(b *seabird.Bot, m *irc.Message) {
	trailing := m.Trailing()
	go func() {
		data, err := p.getPlayerSkills(trailing)
		if err != nil {
			b.MentionReply(m, "%s", err)
			return
		}

		b.MentionReply(m, "%s has %s experience in %s", data.Player, utils.PrettifySuffix(data.Exp), data.Skill)
	}()
}

func (p *runescapePlugin) rankCallback(b *seabird.Bot, m *irc.Message) {
	trailing := m.Trailing()
	go func() {
		data, err := p.getPlayerSkills(trailing)
		if err != nil {
			b.MentionReply(m, "%s", err)
			return
		}

		b.MentionReply(m, "%s has rank %s in %s", data.Player, utils.PrettifyNumber(data.Rank), data.Skill)
	}()
}
<commit_msg>Add ability to get player combat levels<commit_after>package extra

import (
	"errors"
	"fmt"
	"io/ioutil"
	"math"
	"net/http"
	"strconv"
	"strings"

	seabird "github.com/belak/go-seabird"
	"github.com/belak/go-seabird/plugins/utils"
	irc "gopkg.in/irc.v3"
)

var runescapeOldSchoolSkillNames = []string{
	"total",
	"attack",
	"defence",
	"strength",
	"hitpoints",
	"ranged",
	"prayer",
	"magic",
	"cooking",
	"woodcutting",
	"fletching",
	"fishing",
	"firemaking",
	"crafting",
	"smithing",
	"mining",
	"herblore",
	"agility",
	"thieving",
	"slayer",
	"farming",
	"runecraft",
	"hunter",
	"construction",
}

type runescapeLevelMetadata struct {
	Rank  int
	Level int
	Exp   int

	Player string
	Skill  string
}

type runescapePlugin struct{}

func init() {
	seabird.RegisterPlugin("runescape", newRunescapePlugin)
}

func newRunescapeLevelMetadata(name, player, line string) (*runescapeLevelMetadata, error) {
	levelData := strings.Split(line, ",")
	if len(levelData) < 3 {
		return nil, fmt.Errorf("Invalid data")
	}
	rank, err := strconv.Atoi(levelData[0])
	if err != nil {
		return nil, err
	}
	level, err := strconv.Atoi(levelData[1])
	if err != nil {
		return nil, err
	}
	exp, err := strconv.Atoi(levelData[2])
	if err != nil {
		return nil, err
	}
	return &runescapeLevelMetadata{
		Rank:   rank,
		Level:  level,
		Exp:    exp,
		Player: player,
		Skill:  name,
	}, nil
}

func newRunescapePlugin(b *seabird.Bot, cm *seabird.CommandMux) error {
	p := &runescapePlugin{}

	cm.Event("rlvl", p.levelCallback, &seabird.HelpInfo{
		Usage:       "<player> <skill>",
		Description: "Returns a player's Old-School Runescape skill level",
	})
	cm.Event("rexp", p.expCallback, &seabird.HelpInfo{
		Usage:       "<player> <skill>",
		Description: "Returns a player's Old-School Runescape skill exp",
	})
	cm.Event("rrank", p.rankCallback, &seabird.HelpInfo{
		Usage:       "<player> <skill>",
		Description: "Returns a player's Old-School Runescape skill rank",
	})

	return nil
}

func getCombatLevel(attack, defence, strength, hitpoints, ranged, prayer, magic int) int {
	base := 0.25 * (float64(defence) + float64(hitpoints) + math.Floor(float64(prayer)/2))
	melee_option := 0.325 * (float64(attack) + float64(strength))
	ranged_float := float64(ranged)
	ranged_option := 0.325 * (math.Floor(ranged_float/2) + ranged_float)
	magic_float := float64(magic)
	magic_option := 0.325 * (math.Floor(magic_float/2) + magic_float)

	return int(math.Floor(base + math.Max(melee_option, math.Max(ranged_option, magic_option))))
}

func (p *runescapePlugin) getPlayerSkills(search string) (*runescapeLevelMetadata, error) {
	args := strings.SplitN(search, " ", 2)
	if len(args) != 2 {
		return nil, errors.New("Wrong number of args")
	}
	player := args[0]
	skill := args[1]

	resp, err := http.Get("https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=" + player)
	if err != nil {
		return nil, err
	}

	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	data := strings.Split(strings.TrimSpace(string(bytes)), "\n")

	// It's not strictly needed to build all this up, but it may be useful later.
	var ret = make(map[string]*runescapeLevelMetadata)
	if len(data) < len(runescapeOldSchoolSkillNames) {
		return nil, fmt.Errorf("Invalid data")
	}

	for i, name := range runescapeOldSchoolSkillNames {
		md, err := newRunescapeLevelMetadata(name, player, data[i])
		if err != nil {
			return nil, err
		}

		ret[md.Skill] = md
	}

	if skill == "combat" {
		combat := getCombatLevel(
			ret["attack"].Level,
			ret["defence"].Level,
			ret["strength"].Level,
			ret["hitpoints"].Level,
			ret["ranged"].Level,
			ret["prayer"].Level,
			ret["magic"].Level,
		)
		return &runescapeLevelMetadata{
			Rank:   -1,
			Level:  combat,
			Exp:    -1,
			Player: player,
			Skill:  skill,
		}, nil
	}

	// Pull out the proper data
	md := ret[skill]
	if md == nil {
		return nil, fmt.Errorf("Unknown skill %q", skill)
	}

	return md, nil
}

func (p *runescapePlugin) levelCallback(b *seabird.Bot, m *irc.Message) {
	trailing := m.Trailing()
	go func() {
		data, err := p.getPlayerSkills(trailing)
		if err != nil {
			b.MentionReply(m, "%s", err)
			return
		}

		b.MentionReply(m, "%s has level %s %s", data.Player, utils.PrettifyNumber(data.Level), data.Skill)
	}()
}

func (p *runescapePlugin) expCallback(b *seabird.Bot, m *irc.Message) {
	trailing := m.Trailing()
	go func() {
		data, err := p.getPlayerSkills(trailing)
		if err != nil {
			b.MentionReply(m, "%s", err)
			return
		}

		b.MentionReply(m, "%s has %s experience in %s", data.Player, utils.PrettifySuffix(data.Exp), data.Skill)
	}()
}

func (p *runescapePlugin) rankCallback(b *seabird.Bot, m *irc.Message) {
	trailing := m.Trailing()
	go func() {
		data, err := p.getPlayerSkills(trailing)
		if err != nil {
			b.MentionReply(m, "%s", err)
			return
		}

		b.MentionReply(m, "%s has rank %s in %s", data.Player, utils.PrettifyNumber(data.Rank), data.Skill)
	}()
}
<|endoftext|>


<commit_before>// Package bugsnagrevel adds Bugsnag to revel.
// It lets you pass *revel.Controller into bugsnag.Notify(),
// and provides a Filter to catch errors.
package bugsnagrevel

import (
	"context"
	"net/http"
	"strings"

	"github.com/bugsnag/bugsnag-go"
	"github.com/revel/revel"
)

// FrameworkName is the name of the framework this middleware applies to
const FrameworkName string = "Revel"

var errorHandlingState = bugsnag.HandledState{
	SeverityReason:   bugsnag.SeverityReasonUnhandledMiddlewareError,
	OriginalSeverity: bugsnag.SeverityError,
	Unhandled:        true,
	Framework:        FrameworkName,
}

// Filter should be added to the filter chain just after the PanicFilter.
// It sends errors to Bugsnag automatically. Configuration is read out of
// conf/app.conf, you should set bugsnag.apikey, and can also set
// bugsnag.endpoints, bugsnag.releasestage, bugsnag.apptype, bugsnag.appversion,
// bugsnag.projectroot, bugsnag.projectpackages if needed.
func Filter(c *revel.Controller, fc []revel.Filter) {
	notifier := bugsnag.New()
	ctx := bugsnag.AttachRequestData(context.Background(), findProperHTTPRequest(c))
	// Record a session if auto capture sessions is enabled
	if notifier.Config.IsAutoCaptureSessions() {
		ctx = bugsnag.StartSession(ctx)
	}
	c.Args["context"] = ctx
	defer notifier.AutoNotify(c, ctx, errorHandlingState)
	fc[0](c, fc[1:])
}

// Add support to bugsnag for reading data out of *revel.Controllers
func middleware(event *bugsnag.Event, config *bugsnag.Configuration) error {
	for _, datum := range event.RawData {
		if controller, ok := datum.(*revel.Controller); ok {
			// make the request visible to the builtin HttpMiddleware
			event.Context = controller.Action
			event.MetaData.AddStruct("Session", controller.Session)
		}
	}

	return nil
}

func findProperHTTPRequest(c *revel.Controller) *http.Request {
	var req *http.Request
	rawReq := c.Request.In.GetRaw()

	// This *should* always be a *http.Request, but the revel team must have
	// made this an interface{} for a reason, and we might as well be defensive
	// about it
	switch rawReq.(type) {
	case (*http.Request):
		req = rawReq.(*http.Request) // Find the *proper* http request.
	}
	return req
}

type bugsnagRevelLogger struct{}

func (l *bugsnagRevelLogger) Printf(s string, params ...interface{}) {
	if strings.HasPrefix(s, "ERROR") {
		revel.AppLog.Errorf(s, params...)
	} else if strings.HasPrefix(s, "WARN") {
		revel.AppLog.Warnf(s, params...)
	} else {
		revel.AppLog.Infof(s, params...)
	}

}

func init() {
	// To ensure that users can disable the default panic handler (by calling
	// bugsnag.Configure before this function does) we must allow other
	// callbacks to execute before this function.
	order := 2
	revel.OnAppStart(func() {
		bugsnag.OnBeforeNotify(middleware)

		ip := revel.ImportPath
		c := revel.Config
		bugsnag.Configure(bugsnag.Configuration{
			APIKey:   c.StringDefault("bugsnag.apikey", ""),
			Endpoint: c.StringDefault("bugsnag.endpoint", ""),
			Endpoints: bugsnag.Endpoints{
				Notify:   c.StringDefault("bugsnag.endpoints.notify", ""),
				Sessions: c.StringDefault("bugsnag.endpoints.sessions", ""),
			},
			ReleaseStage:        c.StringDefault("bugsnag.releasestage", revel.RunMode),
			AppType:             c.StringDefault("bugsnag.apptype", FrameworkName),
			AppVersion:          c.StringDefault("bugsnag.appversion", ""),
			AutoCaptureSessions: c.BoolDefault("bugsnag.autocapturesessions", true),
			Hostname:            c.StringDefault("bugsnag.device.hostname", ""),
			NotifyReleaseStages: getCsvsOrDefault("bugsnag.notifyreleasestages", nil),
			ProjectPackages:     getCsvsOrDefault("bugsnag.projectpackages", []string{ip + "/app/*", ip + "/app"}),
			SourceRoot:          c.StringDefault("bugsnag.sourceroot", ""),
			ParamsFilters:       getCsvsOrDefault("bugsnag.paramsfilters", []string{"password", "secret", "authorization", "cookie"}),
			Logger:              new(bugsnagRevelLogger),
			Synchronous:         c.BoolDefault("bugsnag.synchronous", false),
		})
	}, order)
}

func getCsvsOrDefault(propertyKey string, d []string) []string {
	if propString, ok := revel.Config.String(propertyKey); ok {
		return strings.Split(propString, ",")
	}
	return d
}
<commit_msg>[chore] Simplify revel project packages<commit_after>// Package bugsnagrevel adds Bugsnag to revel.
// It lets you pass *revel.Controller into bugsnag.Notify(),
// and provides a Filter to catch errors.
package bugsnagrevel

import (
	"context"
	"net/http"
	"strings"

	"github.com/bugsnag/bugsnag-go"
	"github.com/revel/revel"
)

// FrameworkName is the name of the framework this middleware applies to
const FrameworkName string = "Revel"

var errorHandlingState = bugsnag.HandledState{
	SeverityReason:   bugsnag.SeverityReasonUnhandledMiddlewareError,
	OriginalSeverity: bugsnag.SeverityError,
	Unhandled:        true,
	Framework:        FrameworkName,
}

// Filter should be added to the filter chain just after the PanicFilter.
// It sends errors to Bugsnag automatically. Configuration is read out of
// conf/app.conf, you should set bugsnag.apikey, and can also set
// bugsnag.endpoints, bugsnag.releasestage, bugsnag.apptype, bugsnag.appversion,
// bugsnag.projectroot, bugsnag.projectpackages if needed.
func Filter(c *revel.Controller, fc []revel.Filter) {
	notifier := bugsnag.New()
	ctx := bugsnag.AttachRequestData(context.Background(), findProperHTTPRequest(c))
	// Record a session if auto capture sessions is enabled
	if notifier.Config.IsAutoCaptureSessions() {
		ctx = bugsnag.StartSession(ctx)
	}
	c.Args["context"] = ctx
	defer notifier.AutoNotify(c, ctx, errorHandlingState)
	fc[0](c, fc[1:])
}

// Add support to bugsnag for reading data out of *revel.Controllers
func middleware(event *bugsnag.Event, config *bugsnag.Configuration) error {
	for _, datum := range event.RawData {
		if controller, ok := datum.(*revel.Controller); ok {
			// make the request visible to the builtin HttpMiddleware
			event.Context = controller.Action
			event.MetaData.AddStruct("Session", controller.Session)
		}
	}

	return nil
}

func findProperHTTPRequest(c *revel.Controller) *http.Request {
	var req *http.Request
	rawReq := c.Request.In.GetRaw()

	// This *should* always be a *http.Request, but the revel team must have
	// made this an interface{} for a reason, and we might as well be defensive
	// about it
	switch rawReq.(type) {
	case (*http.Request):
		req = rawReq.(*http.Request) // Find the *proper* http request.
	}
	return req
}

type bugsnagRevelLogger struct{}

func (l *bugsnagRevelLogger) Printf(s string, params ...interface{}) {
	if strings.HasPrefix(s, "ERROR") {
		revel.AppLog.Errorf(s, params...)
	} else if strings.HasPrefix(s, "WARN") {
		revel.AppLog.Warnf(s, params...)
	} else {
		revel.AppLog.Infof(s, params...)
	}

}

func init() {
	// To ensure that users can disable the default panic handler (by calling
	// bugsnag.Configure before this function does) we must allow other
	// callbacks to execute before this function.
	order := 2
	revel.OnAppStart(func() {
		bugsnag.OnBeforeNotify(middleware)

		c := revel.Config
		bugsnag.Configure(bugsnag.Configuration{
			APIKey:   c.StringDefault("bugsnag.apikey", ""),
			Endpoint: c.StringDefault("bugsnag.endpoint", ""),
			Endpoints: bugsnag.Endpoints{
				Notify:   c.StringDefault("bugsnag.endpoints.notify", ""),
				Sessions: c.StringDefault("bugsnag.endpoints.sessions", ""),
			},
			ReleaseStage:        c.StringDefault("bugsnag.releasestage", revel.RunMode),
			AppType:             c.StringDefault("bugsnag.apptype", FrameworkName),
			AppVersion:          c.StringDefault("bugsnag.appversion", ""),
			AutoCaptureSessions: c.BoolDefault("bugsnag.autocapturesessions", true),
			Hostname:            c.StringDefault("bugsnag.device.hostname", ""),
			NotifyReleaseStages: getCsvsOrDefault("bugsnag.notifyreleasestages", nil),
			ProjectPackages:     getCsvsOrDefault("bugsnag.projectpackages", []string{revel.ImportPath + "/app/**"}),
			SourceRoot:          c.StringDefault("bugsnag.sourceroot", ""),
			ParamsFilters:       getCsvsOrDefault("bugsnag.paramsfilters", []string{"password", "secret", "authorization", "cookie"}),
			Logger:              new(bugsnagRevelLogger),
			Synchronous:         c.BoolDefault("bugsnag.synchronous", false),
		})
	}, order)
}

func getCsvsOrDefault(propertyKey string, d []string) []string {
	if propString, ok := revel.Config.String(propertyKey); ok {
		return strings.Split(propString, ",")
	}
	return d
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.

package main

import (
	"fmt"
	"net"

	"github.com/miekg/dns"
)

// ServeDNSForward forwards a request to a nameservers and returns the response.
func (s *server) ServeDNSForward(w dns.ResponseWriter, req *dns.Msg) {
	StatsForwardCount.Inc(1)
	if len(s.config.Nameservers) == 0 || dns.CountLabel(req.Question[0].Name) <= s.config.Ndots {
		s.config.log.Infof("no nameservers defined or name too short, can not forward")
		m := new(dns.Msg)
		m.SetReply(req)
		m.SetRcode(req, dns.RcodeServerFailure)
		m.Authoritative = false     // no matter what set to false
		m.RecursionAvailable = true // and this is still true
		w.WriteMsg(m)
		return
	}
	network := "udp"
	if _, ok := w.RemoteAddr().(*net.TCPAddr); ok {
		network = "tcp"
	}

	// Try again, when it fails?
	c := &dns.Client{Net: network, ReadTimeout: 2 * s.config.ReadTimeout, WriteTimeout: 2 * s.config.ReadTimeout}

	// Use request Id for "random" nameserver selection.
	nsid := int(req.Id) % len(s.config.Nameservers)
	try := 0
Redo:
	r, _, err := c.Exchange(req, s.config.Nameservers[nsid])
	if err == nil {
		r.Compress = true
		w.WriteMsg(r)
		return
	}
	// Seen an error, this can only mean, "server not reached", try again
	// but only if we have not exausted our nameservers.
	if try < len(s.config.Nameservers) {
		try++
		nsid = (nsid + 1) % len(s.config.Nameservers)
		goto Redo
	}

	s.config.log.Errorf("failure to forward request %q", err)
	m := new(dns.Msg)
	m.SetReply(req)
	m.SetRcode(req, dns.RcodeServerFailure)
	w.WriteMsg(m)
}

// ServeDNSReverse is the handler for DNS requests for the reverse zone. If nothing is found
// locally the request is forwarded to the forwarder for resolution.
func (s *server) ServeDNSReverse(w dns.ResponseWriter, req *dns.Msg) {
	m := new(dns.Msg)
	m.SetReply(req)
	m.Compress = true
	m.Authoritative = false // Set to false, because I don't know what to do wrt DNSSEC.
	m.RecursionAvailable = true
	var err error
	if m.Answer, err = s.PTRRecords(req.Question[0]); err == nil {
		// TODO(miek): Reverse DNSSEC. We should sign this, but requires a key....and more
		// Probably not worth the hassle?
		if err := w.WriteMsg(m); err != nil {
			s.config.log.Errorf("failure to return reply %q", err)
		}
	}
	// Always forward if not found locally.
	s.ServeDNSForward(w, req)
}

// Lookup looks up name,type using the recursive nameserver defines
// in the server's config. If none defined it returns an error
func (s *server) Lookup(n string, t, dnssec uint16) (*dns.Msg, error) {
	StatsLookupCount.Inc(1)
	if len(s.config.Nameservers) == 0 {
		return nil, fmt.Errorf("no nameservers configured can not lookup name")
	}
	if dns.CountLabel(n) <= s.config.Ndots {
		return nil, fmt.Errorf("name has fewer than three labels")
	}
	m := new(dns.Msg)
	m.SetQuestion(n, t)
	if dnssec > 0 {
		m.SetEdns0(dnssec, true)
	}

	c := &dns.Client{Net: "udp", ReadTimeout: 2 * s.config.ReadTimeout, WriteTimeout: 2 * s.config.ReadTimeout}
	nsid := int(m.Id) % len(s.config.Nameservers)
	try := 0
Redo:
	r, _, err := c.Exchange(m, s.config.Nameservers[nsid])
	if err == nil {
		if r.Rcode != dns.RcodeSuccess {
			return nil, fmt.Errorf("rcode is not equal to success")
		}
		// Reset TTLs to rcache TTL to make some of the other code
		// and the tests not care about TTLs
		for _, rr := range r.Answer {
			rr.Header().Ttl = uint32(s.config.RCacheTtl)
		}
		for _, rr := range r.Extra {
			rr.Header().Ttl = uint32(s.config.RCacheTtl)
		}
		return r, nil
	}
	// Seen an error, this can only mean, "server not reached", try again
	// but only if we have not exausted our nameservers.
	if try < len(s.config.Nameservers) {
		try++
		nsid = (nsid + 1) % len(s.config.Nameservers)
		goto Redo
	}
	return nil, fmt.Errorf("failure to lookup name")
}
<commit_msg>Use Ndots correctly<commit_after>// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.

package main

import (
	"fmt"
	"net"

	"github.com/miekg/dns"
)

// ServeDNSForward forwards a request to a nameservers and returns the response.
func (s *server) ServeDNSForward(w dns.ResponseWriter, req *dns.Msg) {
	StatsForwardCount.Inc(1)
	if len(s.config.Nameservers) == 0 || dns.CountLabel(req.Question[0].Name) < s.config.Ndots {
		s.config.log.Infof("no nameservers defined or name too short, can not forward")
		m := new(dns.Msg)
		m.SetReply(req)
		m.SetRcode(req, dns.RcodeServerFailure)
		m.Authoritative = false     // no matter what set to false
		m.RecursionAvailable = true // and this is still true
		w.WriteMsg(m)
		return
	}
	network := "udp"
	if _, ok := w.RemoteAddr().(*net.TCPAddr); ok {
		network = "tcp"
	}

	// Try again, when it fails?
	c := &dns.Client{Net: network, ReadTimeout: 2 * s.config.ReadTimeout, WriteTimeout: 2 * s.config.ReadTimeout}

	// Use request Id for "random" nameserver selection.
	nsid := int(req.Id) % len(s.config.Nameservers)
	try := 0
Redo:
	r, _, err := c.Exchange(req, s.config.Nameservers[nsid])
	if err == nil {
		r.Compress = true
		w.WriteMsg(r)
		return
	}
	// Seen an error, this can only mean, "server not reached", try again
	// but only if we have not exausted our nameservers.
	if try < len(s.config.Nameservers) {
		try++
		nsid = (nsid + 1) % len(s.config.Nameservers)
		goto Redo
	}

	s.config.log.Errorf("failure to forward request %q", err)
	m := new(dns.Msg)
	m.SetReply(req)
	m.SetRcode(req, dns.RcodeServerFailure)
	w.WriteMsg(m)
}

// ServeDNSReverse is the handler for DNS requests for the reverse zone. If nothing is found
// locally the request is forwarded to the forwarder for resolution.
func (s *server) ServeDNSReverse(w dns.ResponseWriter, req *dns.Msg) {
	m := new(dns.Msg)
	m.SetReply(req)
	m.Compress = true
	m.Authoritative = false // Set to false, because I don't know what to do wrt DNSSEC.
	m.RecursionAvailable = true
	var err error
	if m.Answer, err = s.PTRRecords(req.Question[0]); err == nil {
		// TODO(miek): Reverse DNSSEC. We should sign this, but requires a key....and more
		// Probably not worth the hassle?
		if err := w.WriteMsg(m); err != nil {
			s.config.log.Errorf("failure to return reply %q", err)
		}
	}
	// Always forward if not found locally.
	s.ServeDNSForward(w, req)
}

// Lookup looks up name,type using the recursive nameserver defines
// in the server's config. If none defined it returns an error
func (s *server) Lookup(n string, t, dnssec uint16) (*dns.Msg, error) {
	StatsLookupCount.Inc(1)
	if len(s.config.Nameservers) == 0 {
		return nil, fmt.Errorf("no nameservers configured can not lookup name")
	}
	if dns.CountLabel(n) < s.config.Ndots {
		return nil, fmt.Errorf("name has fewer than three labels")
	}
	m := new(dns.Msg)
	m.SetQuestion(n, t)
	if dnssec > 0 {
		m.SetEdns0(dnssec, true)
	}

	c := &dns.Client{Net: "udp", ReadTimeout: 2 * s.config.ReadTimeout, WriteTimeout: 2 * s.config.ReadTimeout}
	nsid := int(m.Id) % len(s.config.Nameservers)
	try := 0
Redo:
	r, _, err := c.Exchange(m, s.config.Nameservers[nsid])
	if err == nil {
		if r.Rcode != dns.RcodeSuccess {
			return nil, fmt.Errorf("rcode is not equal to success")
		}
		// Reset TTLs to rcache TTL to make some of the other code
		// and the tests not care about TTLs
		for _, rr := range r.Answer {
			rr.Header().Ttl = uint32(s.config.RCacheTtl)
		}
		for _, rr := range r.Extra {
			rr.Header().Ttl = uint32(s.config.RCacheTtl)
		}
		return r, nil
	}
	// Seen an error, this can only mean, "server not reached", try again
	// but only if we have not exausted our nameservers.
	if try < len(s.config.Nameservers) {
		try++
		nsid = (nsid + 1) % len(s.config.Nameservers)
		goto Redo
	}
	return nil, fmt.Errorf("failure to lookup name")
}
<|endoftext|>


<commit_before>package twitter

import (
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/dghubble/go-twitter/twitter"
	"github.com/dghubble/oauth1"
	"github.com/jirwin/quadlek/quadlek"
)

func load(consumerKey, consumerSecret, accessToken, accessSecret string, filter map[string]string) func(bot *quadlek.Bot, store *quadlek.Store) error {

	return func(bot *quadlek.Bot, store *quadlek.Store) error {
		go func() {
			config := oauth1.NewConfig(consumerKey, consumerSecret)
			token := oauth1.NewToken(accessToken, accessSecret)
			httpClient := config.Client(oauth1.NoContext, token)
			client := twitter.NewClient(httpClient)

			followFilters := []string{}
			for follow, _ := range filter {
				followFilters = append(followFilters, follow)
			}

			filterParams := &twitter.StreamFilterParams{
				Follow:        followFilters,
				StallWarnings: twitter.Bool(true),
			}

			stream, err := client.Streams.Filter(filterParams)
			if err != nil {
				log.WithField("err", err).Error("Error streaming tweets.")
				return
			}

			for {
				select {
				case msg := <-stream.Messages:
					switch m := msg.(type) {
					case *twitter.Tweet:
						if channel, ok := filter[m.User.IDStr]; ok {
							twitterUrl := fmt.Sprintf("https://twitter.com/%s/status/%s", m.User.ScreenName, m.IDStr)
							chanId, err := bot.GetChannelId(channel)
							if err != nil {
								log.WithField("err", err).Error("unable to find channel.")
								continue
							}
							bot.Say(chanId, twitterUrl)
						}
					}
				}
			}
		}()

		return nil
	}
}

func Register(consumerKey, consumerSecret, accessToken, accessSecret string, filter map[string]string) quadlek.Plugin {
	return quadlek.MakePlugin(
		"quadlek-twitter",
		nil,
		nil,
		nil,
		nil,
		load(consumerKey, consumerSecret, accessToken, accessSecret, filter),
	)
}
<commit_msg>Limit double tweet posts<commit_after>package twitter

import (
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/dghubble/go-twitter/twitter"
	"github.com/dghubble/oauth1"
	"github.com/jirwin/quadlek/quadlek"
)

func load(consumerKey, consumerSecret, accessToken, accessSecret string, filter map[string]string) func(bot *quadlek.Bot, store *quadlek.Store) error {

	return func(bot *quadlek.Bot, store *quadlek.Store) error {
		go func() {
			config := oauth1.NewConfig(consumerKey, consumerSecret)
			token := oauth1.NewToken(accessToken, accessSecret)
			httpClient := config.Client(oauth1.NoContext, token)
			client := twitter.NewClient(httpClient)

			followFilters := []string{}
			for follow, _ := range filter {
				followFilters = append(followFilters, follow)
			}

			filterParams := &twitter.StreamFilterParams{
				Follow:        followFilters,
				StallWarnings: twitter.Bool(true),
			}

			stream, err := client.Streams.Filter(filterParams)
			if err != nil {
				log.WithField("err", err).Error("Error streaming tweets.")
				return
			}

			for {
				select {
				case msg := <-stream.Messages:
					switch m := msg.(type) {
					case *twitter.Tweet:
						if channel, ok := filter[m.User.IDStr]; ok {
							if filter[m.InReplyToUserIDStr] not channel {
								twitterUrl := fmt.Sprintf("https://twitter.com/%s/status/%s", m.User.ScreenName, m.IDStr)
								chanId, err := bot.GetChannelId(channel)
								if err != nil {
									log.WithField("err", err).Error("unable to find channel.")
									continue
								}
								bot.Say(chanId, twitterUrl)
							}
						}
					}
				}
			}
		}()

		return nil
	}
}

func Register(consumerKey, consumerSecret, accessToken, accessSecret string, filter map[string]string) quadlek.Plugin {
	return quadlek.MakePlugin(
		"quadlek-twitter",
		nil,
		nil,
		nil,
		nil,
		load(consumerKey, consumerSecret, accessToken, accessSecret, filter),
	)
}
<|endoftext|>


<commit_before>package server

import (
    "github.com/itsankoff/gotcha/common"
    "log"
    "encoding/json"
)

type Control struct {
    input           chan *common.Message
    groups          []*Group
    outputStore     *OutputStore
}

func NewControl(input chan *common.Message,
                outputStore *OutputStore) *Control {
    c := &Control{
        input: input,
        outputStore: outputStore,
    }

    go c.listen()
    return c
}

func (c Control) listen() {
    for {
        select {
        case msg := <-c.input:
            log.Println("Control received", msg)

            valid := c.validate(msg)
            if valid {
                var payload map[string]interface{}
                err := json.Unmarshal(msg.Binary(), &payload)
                if err == nil {
                    cmd := msg.Cmd()
                    switch(cmd) {
                    case "register":
                    case "auth":
                    case "list_contacts":
                    case "add_contact":
                    case "remove_contact":
                    case "create_group":
                        groupId := c.CreateGroup()
                        c.AddToGroup(groupId, msg.From())
                    case "add_to_group":
                        groupId := payload["group_id"]
                        userId := payload["user_id"]
                        c.AddToGroup(groupId.(string), userId.(string))
                    case "remove_from_group":
                        groupId := payload["group_id"].(string)
                        userId := payload["user_id"].(string)
                        c.RemoveFromGroup(groupId, userId)
                    case "delete_group":
                        groupId := payload["group_id"].(string)
                        c.DeleteGroup(groupId)
                    case "list_groups":
                    case "join_group":
                    case "leave_group":
                    default:
                        log.Println("Unknown control command", cmd)
                    }
                } else {
                    log.Println("Failed to decode control message payload", msg)
                }
            } else {
                log.Println("Invalid control message", msg)
            }
        }
    }
}

func (c Control) validate(msg *common.Message) bool {
    return true
}

func (c Control) findGroup(groupId string) *Group {
    for _, g := range c.groups {
        if g.Id == groupId {
            return g
        }
    }

    return nil
}

func (c Control) RegisterUser(user string, password string) *common.User {
    return nil
}

func (c Control) AuthUser(user string, password string) bool {
    return false
}

func (c Control) ListContacts(user string) []*common.User {
    return nil
}

func (c Control) AddContact(user *common.User, contact *common.User) bool {
    return false
}

func (c Control) RemoveContact(user *common.User, contact *common.User) bool {
    return false
}

func (c *Control) CreateGroup() string {
    group := NewGroup()
    c.groups = append(c.groups, group)
    c.outputStore.AddOutput(group.Id, group.Out)
    return group.Id
}

func (c Control) AddToGroup(groupId string, userId string) bool {
    group := c.findGroup(groupId)
    if group == nil {
        log.Println("Failed to add user. No group with id", groupId, userId)
        return false
    }

    userOutput := c.outputStore.GetOutput(userId)
    if userOutput == nil {
        log.Println("Failed to add user to group. No user output",
                    groupId, userId)
        return false
    }

    return group.AddOutput(userId, userOutput)
}

func (c Control) RemoveFromGroup(user string, groupId string) bool {
    group := c.findGroup(groupId)
    if group == nil {
        log.Println("Failed to remove user. No group with id", groupId, user)
        return false
    }

    return group.RemoveOutput(user)
}

func (c *Control) DeleteGroup(groupId string) bool {
    for i, group:= range c.groups {
        if group.Id == groupId {
            c.groups = append(c.groups[:i], c.groups[i+1:]...)
            c.outputStore.RemoveOutput(group.Id)
            close(group.Out)
            return true
        }
    }

    return false
}

func (c Control) ListGroups(user *common.User) *[]string {
    groupIds := []string{}
    for _, g := range c.groups {
        groupIds = append(groupIds, g.Id)
    }

    return &groupIds
}
<commit_msg>Fix parsing data for control messages<commit_after>package server

import (
    "github.com/itsankoff/gotcha/common"
    "log"
    "encoding/json"
)

type Control struct {
    input           chan *common.Message
    groups          []*Group
    outputStore     *OutputStore
}

func NewControl(input chan *common.Message,
                outputStore *OutputStore) *Control {
    c := &Control{
        input: input,
        outputStore: outputStore,
    }

    go c.listen()
    return c
}

func (c Control) listen() {
    for {
        select {
        case msg := <-c.input:
            log.Println("Control received", msg)

            valid := c.validate(msg)
            if valid {
                var payload map[string]interface{}
                err := json.Unmarshal([]byte(msg.String()), &payload)
                if err == nil {
                    cmd := msg.Cmd()
                    switch(cmd) {
                    case "register":
                    case "auth":
                    case "list_contacts":
                    case "add_contact":
                    case "remove_contact":
                    case "create_group":
                        groupId := c.CreateGroup()
                        c.AddToGroup(groupId, msg.From())
                    case "add_to_group":
                        groupId := payload["group_id"]
                        userId := payload["user_id"]
                        c.AddToGroup(groupId.(string), userId.(string))
                    case "remove_from_group":
                        groupId := payload["group_id"].(string)
                        userId := payload["user_id"].(string)
                        c.RemoveFromGroup(groupId, userId)
                    case "delete_group":
                        groupId := payload["group_id"].(string)
                        c.DeleteGroup(groupId)
                    case "list_groups":
                    case "join_group":
                    case "leave_group":
                    default:
                        log.Println("Unknown control command", cmd)
                    }
                } else {
                    log.Println("Failed to decode control message payload", msg)
                }
            } else {
                log.Println("Invalid control message", msg)
            }
        }
    }
}

func (c Control) validate(msg *common.Message) bool {
    return true
}

func (c Control) findGroup(groupId string) *Group {
    for _, g := range c.groups {
        if g.Id == groupId {
            return g
        }
    }

    return nil
}

func (c Control) RegisterUser(user string, password string) *common.User {
    return nil
}

func (c Control) AuthUser(user string, password string) bool {
    return false
}

func (c Control) ListContacts(user string) []*common.User {
    return nil
}

func (c Control) AddContact(user *common.User, contact *common.User) bool {
    return false
}

func (c Control) RemoveContact(user *common.User, contact *common.User) bool {
    return false
}

func (c *Control) CreateGroup() string {
    group := NewGroup()
    c.groups = append(c.groups, group)
    c.outputStore.AddOutput(group.Id, group.Out)
    return group.Id
}

func (c Control) AddToGroup(groupId string, userId string) bool {
    group := c.findGroup(groupId)
    if group == nil {
        log.Println("Failed to add user. No group with id", groupId, userId)
        return false
    }

    userOutput := c.outputStore.GetOutput(userId)
    if userOutput == nil {
        log.Println("Failed to add user to group. No user output",
                    groupId, userId)
        return false
    }

    return group.AddOutput(userId, userOutput)
}

func (c Control) RemoveFromGroup(user string, groupId string) bool {
    group := c.findGroup(groupId)
    if group == nil {
        log.Println("Failed to remove user. No group with id", groupId, user)
        return false
    }

    return group.RemoveOutput(user)
}

func (c *Control) DeleteGroup(groupId string) bool {
    for i, group:= range c.groups {
        if group.Id == groupId {
            c.groups = append(c.groups[:i], c.groups[i+1:]...)
            c.outputStore.RemoveOutput(group.Id)
            close(group.Out)
            return true
        }
    }

    return false
}

func (c Control) ListGroups(user *common.User) *[]string {
    groupIds := []string{}
    for _, g := range c.groups {
        groupIds = append(groupIds, g.Id)
    }

    return &groupIds
}
<|endoftext|>