<commit_before>// +build linux

package libcontainer

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"strings"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runc/libcontainer/system"
	"github.com/opencontainers/runc/libcontainer/user"
	"github.com/opencontainers/runc/libcontainer/utils"
	"github.com/vishvananda/netlink"
)

type initType string

const (
	initSetns    initType = "setns"
	initStandard initType = "standard"
)

type pid struct {
	Pid int `json:"pid"`
}

// network is an internal struct used to setup container networks.
type network struct {
	configs.Network

	// TempVethPeerName is a unique temporary veth peer name that was placed into
	// the container's namespace.
	TempVethPeerName string `json:"temp_veth_peer_name"`
}

// initConfig is used for transferring parameters from Exec() to Init()
type initConfig struct {
	Args             []string         `json:"args"`
	Env              []string         `json:"env"`
	Cwd              string           `json:"cwd"`
	Capabilities     []string         `json:"capabilities"`
	ProcessLabel     string           `json:"process_label"`
	AppArmorProfile  string           `json:"apparmor_profile"`
	NoNewPrivileges  bool             `json:"no_new_privileges"`
	User             string           `json:"user"`
	AdditionalGroups []string         `json:"additional_groups"`
	Config           *configs.Config  `json:"config"`
	Console          string           `json:"console"`
	Networks         []*network       `json:"network"`
	PassedFilesCount int              `json:"passed_files_count"`
	ContainerId      string           `json:"containerid"`
	Rlimits          []configs.Rlimit `json:"rlimits"`
	ExecFifoPath     string           `json:"start_pipe_path"`
}

type initer interface {
	Init() error
}

func newContainerInit(t initType, pipe *os.File, stateDirFD int) (initer, error) {
	var config *initConfig
	if err := json.NewDecoder(pipe).Decode(&config); err != nil {
		return nil, err
	}
	if err := populateProcessEnvironment(config.Env); err != nil {
		return nil, err
	}
	switch t {
	case initSetns:
		return &linuxSetnsInit{
			config: config,
		}, nil
	case initStandard:
		return &linuxStandardInit{
			pipe:       pipe,
			parentPid:  syscall.Getppid(),
			config:     config,
			stateDirFD: stateDirFD,
		}, nil
	}
	return nil, fmt.Errorf("unknown init type %q", t)
}

// populateProcessEnvironment loads the provided environment variables into the
// current processes's environment.
func populateProcessEnvironment(env []string) error {
	for _, pair := range env {
		p := strings.SplitN(pair, "=", 2)
		if len(p) < 2 {
			return fmt.Errorf("invalid environment '%v'", pair)
		}
		if err := os.Setenv(p[0], p[1]); err != nil {
			return err
		}
	}
	return nil
}

// finalizeNamespace drops the caps, sets the correct user
// and working dir, and closes any leaked file descriptors
// before executing the command inside the namespace
func finalizeNamespace(config *initConfig) error {
	// Ensure that all unwanted fds we may have accidentally
	// inherited are marked close-on-exec so they stay out of the
	// container
	if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
		return err
	}

	capabilities := config.Config.Capabilities
	if config.Capabilities != nil {
		capabilities = config.Capabilities
	}
	w, err := newCapWhitelist(capabilities)
	if err != nil {
		return err
	}
	// drop capabilities in bounding set before changing user
	if err := w.dropBoundingSet(); err != nil {
		return err
	}
	// preserve existing capabilities while we change users
	if err := system.SetKeepCaps(); err != nil {
		return err
	}
	if err := setupUser(config); err != nil {
		return err
	}
	if err := system.ClearKeepCaps(); err != nil {
		return err
	}
	// drop all other capabilities
	if err := w.drop(); err != nil {
		return err
	}
	if config.Cwd != "" {
		if err := syscall.Chdir(config.Cwd); err != nil {
			return err
		}
	}
	return nil
}

// syncParentReady sends to the given pipe a JSON payload which indicates that
// the init is ready to Exec the child process. It then waits for the parent to
// indicate that it is cleared to Exec.
func syncParentReady(pipe io.ReadWriter) error {
	// Tell parent.
	if err := utils.WriteJSON(pipe, syncT{procReady}); err != nil {
		return err
	}
	// Wait for parent to give the all-clear.
	var procSync syncT
	if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
		if err == io.EOF {
			return fmt.Errorf("parent closed synchronisation channel")
		}
		if procSync.Type != procRun {
			return fmt.Errorf("invalid synchronisation flag from parent")
		}
	}
	return nil
}

// syncParentHooks sends to the given pipe a JSON payload which indicates that
// the parent should execute pre-start hooks. It then waits for the parent to
// indicate that it is cleared to resume.
func syncParentHooks(pipe io.ReadWriter) error {
	// Tell parent.
	if err := utils.WriteJSON(pipe, syncT{procHooks}); err != nil {
		return err
	}
	// Wait for parent to give the all-clear.
	var procSync syncT
	if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
		if err == io.EOF {
			return fmt.Errorf("parent closed synchronisation channel")
		}
		if procSync.Type != procResume {
			return fmt.Errorf("invalid synchronisation flag from parent")
		}
	}
	return nil
}

// setupUser changes the groups, gid, and uid for the user inside the container
func setupUser(config *initConfig) error {
	// Set up defaults.
	defaultExecUser := user.ExecUser{
		Uid:  syscall.Getuid(),
		Gid:  syscall.Getgid(),
		Home: "/",
	}
	passwdPath, err := user.GetPasswdPath()
	if err != nil {
		return err
	}
	groupPath, err := user.GetGroupPath()
	if err != nil {
		return err
	}
	execUser, err := user.GetExecUserPath(config.User, &defaultExecUser, passwdPath, groupPath)
	if err != nil {
		return err
	}

	var addGroups []int
	if len(config.AdditionalGroups) > 0 {
		addGroups, err = user.GetAdditionalGroupsPath(config.AdditionalGroups, groupPath)
		if err != nil {
			return err
		}
	}
	// before we change to the container's user make sure that the processes STDIO
	// is correctly owned by the user that we are switching to.
	if err := fixStdioPermissions(execUser); err != nil {
		return err
	}
	suppGroups := append(execUser.Sgids, addGroups...)
	if err := syscall.Setgroups(suppGroups); err != nil {
		return err
	}

	if err := system.Setgid(execUser.Gid); err != nil {
		return err
	}
	if err := system.Setuid(execUser.Uid); err != nil {
		return err
	}
	// if we didn't get HOME already, set it based on the user's HOME
	if envHome := os.Getenv("HOME"); envHome == "" {
		if err := os.Setenv("HOME", execUser.Home); err != nil {
			return err
		}
	}
	return nil
}

// fixStdioPermissions fixes the permissions of PID 1's STDIO within the container to the specified user.
// The ownership needs to match because it is created outside of the container and needs to be
// localized.
func fixStdioPermissions(u *user.ExecUser) error {
	var null syscall.Stat_t
	if err := syscall.Stat("/dev/null", &null); err != nil {
		return err
	}
	for _, fd := range []uintptr{
		os.Stdin.Fd(),
		os.Stderr.Fd(),
		os.Stdout.Fd(),
	} {
		var s syscall.Stat_t
		if err := syscall.Fstat(int(fd), &s); err != nil {
			return err
		}
		// skip chown of /dev/null if it was used as one of the STDIO fds.
		if s.Rdev == null.Rdev {
			continue
		}
		if err := syscall.Fchown(int(fd), u.Uid, u.Gid); err != nil {
			return err
		}
	}
	return nil
}

// setupNetwork sets up and initializes any network interface inside the container.
func setupNetwork(config *initConfig) error {
	for _, config := range config.Networks {
		strategy, err := getStrategy(config.Type)
		if err != nil {
			return err
		}
		if err := strategy.initialize(config); err != nil {
			return err
		}
	}
	return nil
}

func setupRoute(config *configs.Config) error {
	for _, config := range config.Routes {
		_, dst, err := net.ParseCIDR(config.Destination)
		if err != nil {
			return err
		}
		src := net.ParseIP(config.Source)
		if src == nil {
			return fmt.Errorf("Invalid source for route: %s", config.Source)
		}
		gw := net.ParseIP(config.Gateway)
		if gw == nil {
			return fmt.Errorf("Invalid gateway for route: %s", config.Gateway)
		}
		l, err := netlink.LinkByName(config.InterfaceName)
		if err != nil {
			return err
		}
		route := &netlink.Route{
			Scope:     netlink.SCOPE_UNIVERSE,
			Dst:       dst,
			Src:       src,
			Gw:        gw,
			LinkIndex: l.Attrs().Index,
		}
		if err := netlink.RouteAdd(route); err != nil {
			return err
		}
	}
	return nil
}

func setupRlimits(limits []configs.Rlimit, pid int) error {
	for _, rlimit := range limits {
		if err := system.Prlimit(pid, rlimit.Type, syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}); err != nil {
			return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err)
		}
	}
	return nil
}

func setOomScoreAdj(oomScoreAdj int, pid int) error {
	path := fmt.Sprintf("/proc/%d/oom_score_adj", pid)

	return ioutil.WriteFile(path, []byte(strconv.Itoa(oomScoreAdj)), 0600)
}

// killCgroupProcesses freezes then iterates over all the processes inside the
// manager's cgroups sending a SIGKILL to each process then waiting for them to
// exit.
func killCgroupProcesses(m cgroups.Manager) error {
	var procs []*os.Process
	if err := m.Freeze(configs.Frozen); err != nil {
		logrus.Warn(err)
	}
	pids, err := m.GetAllPids()
	if err != nil {
		m.Freeze(configs.Thawed)
		return err
	}
	for _, pid := range pids {
		p, err := os.FindProcess(pid)
		if err != nil {
			logrus.Warn(err)
			continue
		}
		procs = append(procs, p)
		if err := p.Kill(); err != nil {
			logrus.Warn(err)
		}
	}
	if err := m.Freeze(configs.Thawed); err != nil {
		logrus.Warn(err)
	}
	for _, p := range procs {
		if _, err := p.Wait(); err != nil {
			logrus.Warn(err)
		}
	}
	return nil
}
<commit_msg>Fix the err info of chdir(cwd) failure<commit_after>// +build linux

package libcontainer

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"strings"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runc/libcontainer/system"
	"github.com/opencontainers/runc/libcontainer/user"
	"github.com/opencontainers/runc/libcontainer/utils"
	"github.com/vishvananda/netlink"
)

type initType string

const (
	initSetns    initType = "setns"
	initStandard initType = "standard"
)

type pid struct {
	Pid int `json:"pid"`
}

// network is an internal struct used to setup container networks.
type network struct {
	configs.Network

	// TempVethPeerName is a unique temporary veth peer name that was placed into
	// the container's namespace.
	TempVethPeerName string `json:"temp_veth_peer_name"`
}

// initConfig is used for transferring parameters from Exec() to Init()
type initConfig struct {
	Args             []string         `json:"args"`
	Env              []string         `json:"env"`
	Cwd              string           `json:"cwd"`
	Capabilities     []string         `json:"capabilities"`
	ProcessLabel     string           `json:"process_label"`
	AppArmorProfile  string           `json:"apparmor_profile"`
	NoNewPrivileges  bool             `json:"no_new_privileges"`
	User             string           `json:"user"`
	AdditionalGroups []string         `json:"additional_groups"`
	Config           *configs.Config  `json:"config"`
	Console          string           `json:"console"`
	Networks         []*network       `json:"network"`
	PassedFilesCount int              `json:"passed_files_count"`
	ContainerId      string           `json:"containerid"`
	Rlimits          []configs.Rlimit `json:"rlimits"`
	ExecFifoPath     string           `json:"start_pipe_path"`
}

type initer interface {
	Init() error
}

func newContainerInit(t initType, pipe *os.File, stateDirFD int) (initer, error) {
	var config *initConfig
	if err := json.NewDecoder(pipe).Decode(&config); err != nil {
		return nil, err
	}
	if err := populateProcessEnvironment(config.Env); err != nil {
		return nil, err
	}
	switch t {
	case initSetns:
		return &linuxSetnsInit{
			config: config,
		}, nil
	case initStandard:
		return &linuxStandardInit{
			pipe:       pipe,
			parentPid:  syscall.Getppid(),
			config:     config,
			stateDirFD: stateDirFD,
		}, nil
	}
	return nil, fmt.Errorf("unknown init type %q", t)
}

// populateProcessEnvironment loads the provided environment variables into the
// current processes's environment.
func populateProcessEnvironment(env []string) error {
	for _, pair := range env {
		p := strings.SplitN(pair, "=", 2)
		if len(p) < 2 {
			return fmt.Errorf("invalid environment '%v'", pair)
		}
		if err := os.Setenv(p[0], p[1]); err != nil {
			return err
		}
	}
	return nil
}

// finalizeNamespace drops the caps, sets the correct user
// and working dir, and closes any leaked file descriptors
// before executing the command inside the namespace
func finalizeNamespace(config *initConfig) error {
	// Ensure that all unwanted fds we may have accidentally
	// inherited are marked close-on-exec so they stay out of the
	// container
	if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
		return err
	}

	capabilities := config.Config.Capabilities
	if config.Capabilities != nil {
		capabilities = config.Capabilities
	}
	w, err := newCapWhitelist(capabilities)
	if err != nil {
		return err
	}
	// drop capabilities in bounding set before changing user
	if err := w.dropBoundingSet(); err != nil {
		return err
	}
	// preserve existing capabilities while we change users
	if err := system.SetKeepCaps(); err != nil {
		return err
	}
	if err := setupUser(config); err != nil {
		return err
	}
	if err := system.ClearKeepCaps(); err != nil {
		return err
	}
	// drop all other capabilities
	if err := w.drop(); err != nil {
		return err
	}
	if config.Cwd != "" {
		if err := syscall.Chdir(config.Cwd); err != nil {
			return fmt.Errorf("chdir to cwd (%q) set in config.json failed: %v", config.Cwd, err)
		}
	}
	return nil
}

// syncParentReady sends to the given pipe a JSON payload which indicates that
// the init is ready to Exec the child process. It then waits for the parent to
// indicate that it is cleared to Exec.
func syncParentReady(pipe io.ReadWriter) error {
	// Tell parent.
	if err := utils.WriteJSON(pipe, syncT{procReady}); err != nil {
		return err
	}
	// Wait for parent to give the all-clear.
	var procSync syncT
	if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
		if err == io.EOF {
			return fmt.Errorf("parent closed synchronisation channel")
		}
		if procSync.Type != procRun {
			return fmt.Errorf("invalid synchronisation flag from parent")
		}
	}
	return nil
}

// syncParentHooks sends to the given pipe a JSON payload which indicates that
// the parent should execute pre-start hooks. It then waits for the parent to
// indicate that it is cleared to resume.
func syncParentHooks(pipe io.ReadWriter) error {
	// Tell parent.
	if err := utils.WriteJSON(pipe, syncT{procHooks}); err != nil {
		return err
	}
	// Wait for parent to give the all-clear.
	var procSync syncT
	if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
		if err == io.EOF {
			return fmt.Errorf("parent closed synchronisation channel")
		}
		if procSync.Type != procResume {
			return fmt.Errorf("invalid synchronisation flag from parent")
		}
	}
	return nil
}

// setupUser changes the groups, gid, and uid for the user inside the container
func setupUser(config *initConfig) error {
	// Set up defaults.
	defaultExecUser := user.ExecUser{
		Uid:  syscall.Getuid(),
		Gid:  syscall.Getgid(),
		Home: "/",
	}
	passwdPath, err := user.GetPasswdPath()
	if err != nil {
		return err
	}
	groupPath, err := user.GetGroupPath()
	if err != nil {
		return err
	}
	execUser, err := user.GetExecUserPath(config.User, &defaultExecUser, passwdPath, groupPath)
	if err != nil {
		return err
	}

	var addGroups []int
	if len(config.AdditionalGroups) > 0 {
		addGroups, err = user.GetAdditionalGroupsPath(config.AdditionalGroups, groupPath)
		if err != nil {
			return err
		}
	}
	// before we change to the container's user make sure that the processes STDIO
	// is correctly owned by the user that we are switching to.
	if err := fixStdioPermissions(execUser); err != nil {
		return err
	}
	suppGroups := append(execUser.Sgids, addGroups...)
	if err := syscall.Setgroups(suppGroups); err != nil {
		return err
	}

	if err := system.Setgid(execUser.Gid); err != nil {
		return err
	}
	if err := system.Setuid(execUser.Uid); err != nil {
		return err
	}
	// if we didn't get HOME already, set it based on the user's HOME
	if envHome := os.Getenv("HOME"); envHome == "" {
		if err := os.Setenv("HOME", execUser.Home); err != nil {
			return err
		}
	}
	return nil
}

// fixStdioPermissions fixes the permissions of PID 1's STDIO within the container to the specified user.
// The ownership needs to match because it is created outside of the container and needs to be
// localized.
func fixStdioPermissions(u *user.ExecUser) error {
	var null syscall.Stat_t
	if err := syscall.Stat("/dev/null", &null); err != nil {
		return err
	}
	for _, fd := range []uintptr{
		os.Stdin.Fd(),
		os.Stderr.Fd(),
		os.Stdout.Fd(),
	} {
		var s syscall.Stat_t
		if err := syscall.Fstat(int(fd), &s); err != nil {
			return err
		}
		// skip chown of /dev/null if it was used as one of the STDIO fds.
		if s.Rdev == null.Rdev {
			continue
		}
		if err := syscall.Fchown(int(fd), u.Uid, u.Gid); err != nil {
			return err
		}
	}
	return nil
}

// setupNetwork sets up and initializes any network interface inside the container.
func setupNetwork(config *initConfig) error {
	for _, config := range config.Networks {
		strategy, err := getStrategy(config.Type)
		if err != nil {
			return err
		}
		if err := strategy.initialize(config); err != nil {
			return err
		}
	}
	return nil
}

func setupRoute(config *configs.Config) error {
	for _, config := range config.Routes {
		_, dst, err := net.ParseCIDR(config.Destination)
		if err != nil {
			return err
		}
		src := net.ParseIP(config.Source)
		if src == nil {
			return fmt.Errorf("Invalid source for route: %s", config.Source)
		}
		gw := net.ParseIP(config.Gateway)
		if gw == nil {
			return fmt.Errorf("Invalid gateway for route: %s", config.Gateway)
		}
		l, err := netlink.LinkByName(config.InterfaceName)
		if err != nil {
			return err
		}
		route := &netlink.Route{
			Scope:     netlink.SCOPE_UNIVERSE,
			Dst:       dst,
			Src:       src,
			Gw:        gw,
			LinkIndex: l.Attrs().Index,
		}
		if err := netlink.RouteAdd(route); err != nil {
			return err
		}
	}
	return nil
}

func setupRlimits(limits []configs.Rlimit, pid int) error {
	for _, rlimit := range limits {
		if err := system.Prlimit(pid, rlimit.Type, syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}); err != nil {
			return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err)
		}
	}
	return nil
}

func setOomScoreAdj(oomScoreAdj int, pid int) error {
	path := fmt.Sprintf("/proc/%d/oom_score_adj", pid)

	return ioutil.WriteFile(path, []byte(strconv.Itoa(oomScoreAdj)), 0600)
}

// killCgroupProcesses freezes then iterates over all the processes inside the
// manager's cgroups sending a SIGKILL to each process then waiting for them to
// exit.
func killCgroupProcesses(m cgroups.Manager) error {
	var procs []*os.Process
	if err := m.Freeze(configs.Frozen); err != nil {
		logrus.Warn(err)
	}
	pids, err := m.GetAllPids()
	if err != nil {
		m.Freeze(configs.Thawed)
		return err
	}
	for _, pid := range pids {
		p, err := os.FindProcess(pid)
		if err != nil {
			logrus.Warn(err)
			continue
		}
		procs = append(procs, p)
		if err := p.Kill(); err != nil {
			logrus.Warn(err)
		}
	}
	if err := m.Freeze(configs.Thawed); err != nil {
		logrus.Warn(err)
	}
	for _, p := range procs {
		if _, err := p.Wait(); err != nil {
			logrus.Warn(err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package sdl

// #include <SDL2/SDL.h>
import "C"

type PixelFormat struct {
	Format uint32
	Palette *Palette
	BitsPerPixels uint8
	BytesPerPixel uint8
	Padding [2]uint8
	Rmask uint32
	Gmask uint32
	Bmask uint32
	Amask uint32
	Rloss uint8
	Gloss uint8
	Bloss uint8
	Aloss uint8
	Rshift uint8
	Gshift uint8
	Bshift uint8
	Ashift uint8
	RefCount int
	next *PixelFormat
}

type Palette struct {
	Ncolors int
	Colors *Color
	Version uint32
	RefCount int
}

type Color struct {
	R uint8
	G uint8
	B uint8
	A uint8
}

func (c Color) Uint32() uint32 {
	var v uint32
	v |= uint32(c.R) << 16
	v |= uint32(c.G) << 8
	v |= uint32(c.B)
	return v
}

const (
    PIXELTYPE_UNKNOWN = iota
    PIXELTYPE_INDEX1
    PIXELTYPE_INDEX4
    PIXELTYPE_INDEX8
    PIXELTYPE_PACKED8
    PIXELTYPE_PACKED16
    PIXELTYPE_PACKED32
    PIXELTYPE_ARRAYU8
    PIXELTYPE_ARRAYU16
    PIXELTYPE_ARRAYU32
    PIXELTYPE_ARRAYF16
    PIXELTYPE_ARRAYF32
)

/** Bitmap pixel order high bit -> low bit. */
const (
    BITMAPORDER_NONE = iota
    BITMAPORDER_4321
    BITMAPORDER_1234
)

/** Packed component order high bit -> low bit. */
const (
    PACKEDORDER_NONE = iota
    PACKEDORDER_XRGB
    PACKEDORDER_RGBX
    PACKEDORDER_ARGB
    PACKEDORDER_RGBA
    PACKEDORDER_XBGR
    PACKEDORDER_BGRX
    PACKEDORDER_ABGR
    PACKEDORDER_BGRA
)

/** Array component order low byte -> high byte. */
const (
    ARRAYORDER_NONE = iota
    ARRAYORDER_RGB
    ARRAYORDER_RGBA
    ARRAYORDER_ARGB
    ARRAYORDER_BGR
    ARRAYORDER_BGRA
    ARRAYORDER_ABGR
)

/** Packed component layout. */
const (
    PACKEDLAYOUT_NONE = iota
    PACKEDLAYOUT_332
    PACKEDLAYOUT_4444
    PACKEDLAYOUT_1555
    PACKEDLAYOUT_5551
    PACKEDLAYOUT_565
    PACKEDLAYOUT_8888
    PACKEDLAYOUT_2101010
    PACKEDLAYOUT_1010102
)

const (
    PIXELFORMAT_UNKNOWN = iota
    PIXELFORMAT_ARGB8888 = ((1 << 28) | ((PIXELTYPE_PACKED32) << 24) | ((PACKEDORDER_ARGB) << 20) | ((PACKEDLAYOUT_8888) << 16) | ((32) << 8) | ((4) << 0))
)
<commit_msg>added pixel format<commit_after>package sdl

// #include <SDL2/SDL.h>
import "C"

type PixelFormat struct {
	Format uint32
	Palette *Palette
	BitsPerPixels uint8
	BytesPerPixel uint8
	Padding [2]uint8
	Rmask uint32
	Gmask uint32
	Bmask uint32
	Amask uint32
	Rloss uint8
	Gloss uint8
	Bloss uint8
	Aloss uint8
	Rshift uint8
	Gshift uint8
	Bshift uint8
	Ashift uint8
	RefCount int
	next *PixelFormat
}

type Palette struct {
	Ncolors int
	Colors *Color
	Version uint32
	RefCount int
}

type Color struct {
	R uint8
	G uint8
	B uint8
	A uint8
}

func (c Color) Uint32() uint32 {
	var v uint32
	v |= uint32(c.R) << 16
	v |= uint32(c.G) << 8
	v |= uint32(c.B)
	return v
}

const (
    PIXELTYPE_UNKNOWN = iota
    PIXELTYPE_INDEX1
    PIXELTYPE_INDEX4
    PIXELTYPE_INDEX8
    PIXELTYPE_PACKED8
    PIXELTYPE_PACKED16
    PIXELTYPE_PACKED32
    PIXELTYPE_ARRAYU8
    PIXELTYPE_ARRAYU16
    PIXELTYPE_ARRAYU32
    PIXELTYPE_ARRAYF16
    PIXELTYPE_ARRAYF32
)

/** Bitmap pixel order high bit -> low bit. */
const (
    BITMAPORDER_NONE = iota
    BITMAPORDER_4321
    BITMAPORDER_1234
)

/** Packed component order high bit -> low bit. */
const (
    PACKEDORDER_NONE = iota
    PACKEDORDER_XRGB
    PACKEDORDER_RGBX
    PACKEDORDER_ARGB
    PACKEDORDER_RGBA
    PACKEDORDER_XBGR
    PACKEDORDER_BGRX
    PACKEDORDER_ABGR
    PACKEDORDER_BGRA
)

/** Array component order low byte -> high byte. */
const (
    ARRAYORDER_NONE = iota
    ARRAYORDER_RGB
    ARRAYORDER_RGBA
    ARRAYORDER_ARGB
    ARRAYORDER_BGR
    ARRAYORDER_BGRA
    ARRAYORDER_ABGR
)

/** Packed component layout. */
const (
    PACKEDLAYOUT_NONE = iota
    PACKEDLAYOUT_332
    PACKEDLAYOUT_4444
    PACKEDLAYOUT_1555
    PACKEDLAYOUT_5551
    PACKEDLAYOUT_565
    PACKEDLAYOUT_8888
    PACKEDLAYOUT_2101010
    PACKEDLAYOUT_1010102
)

const (
	PIXELFORMAT_UNKNOWN     = C.SDL_PIXELFORMAT_UNKNOWN
	PIXELFORMAT_INDEX1LSB   = C.SDL_PIXELFORMAT_INDEX1LSB
	PIXELFORMAT_INDEX1MSB   = C.SDL_PIXELFORMAT_INDEX1MSB
	PIXELFORMAT_INDEX4LSB   = C.SDL_PIXELFORMAT_INDEX4LSB
	PIXELFORMAT_INDEX4MSB   = C.SDL_PIXELFORMAT_INDEX4MSB
	PIXELFORMAT_INDEX8      = C.SDL_PIXELFORMAT_INDEX8
	PIXELFORMAT_RGB332      = C.SDL_PIXELFORMAT_RGB332
	PIXELFORMAT_RGB444      = C.SDL_PIXELFORMAT_RGB444
	PIXELFORMAT_RGB555      = C.SDL_PIXELFORMAT_RGB555
	PIXELFORMAT_BGR555      = C.SDL_PIXELFORMAT_BGR555
	PIXELFORMAT_ARGB4444    = C.SDL_PIXELFORMAT_ARGB4444
	PIXELFORMAT_RGBA4444    = C.SDL_PIXELFORMAT_RGBA4444
	PIXELFORMAT_ABGR4444    = C.SDL_PIXELFORMAT_ABGR4444
	PIXELFORMAT_BGRA4444    = C.SDL_PIXELFORMAT_BGRA4444
	PIXELFORMAT_ARGB1555    = C.SDL_PIXELFORMAT_ARGB1555
	PIXELFORMAT_RGBA5551    = C.SDL_PIXELFORMAT_RGBA5551
	PIXELFORMAT_ABGR1555    = C.SDL_PIXELFORMAT_ABGR1555
	PIXELFORMAT_BGRA5551    = C.SDL_PIXELFORMAT_BGRA5551
	PIXELFORMAT_RGB565      = C.SDL_PIXELFORMAT_RGB565
	PIXELFORMAT_BGR565      = C.SDL_PIXELFORMAT_BGR565
	PIXELFORMAT_RGB24       = C.SDL_PIXELFORMAT_RGB24
	PIXELFORMAT_BGR24       = C.SDL_PIXELFORMAT_BGR24
	PIXELFORMAT_RGB888      = C.SDL_PIXELFORMAT_RGB888
	PIXELFORMAT_RGBX8888    = C.SDL_PIXELFORMAT_RGBX8888
	PIXELFORMAT_BGR888      = C.SDL_PIXELFORMAT_BGR888
	PIXELFORMAT_BGRX8888    = C.SDL_PIXELFORMAT_BGRX8888
	PIXELFORMAT_ARGB8888    = C.SDL_PIXELFORMAT_ARGB8888
	PIXELFORMAT_RGBA8888    = C.SDL_PIXELFORMAT_RGBA8888
	PIXELFORMAT_ABGR8888    = C.SDL_PIXELFORMAT_ABGR8888
	PIXELFORMAT_BGRA8888    = C.SDL_PIXELFORMAT_BGRA8888
	PIXELFORMAT_ARGB2101010 = C.SDL_PIXELFORMAT_ARGB2101010
	PIXELFORMAT_YV12        = C.SDL_PIXELFORMAT_YV12
	PIXELFORMAT_IYUV        = C.SDL_PIXELFORMAT_IYUV
	PIXELFORMAT_YUY2        = C.SDL_PIXELFORMAT_YUY2
	PIXELFORMAT_UYVY        = C.SDL_PIXELFORMAT_UYVY
	PIXELFORMAT_YVYU        = C.SDL_PIXELFORMAT_YVYU
)
<|endoftext|>


<commit_before>package libgobuster

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"os"
	"strings"
	"sync"
)

const (
	// VERSION contains the current gobuster version
	VERSION = "1.4.2"
)

// SetupFunc is the "setup" function prototype for implementations
type SetupFunc func(*Gobuster) error

// ProcessFunc is the "process" function prototype for implementations
type ProcessFunc func(*Gobuster, string) ([]Result, error)

// ResultToStringFunc is the "to string" function prototype for implementations
type ResultToStringFunc func(*Gobuster, *Result) (*string, error)

// Gobuster is the main object when creating a new run
type Gobuster struct {
	Opts             *Options
	http             *httpClient
	WildcardIps      stringSet
	context          context.Context
	requestsExpected int
	requestsIssued   int
	mu               *sync.RWMutex
	plugin           GobusterPlugin
	IsWildcard       bool
	resultChan       chan Result
	errorChan        chan error
}

// GobusterPlugin is an interface which plugins must implement
type GobusterPlugin interface {
	Setup(*Gobuster) error
	Process(*Gobuster, string) ([]Result, error)
	ResultToString(*Gobuster, *Result) (*string, error)
}

// NewGobuster returns a new Gobuster object
func NewGobuster(c context.Context, opts *Options, plugin GobusterPlugin) (*Gobuster, error) {
	// validate given options
	multiErr := opts.validate()
	if multiErr != nil {
		return nil, multiErr
	}

	var g Gobuster
	g.WildcardIps = newStringSet()
	g.context = c
	g.Opts = opts
	h, err := newHTTPClient(c, opts)
	if err != nil {
		return nil, err
	}
	g.http = h

	g.plugin = plugin
	g.mu = new(sync.RWMutex)

	g.resultChan = make(chan Result)
	g.errorChan = make(chan error)

	return &g, nil
}

// Results returns a channel of Results
func (g *Gobuster) Results() <-chan Result {
	return g.resultChan
}

// Errors returns a channel of errors
func (g *Gobuster) Errors() <-chan error {
	return g.errorChan
}

func (g *Gobuster) incrementRequests() {
	g.mu.Lock()
	g.requestsIssued++
	g.mu.Unlock()
}

// PrintProgress outputs the current wordlist progress to stderr
func (g *Gobuster) PrintProgress() {
	g.mu.RLock()
	if g.Opts.Wordlist == "-" {
		fmt.Fprintf(os.Stderr, "\rProgress: %d", g.requestsIssued)
		// only print status if we already read in the wordlist
	} else if g.requestsExpected > 0 {
		fmt.Fprintf(os.Stderr, "\rProgress: %d / %d", g.requestsIssued, g.requestsExpected)
	}
	g.mu.RUnlock()
}

// ClearProgress removes the last status line from stderr
func (g *Gobuster) ClearProgress() {
	fmt.Fprint(os.Stderr, resetTerminal())
}

// GetRequest issues a GET request to the target and returns
// the status code, length and an error
func (g *Gobuster) GetRequest(url string) (*int, *int64, error) {
	g.incrementRequests()
	return g.http.makeRequest(url, g.Opts.Cookies)
}

func (g *Gobuster) worker(wordChan <-chan string, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-g.context.Done():
			return
		case word := <-wordChan:
			// worker finished
			if word == "" {
				return
			}
			// Mode-specific processing
			res, err := g.plugin.Process(g, word)
			if err != nil {
				// do not exit and continue
				g.errorChan <- err
				continue
			} else {
				for _, r := range res {
					g.resultChan <- r
				}
			}
		}
	}
}

func (g *Gobuster) getWordlist() (*bufio.Scanner, error) {
	if g.Opts.Wordlist == "-" {
		// Read directly from stdin
		return bufio.NewScanner(os.Stdin), nil
	}
	// Pull content from the wordlist
	wordlist, err := os.Open(g.Opts.Wordlist)
	if err != nil {
		return nil, fmt.Errorf("failed to open wordlist: %v", err)
	}

	lines, err := lineCounter(wordlist)
	if err != nil {
		return nil, fmt.Errorf("failed to get number of lines: %v", err)
	}

	// mutiply by extensions to get the total number of requests
	if len(g.Opts.ExtensionsParsed.Set) > 0 {
		lines = lines + (lines * len(g.Opts.ExtensionsParsed.Set))
	}
	g.requestsExpected = lines
	g.requestsIssued = 0

	// rewind wordlist
	_, err = wordlist.Seek(0, 0)
	if err != nil {
		return nil, fmt.Errorf("failed to rewind wordlist: %v", err)
	}
	return bufio.NewScanner(wordlist), nil
}

// Start the busting of the website with the given
// set of settings from the command line.
func (g *Gobuster) Start() error {
	if err := g.plugin.Setup(g); err != nil {
		return err
	}

	var workerGroup sync.WaitGroup
	workerGroup.Add(g.Opts.Threads)

	wordChan := make(chan string, g.Opts.Threads)

	// Create goroutines for each of the number of threads
	// specified.
	for i := 0; i < g.Opts.Threads; i++ {
		go g.worker(wordChan, &workerGroup)
	}

	scanner, err := g.getWordlist()
	if err != nil {
		return err
	}

Scan:
	for scanner.Scan() {
		select {
		case <-g.context.Done():
			break Scan
		default:
			word := strings.TrimSpace(scanner.Text())
			// Skip "comment" (starts with #), as well as empty lines
			if !strings.HasPrefix(word, "#") && len(word) > 0 {
				wordChan <- word
			}
		}
	}
	close(wordChan)
	workerGroup.Wait()
	close(g.resultChan)
	close(g.errorChan)
	return nil
}

// GetConfigString returns the current config as a printable string
func (g *Gobuster) GetConfigString() (string, error) {
	buf := &bytes.Buffer{}
	o := g.Opts
	if _, err := fmt.Fprintf(buf, "[+] Mode         : %s\n", o.Mode); err != nil {
		return "", err
	}
	if _, err := fmt.Fprintf(buf, "[+] Url/Domain   : %s\n", o.URL); err != nil {
		return "", err
	}
	if _, err := fmt.Fprintf(buf, "[+] Threads      : %d\n", o.Threads); err != nil {
		return "", err
	}

	wordlist := "stdin (pipe)"
	if o.Wordlist != "-" {
		wordlist = o.Wordlist
	}
	if _, err := fmt.Fprintf(buf, "[+] Wordlist     : %s\n", wordlist); err != nil {
		return "", err
	}

	if o.Mode == ModeDir {
		if _, err := fmt.Fprintf(buf, "[+] Status codes : %s\n", o.StatusCodesParsed.Stringify()); err != nil {
			return "", err
		}

		if o.Proxy != "" {
			if _, err := fmt.Fprintf(buf, "[+] Proxy        : %s\n", o.Proxy); err != nil {
				return "", err
			}
		}

		if o.Cookies != "" {
			if _, err := fmt.Fprintf(buf, "[+] Cookies      : %s\n", o.Cookies); err != nil {
				return "", err
			}
		}

		if o.UserAgent != "" {
			if _, err := fmt.Fprintf(buf, "[+] User Agent   : %s\n", o.UserAgent); err != nil {
				return "", err
			}
		}

		if o.IncludeLength {
			if _, err := fmt.Fprintf(buf, "[+] Show length  : true\n"); err != nil {
				return "", err
			}
		}

		if o.Username != "" {
			if _, err := fmt.Fprintf(buf, "[+] Auth User    : %s\n", o.Username); err != nil {
				return "", err
			}
		}

		if len(o.Extensions) > 0 {
			if _, err := fmt.Fprintf(buf, "[+] Extensions   : %s\n", o.ExtensionsParsed.Stringify()); err != nil {
				return "", err
			}
		}

		if o.UseSlash {
			if _, err := fmt.Fprintf(buf, "[+] Add Slash    : true\n"); err != nil {
				return "", err
			}
		}

		if o.FollowRedirect {
			if _, err := fmt.Fprintf(buf, "[+] Follow Redir : true\n"); err != nil {
				return "", err
			}
		}

		if o.Expanded {
			if _, err := fmt.Fprintf(buf, "[+] Expanded     : true\n"); err != nil {
				return "", err
			}
		}

		if o.NoStatus {
			if _, err := fmt.Fprintf(buf, "[+] No status    : true\n"); err != nil {
				return "", err
			}
		}

		if o.Verbose {
			if _, err := fmt.Fprintf(buf, "[+] Verbose      : true\n"); err != nil {
				return "", err
			}
		}

		if _, err := fmt.Fprintf(buf, "[+] Timeout      : %s\n", o.Timeout.String()); err != nil {
			return "", err
		}
	}

	return strings.TrimSpace(buf.String()), nil
}
<commit_msg>better check if receive succeeded<commit_after>package libgobuster

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"os"
	"strings"
	"sync"
)

const (
	// VERSION contains the current gobuster version
	VERSION = "1.4.2"
)

// SetupFunc is the "setup" function prototype for implementations
type SetupFunc func(*Gobuster) error

// ProcessFunc is the "process" function prototype for implementations
type ProcessFunc func(*Gobuster, string) ([]Result, error)

// ResultToStringFunc is the "to string" function prototype for implementations
type ResultToStringFunc func(*Gobuster, *Result) (*string, error)

// Gobuster is the main object when creating a new run
type Gobuster struct {
	Opts             *Options
	http             *httpClient
	WildcardIps      stringSet
	context          context.Context
	requestsExpected int
	requestsIssued   int
	mu               *sync.RWMutex
	plugin           GobusterPlugin
	IsWildcard       bool
	resultChan       chan Result
	errorChan        chan error
}

// GobusterPlugin is an interface which plugins must implement
type GobusterPlugin interface {
	Setup(*Gobuster) error
	Process(*Gobuster, string) ([]Result, error)
	ResultToString(*Gobuster, *Result) (*string, error)
}

// NewGobuster returns a new Gobuster object
func NewGobuster(c context.Context, opts *Options, plugin GobusterPlugin) (*Gobuster, error) {
	// validate given options
	multiErr := opts.validate()
	if multiErr != nil {
		return nil, multiErr
	}

	var g Gobuster
	g.WildcardIps = newStringSet()
	g.context = c
	g.Opts = opts
	h, err := newHTTPClient(c, opts)
	if err != nil {
		return nil, err
	}
	g.http = h

	g.plugin = plugin
	g.mu = new(sync.RWMutex)

	g.resultChan = make(chan Result)
	g.errorChan = make(chan error)

	return &g, nil
}

// Results returns a channel of Results
func (g *Gobuster) Results() <-chan Result {
	return g.resultChan
}

// Errors returns a channel of errors
func (g *Gobuster) Errors() <-chan error {
	return g.errorChan
}

func (g *Gobuster) incrementRequests() {
	g.mu.Lock()
	g.requestsIssued++
	g.mu.Unlock()
}

// PrintProgress outputs the current wordlist progress to stderr
func (g *Gobuster) PrintProgress() {
	g.mu.RLock()
	if g.Opts.Wordlist == "-" {
		fmt.Fprintf(os.Stderr, "\rProgress: %d", g.requestsIssued)
		// only print status if we already read in the wordlist
	} else if g.requestsExpected > 0 {
		fmt.Fprintf(os.Stderr, "\rProgress: %d / %d", g.requestsIssued, g.requestsExpected)
	}
	g.mu.RUnlock()
}

// ClearProgress removes the last status line from stderr
func (g *Gobuster) ClearProgress() {
	fmt.Fprint(os.Stderr, resetTerminal())
}

// GetRequest issues a GET request to the target and returns
// the status code, length and an error
func (g *Gobuster) GetRequest(url string) (*int, *int64, error) {
	g.incrementRequests()
	return g.http.makeRequest(url, g.Opts.Cookies)
}

func (g *Gobuster) worker(wordChan <-chan string, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-g.context.Done():
			return
		case word, ok := <-wordChan:
			// worker finished
			if !ok {
				return
			}
			// Mode-specific processing
			res, err := g.plugin.Process(g, word)
			if err != nil {
				// do not exit and continue
				g.errorChan <- err
				continue
			} else {
				for _, r := range res {
					g.resultChan <- r
				}
			}
		}
	}
}

func (g *Gobuster) getWordlist() (*bufio.Scanner, error) {
	if g.Opts.Wordlist == "-" {
		// Read directly from stdin
		return bufio.NewScanner(os.Stdin), nil
	}
	// Pull content from the wordlist
	wordlist, err := os.Open(g.Opts.Wordlist)
	if err != nil {
		return nil, fmt.Errorf("failed to open wordlist: %v", err)
	}

	lines, err := lineCounter(wordlist)
	if err != nil {
		return nil, fmt.Errorf("failed to get number of lines: %v", err)
	}

	// mutiply by extensions to get the total number of requests
	if len(g.Opts.ExtensionsParsed.Set) > 0 {
		lines = lines + (lines * len(g.Opts.ExtensionsParsed.Set))
	}
	g.requestsExpected = lines
	g.requestsIssued = 0

	// rewind wordlist
	_, err = wordlist.Seek(0, 0)
	if err != nil {
		return nil, fmt.Errorf("failed to rewind wordlist: %v", err)
	}
	return bufio.NewScanner(wordlist), nil
}

// Start the busting of the website with the given
// set of settings from the command line.
func (g *Gobuster) Start() error {
	if err := g.plugin.Setup(g); err != nil {
		return err
	}

	var workerGroup sync.WaitGroup
	workerGroup.Add(g.Opts.Threads)

	wordChan := make(chan string, g.Opts.Threads)

	// Create goroutines for each of the number of threads
	// specified.
	for i := 0; i < g.Opts.Threads; i++ {
		go g.worker(wordChan, &workerGroup)
	}

	scanner, err := g.getWordlist()
	if err != nil {
		return err
	}

Scan:
	for scanner.Scan() {
		select {
		case <-g.context.Done():
			break Scan
		default:
			word := strings.TrimSpace(scanner.Text())
			// Skip "comment" (starts with #), as well as empty lines
			if !strings.HasPrefix(word, "#") && len(word) > 0 {
				wordChan <- word
			}
		}
	}
	close(wordChan)
	workerGroup.Wait()
	close(g.resultChan)
	close(g.errorChan)
	return nil
}

// GetConfigString returns the current config as a printable string
func (g *Gobuster) GetConfigString() (string, error) {
	buf := &bytes.Buffer{}
	o := g.Opts
	if _, err := fmt.Fprintf(buf, "[+] Mode         : %s\n", o.Mode); err != nil {
		return "", err
	}
	if _, err := fmt.Fprintf(buf, "[+] Url/Domain   : %s\n", o.URL); err != nil {
		return "", err
	}
	if _, err := fmt.Fprintf(buf, "[+] Threads      : %d\n", o.Threads); err != nil {
		return "", err
	}

	wordlist := "stdin (pipe)"
	if o.Wordlist != "-" {
		wordlist = o.Wordlist
	}
	if _, err := fmt.Fprintf(buf, "[+] Wordlist     : %s\n", wordlist); err != nil {
		return "", err
	}

	if o.Mode == ModeDir {
		if _, err := fmt.Fprintf(buf, "[+] Status codes : %s\n", o.StatusCodesParsed.Stringify()); err != nil {
			return "", err
		}

		if o.Proxy != "" {
			if _, err := fmt.Fprintf(buf, "[+] Proxy        : %s\n", o.Proxy); err != nil {
				return "", err
			}
		}

		if o.Cookies != "" {
			if _, err := fmt.Fprintf(buf, "[+] Cookies      : %s\n", o.Cookies); err != nil {
				return "", err
			}
		}

		if o.UserAgent != "" {
			if _, err := fmt.Fprintf(buf, "[+] User Agent   : %s\n", o.UserAgent); err != nil {
				return "", err
			}
		}

		if o.IncludeLength {
			if _, err := fmt.Fprintf(buf, "[+] Show length  : true\n"); err != nil {
				return "", err
			}
		}

		if o.Username != "" {
			if _, err := fmt.Fprintf(buf, "[+] Auth User    : %s\n", o.Username); err != nil {
				return "", err
			}
		}

		if len(o.Extensions) > 0 {
			if _, err := fmt.Fprintf(buf, "[+] Extensions   : %s\n", o.ExtensionsParsed.Stringify()); err != nil {
				return "", err
			}
		}

		if o.UseSlash {
			if _, err := fmt.Fprintf(buf, "[+] Add Slash    : true\n"); err != nil {
				return "", err
			}
		}

		if o.FollowRedirect {
			if _, err := fmt.Fprintf(buf, "[+] Follow Redir : true\n"); err != nil {
				return "", err
			}
		}

		if o.Expanded {
			if _, err := fmt.Fprintf(buf, "[+] Expanded     : true\n"); err != nil {
				return "", err
			}
		}

		if o.NoStatus {
			if _, err := fmt.Fprintf(buf, "[+] No status    : true\n"); err != nil {
				return "", err
			}
		}

		if o.Verbose {
			if _, err := fmt.Fprintf(buf, "[+] Verbose      : true\n"); err != nil {
				return "", err
			}
		}

		if _, err := fmt.Fprintf(buf, "[+] Timeout      : %s\n", o.Timeout.String()); err != nil {
			return "", err
		}
	}

	return strings.TrimSpace(buf.String()), nil
}
<|endoftext|>


<commit_before>package sdp

import (
	"fmt"
	"net/url"
	"strconv"
	"time"
)

// Constants for SDP attributes used in JSEP
const (
	AttrKeyIdentity        = "identity"
	AttrKeyGroup           = "group"
	AttrKeySSRC            = "ssrc"
	AttrKeySSRCGroup       = "ssrc-group"
	AttrKeyMsid            = "msid"
	AttrKeyMsidSemantic    = "msid-semantic"
	AttrKeyConnectionSetup = "setup"
	AttrKeyMID             = "mid"
	AttrKeyICELite         = "ice-lite"
	AttrKeyRTCPMux         = "rtcp-mux"
	AttrKeyRTCPRsize       = "rtcp-rsize"
	AttrKeyInactive        = "inactive"
	AttrKeyRecvOnly        = "recvonly"
	AttrKeySendOnly        = "sendonly"
	AttrKeySendRecv        = "sendrecv"
	AttrKeyExtMap          = "extmap"
)

// Constants for semantic tokens used in JSEP
const (
	SemanticTokenLipSynchronization     = "LS"
	SemanticTokenFlowIdentification     = "FID"
	SemanticTokenForwardErrorCorrection = "FEC"
	SemanticTokenWebRTCMediaStreams     = "WMS"
)

// Constants for extmap key
const (
	ExtMapValueTransportCC = 3
)

// extMapURI is a map
var extMapURI = map[int]string{
	ExtMapValueTransportCC: "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
}

// API to match draft-ietf-rtcweb-jsep
// Move to webrtc or its own package?

// NewJSEPSessionDescription creates a new SessionDescription with
// some settings that are required by the JSEP spec.
func NewJSEPSessionDescription(identity bool) *SessionDescription {
	d := &SessionDescription{
		Version: 0,
		Origin: Origin{
			Username:       "-",
			SessionID:      newSessionID(),
			SessionVersion: uint64(time.Now().Unix()),
			NetworkType:    "IN",
			AddressType:    "IP4",
			UnicastAddress: "0.0.0.0",
		},
		SessionName: "-",
		TimeDescriptions: []TimeDescription{
			{
				Timing: Timing{
					StartTime: 0,
					StopTime:  0,
				},
				RepeatTimes: nil,
			},
		},
		Attributes: []Attribute{
			// 	"Attribute(ice-options:trickle)", // TODO: implement trickle ICE
		},
	}

	if identity {
		d.WithPropertyAttribute(AttrKeyIdentity)
	}

	return d
}

// WithPropertyAttribute adds a property attribute 'a=key' to the session description
func (s *SessionDescription) WithPropertyAttribute(key string) *SessionDescription {
	s.Attributes = append(s.Attributes, NewPropertyAttribute(key))
	return s
}

// WithValueAttribute adds a value attribute 'a=key:value' to the session description
func (s *SessionDescription) WithValueAttribute(key, value string) *SessionDescription {
	s.Attributes = append(s.Attributes, NewAttribute(key, value))
	return s
}

// WithFingerprint adds a fingerprint to the session description
func (s *SessionDescription) WithFingerprint(algorithm, value string) *SessionDescription {
	return s.WithValueAttribute("fingerprint", algorithm+" "+value)
}

// WithMedia adds a media description to the session description
func (s *SessionDescription) WithMedia(md *MediaDescription) *SessionDescription {
	s.MediaDescriptions = append(s.MediaDescriptions, md)
	return s
}

// NewJSEPMediaDescription creates a new MediaName with
// some settings that are required by the JSEP spec.
func NewJSEPMediaDescription(codecType string, codecPrefs []string) *MediaDescription {
	// TODO: handle codecPrefs
	d := &MediaDescription{
		MediaName: MediaName{
			Media:  codecType,
			Port:   RangedPort{Value: 9},
			Protos: []string{"UDP", "TLS", "RTP", "SAVPF"},
		},
		ConnectionInformation: &ConnectionInformation{
			NetworkType: "IN",
			AddressType: "IP4",
			Address: &Address{
				Address: "0.0.0.0",
			},
		},
	}
	return d
}

// WithPropertyAttribute adds a property attribute 'a=key' to the media description
func (d *MediaDescription) WithPropertyAttribute(key string) *MediaDescription {
	d.Attributes = append(d.Attributes, NewPropertyAttribute(key))
	return d
}

// WithValueAttribute adds a value attribute 'a=key:value' to the media description
func (d *MediaDescription) WithValueAttribute(key, value string) *MediaDescription {
	d.Attributes = append(d.Attributes, NewAttribute(key, value))
	return d
}

// WithICECredentials adds ICE credentials to the media description
func (d *MediaDescription) WithICECredentials(username, password string) *MediaDescription {
	return d.
		WithValueAttribute("ice-ufrag", username).
		WithValueAttribute("ice-pwd", password)
}

// WithCodec adds codec information to the media description
func (d *MediaDescription) WithCodec(payloadType uint8, name string, clockrate uint32, channels uint16, fmtp string) *MediaDescription {
	d.MediaName.Formats = append(d.MediaName.Formats, strconv.Itoa(int(payloadType)))
	rtpmap := fmt.Sprintf("%d %s/%d", payloadType, name, clockrate)
	if channels > 0 {
		rtpmap = rtpmap + fmt.Sprintf("/%d", channels)
	}
	d.WithValueAttribute("rtpmap", rtpmap)
	if fmtp != "" {
		d.WithValueAttribute("fmtp", fmt.Sprintf("%d %s", payloadType, fmtp))
	}
	return d
}

// WithMediaSource adds media source information to the media description
func (d *MediaDescription) WithMediaSource(ssrc uint32, cname, streamLabel, label string) *MediaDescription {
	return d.
		WithValueAttribute("ssrc", fmt.Sprintf("%d cname:%s", ssrc, cname)). // Deprecated but not phased out?
		WithValueAttribute("ssrc", fmt.Sprintf("%d msid:%s %s", ssrc, streamLabel, label)).
		WithValueAttribute("ssrc", fmt.Sprintf("%d mslabel:%s", ssrc, streamLabel)). // Deprecated but not phased out?
		WithValueAttribute("ssrc", fmt.Sprintf("%d label:%s", ssrc, label))          // Deprecated but not phased out?
}

// WithCandidate adds an ICE candidate to the media description
// Deprecated: use WithICECandidate instead
func (d *MediaDescription) WithCandidate(value string) *MediaDescription {
	return d.WithValueAttribute("candidate", value)
}

// WithICECandidate adds an ICE candidate to the media description
func (d *MediaDescription) WithICECandidate(c ICECandidate) *MediaDescription {
	return d.WithValueAttribute("candidate", c.Marshal())
}

// WithExtMap adds an extmap to the media description
func (d *MediaDescription) WithExtMap(e ExtMap) *MediaDescription {
	return d.WithPropertyAttribute(e.Marshal())
}

// WithTransportCCExtMap adds an extmap to the media description
func (d *MediaDescription) WithTransportCCExtMap() *MediaDescription {
	uri, _ := url.Parse(extMapURI[ExtMapValueTransportCC])
	e := ExtMap{
		Value: ExtMapValueTransportCC,
		URI:   uri,
	}
	return d.WithExtMap(e)
}
<commit_msg>Media description fingerprints<commit_after>package sdp

import (
	"fmt"
	"net/url"
	"strconv"
	"time"
)

// Constants for SDP attributes used in JSEP
const (
	AttrKeyIdentity        = "identity"
	AttrKeyGroup           = "group"
	AttrKeySSRC            = "ssrc"
	AttrKeySSRCGroup       = "ssrc-group"
	AttrKeyMsid            = "msid"
	AttrKeyMsidSemantic    = "msid-semantic"
	AttrKeyConnectionSetup = "setup"
	AttrKeyMID             = "mid"
	AttrKeyICELite         = "ice-lite"
	AttrKeyRTCPMux         = "rtcp-mux"
	AttrKeyRTCPRsize       = "rtcp-rsize"
	AttrKeyInactive        = "inactive"
	AttrKeyRecvOnly        = "recvonly"
	AttrKeySendOnly        = "sendonly"
	AttrKeySendRecv        = "sendrecv"
	AttrKeyExtMap          = "extmap"
)

// Constants for semantic tokens used in JSEP
const (
	SemanticTokenLipSynchronization     = "LS"
	SemanticTokenFlowIdentification     = "FID"
	SemanticTokenForwardErrorCorrection = "FEC"
	SemanticTokenWebRTCMediaStreams     = "WMS"
)

// Constants for extmap key
const (
	ExtMapValueTransportCC = 3
)

// extMapURI is a map
var extMapURI = map[int]string{
	ExtMapValueTransportCC: "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
}

// API to match draft-ietf-rtcweb-jsep
// Move to webrtc or its own package?

// NewJSEPSessionDescription creates a new SessionDescription with
// some settings that are required by the JSEP spec.
func NewJSEPSessionDescription(identity bool) *SessionDescription {
	d := &SessionDescription{
		Version: 0,
		Origin: Origin{
			Username:       "-",
			SessionID:      newSessionID(),
			SessionVersion: uint64(time.Now().Unix()),
			NetworkType:    "IN",
			AddressType:    "IP4",
			UnicastAddress: "0.0.0.0",
		},
		SessionName: "-",
		TimeDescriptions: []TimeDescription{
			{
				Timing: Timing{
					StartTime: 0,
					StopTime:  0,
				},
				RepeatTimes: nil,
			},
		},
		Attributes: []Attribute{
			// 	"Attribute(ice-options:trickle)", // TODO: implement trickle ICE
		},
	}

	if identity {
		d.WithPropertyAttribute(AttrKeyIdentity)
	}

	return d
}

// WithPropertyAttribute adds a property attribute 'a=key' to the session description
func (s *SessionDescription) WithPropertyAttribute(key string) *SessionDescription {
	s.Attributes = append(s.Attributes, NewPropertyAttribute(key))
	return s
}

// WithValueAttribute adds a value attribute 'a=key:value' to the session description
func (s *SessionDescription) WithValueAttribute(key, value string) *SessionDescription {
	s.Attributes = append(s.Attributes, NewAttribute(key, value))
	return s
}

// WithFingerprint adds a fingerprint to the session description
func (s *SessionDescription) WithFingerprint(algorithm, value string) *SessionDescription {
	return s.WithValueAttribute("fingerprint", algorithm+" "+value)
}

// WithMedia adds a media description to the session description
func (s *SessionDescription) WithMedia(md *MediaDescription) *SessionDescription {
	s.MediaDescriptions = append(s.MediaDescriptions, md)
	return s
}

// NewJSEPMediaDescription creates a new MediaName with
// some settings that are required by the JSEP spec.
func NewJSEPMediaDescription(codecType string, codecPrefs []string) *MediaDescription {
	// TODO: handle codecPrefs
	d := &MediaDescription{
		MediaName: MediaName{
			Media:  codecType,
			Port:   RangedPort{Value: 9},
			Protos: []string{"UDP", "TLS", "RTP", "SAVPF"},
		},
		ConnectionInformation: &ConnectionInformation{
			NetworkType: "IN",
			AddressType: "IP4",
			Address: &Address{
				Address: "0.0.0.0",
			},
		},
	}
	return d
}

// WithPropertyAttribute adds a property attribute 'a=key' to the media description
func (d *MediaDescription) WithPropertyAttribute(key string) *MediaDescription {
	d.Attributes = append(d.Attributes, NewPropertyAttribute(key))
	return d
}

// WithValueAttribute adds a value attribute 'a=key:value' to the media description
func (d *MediaDescription) WithValueAttribute(key, value string) *MediaDescription {
	d.Attributes = append(d.Attributes, NewAttribute(key, value))
	return d
}

// WithFingerprint adds a fingerprint to the media description
func (d *MediaDescription) WithFingerprint(algorithm, value string) *MediaDescription {
	return d.WithValueAttribute("fingerprint", algorithm+" "+value)
}

// WithICECredentials adds ICE credentials to the media description
func (d *MediaDescription) WithICECredentials(username, password string) *MediaDescription {
	return d.
		WithValueAttribute("ice-ufrag", username).
		WithValueAttribute("ice-pwd", password)
}

// WithCodec adds codec information to the media description
func (d *MediaDescription) WithCodec(payloadType uint8, name string, clockrate uint32, channels uint16, fmtp string) *MediaDescription {
	d.MediaName.Formats = append(d.MediaName.Formats, strconv.Itoa(int(payloadType)))
	rtpmap := fmt.Sprintf("%d %s/%d", payloadType, name, clockrate)
	if channels > 0 {
		rtpmap = rtpmap + fmt.Sprintf("/%d", channels)
	}
	d.WithValueAttribute("rtpmap", rtpmap)
	if fmtp != "" {
		d.WithValueAttribute("fmtp", fmt.Sprintf("%d %s", payloadType, fmtp))
	}
	return d
}

// WithMediaSource adds media source information to the media description
func (d *MediaDescription) WithMediaSource(ssrc uint32, cname, streamLabel, label string) *MediaDescription {
	return d.
		WithValueAttribute("ssrc", fmt.Sprintf("%d cname:%s", ssrc, cname)). // Deprecated but not phased out?
		WithValueAttribute("ssrc", fmt.Sprintf("%d msid:%s %s", ssrc, streamLabel, label)).
		WithValueAttribute("ssrc", fmt.Sprintf("%d mslabel:%s", ssrc, streamLabel)). // Deprecated but not phased out?
		WithValueAttribute("ssrc", fmt.Sprintf("%d label:%s", ssrc, label))          // Deprecated but not phased out?
}

// WithCandidate adds an ICE candidate to the media description
// Deprecated: use WithICECandidate instead
func (d *MediaDescription) WithCandidate(value string) *MediaDescription {
	return d.WithValueAttribute("candidate", value)
}

// WithICECandidate adds an ICE candidate to the media description
func (d *MediaDescription) WithICECandidate(c ICECandidate) *MediaDescription {
	return d.WithValueAttribute("candidate", c.Marshal())
}

// WithExtMap adds an extmap to the media description
func (d *MediaDescription) WithExtMap(e ExtMap) *MediaDescription {
	return d.WithPropertyAttribute(e.Marshal())
}

// WithTransportCCExtMap adds an extmap to the media description
func (d *MediaDescription) WithTransportCCExtMap() *MediaDescription {
	uri, _ := url.Parse(extMapURI[ExtMapValueTransportCC])
	e := ExtMap{
		Value: ExtMapValueTransportCC,
		URI:   uri,
	}
	return d.WithExtMap(e)
}
<|endoftext|>


<commit_before>package api

import (
	"emp/db"
	"emp/objects"
	"fmt"
	"quibit"
	"time"
)

// Handle a Version Request or Reply
func fVERSION(config *ApiConfig, frame quibit.Frame, version *objects.Version) {

	// Verify not objects.BROADCAST
	if frame.Header.Type == objects.BROADCAST {
		// SHUN THE NODE! SHUN IT WITH FIRE!
		config.Log <- "Node sent a version message as a broadcast. Disconnecting..."
		quibit.KillPeer(frame.Peer)
		return
	}

	// Verify Protcol Version, else Disconnect
	if version.Version != objects.LOCAL_VERSION {
		config.Log <- fmt.Sprintf("Peer protocol version does not match local version: %d", version.Version)
		quibit.KillPeer(frame.Peer)
		return
	}

	// Verify Timestamp (5 minute window), else Disconnect
	dur := time.Since(version.Timestamp)
	if dur.Minutes()+5 > 10 {
		config.Log <- fmt.Sprintf("Peer timestamp too far off local time: %s", dur.String())
		quibit.KillPeer(frame.Peer)
		return
	}

	// If backbone node, verify IP
	backbone := false
	for _, b := range []byte(version.IpAddress) {
		if b != 0 {
			backbone = true
		}
	}

	if backbone {
		testIP := quibit.GetPeer(frame.Peer).IP
		if version.IpAddress.String() != testIP.String() {
			config.Log <- fmt.Sprintf("Backbone node broadcast incorrect IP: %s", version.IpAddress.String())
			quibit.KillPeer(frame.Peer)
			return
		}

		// Add to Master Node List
		var node objects.Node

		node.IP = version.IpAddress
		node.Port = version.Port
		node.LastSeen = time.Now().Round(time.Second)
		config.NodeList.Nodes[node.String()] = node
	}

	var sending *quibit.Frame
	if frame.Header.Type == objects.REQUEST {
		// If a objects.REQUEST, send local version as a objects.REPLY
		config.LocalVersion.Timestamp = time.Now().Round(time.Second)
		sending = objects.MakeFrame(objects.VERSION, objects.REPLY, &config.LocalVersion)
	} else {
		// If a objects.REPLY, send a peer list as a objects.REQUEST
		sending = objects.MakeFrame(objects.PEER, objects.REQUEST, &config.NodeList)
	}
	sending.Peer = frame.Peer
	config.SendQueue <- *sending
} // End fVERSION

// Handle Peer List Requests or Replies
func fPEER(config *ApiConfig, frame quibit.Frame, nodeList *objects.NodeList) {

	// Verify not objects.BROADCAST
	if frame.Header.Type == objects.BROADCAST {
		// SHUN THE NODE! SHUN IT WITH FIRE!
		config.Log <- "Node sent a peer frame as a broadcast. Disconnecting..."
		quibit.KillPeer(frame.Peer)
		return
	}

	var sending *quibit.Frame
	if frame.Header.Type == objects.REQUEST {
		// If a objects.REQUEST, send back peer objects.REPLY
		sending = objects.MakeFrame(objects.PEER, objects.REPLY, &config.NodeList)
	} else {
		// If a objects.REPLY, send an object list as a objects.REQUEST
		sending = objects.MakeFrame(objects.OBJ, objects.REQUEST, db.ObjList())
	}

	sending.Peer = frame.Peer

	config.SendQueue <- *sending

	// Merge incoming list with current list
	for key, node := range nodeList.Nodes {
		_, ok := config.NodeList.Nodes[key]
		if !ok {
			config.NodeList.Nodes[key] = node
			p := new(quibit.Peer)
			p.IP = node.IP
			p.Port = node.Port
			config.PeerQueue <- *p
			time.Sleep(time.Millisecond)
			newVer := objects.MakeFrame(objects.VERSION, objects.REQUEST, &config.LocalVersion)
			config.SendQueue <- *newVer
		} // End if
	} // End for
} // End fPEER

// Handle Object Vector Requests or Replies
func fOBJ(config *ApiConfig, frame quibit.Frame, obj *objects.Obj) {
	var sending *quibit.Frame

	// Verify not objects.BROADCAST
	if frame.Header.Type == objects.BROADCAST {
		// SHUN THE NODE! SHUN IT WITH FIRE!
		config.Log <- "Node sent an obj frame as a broadcast. Disconnecting..."
		quibit.KillPeer(frame.Peer)
		return
	}

	if frame.Header.Type == objects.REQUEST {
		// If a objects.REQUEST, send local object list as objects.REPLY
		sending = objects.MakeFrame(objects.OBJ, objects.REPLY, db.ObjList())
		sending.Peer = frame.Peer
		config.SendQueue <- *sending
	}

	// For each object in object list:
	// If object not stored locally, send GETOBJ objects.REQUEST
	for _, hash := range obj.HashList {
		if db.Contains(hash) == db.NOTFOUND {
			sending = objects.MakeFrame(objects.GETOBJ, objects.REQUEST, &hash)
			sending.Peer = frame.Peer
			config.SendQueue <- *sending
		}
	}
}

// Handle Object Detail Requests
func fGETOBJ(config *ApiConfig, frame quibit.Frame, hash *objects.Hash) {
	// Verify not objects.BROADCAST
	if frame.Header.Type == objects.BROADCAST {
		// SHUN THE NODE! SHUN IT WITH FIRE!
		config.Log <- "Node sent a getobj message as a broadcast. Disconnecting..."
		quibit.KillPeer(frame.Peer)
		return
	}

	// If object stored locally, send object as a objects.REPLY
	var sending *quibit.Frame
	if frame.Header.Type == objects.REQUEST {
		switch db.Contains(*hash) {
		case db.PUBKEY:
			sending = objects.MakeFrame(objects.PUBKEY, objects.REPLY, db.GetPubkey(config.Log, *hash))
		case db.PURGE:
			sending = objects.MakeFrame(objects.PURGE, objects.REPLY, db.GetPurge(config.Log, *hash))
		case db.MSG:
			sending = objects.MakeFrame(objects.MSG, objects.REPLY, db.GetMessage(config.Log, *hash))
		case db.PUBKEYRQ:
			sending = objects.MakeFrame(objects.PUBKEY_REQUEST, objects.REPLY, hash)
		default:
			sending = objects.MakeFrame(objects.GETOBJ, objects.REPLY, new(objects.NilPayload))
		} // End switch
		sending.Peer = frame.Peer
		config.SendQueue <- *sending
	} // End if
} // End fGETOBJ

// Handle Public Key Request Broadcasts
func fPUBKEY_REQUEST(config *ApiConfig, frame quibit.Frame, pubHash *objects.Hash) {
	// Check Hash in Object List

	switch db.Contains(*pubHash) {
	// If request is Not in List, store the request
	case db.NOTFOUND:
		// If a objects.BROADCAST, send out another objects.BROADCAST
		db.Add(*pubHash, db.PUBKEYRQ)
		if frame.Header.Type == objects.BROADCAST {
			config.SendQueue <- *objects.MakeFrame(objects.PUBKEY_REQUEST, objects.BROADCAST, pubHash)
		}

	// If request is a Public Key in List:
	case db.PUBKEY:
		// Send out the PUBKEY as a objects.BROADCAST
		config.SendQueue <- *objects.MakeFrame(objects.PUBKEY, objects.BROADCAST, db.GetPubkey(config.Log, *pubHash))
	}
}

// Handle Public Key Broadcasts
func fPUBKEY(config *ApiConfig, frame quibit.Frame, pubkey *objects.EncryptedPubkey) {
	// Check Hash in Object List
	switch db.Contains(pubkey.AddrHash) {
	// If request is a Pubkey Request, remove the pubkey request
	case db.PUBKEYRQ:
		db.Delete(pubkey.AddrHash)
		fallthrough
	case db.NOTFOUND:
		// Add Pubkey to database
		err := db.AddPubkey(config.Log, *pubkey)
		if err != nil {
			config.Log <- fmt.Sprintf("Error adding pubkey to database: %s", err)
			break
		}
		// If a objects.BROADCAST, send a objects.BROADCAST
		if frame.Header.Type == objects.BROADCAST {
			config.SendQueue <- *objects.MakeFrame(objects.PUBKEY, objects.BROADCAST, pubkey)
		}

		config.PubkeyRegister <- pubkey.AddrHash
	}
} // End fPUBKEY

// Handle Encrypted Message Broadcasts
func fMSG(config *ApiConfig, frame quibit.Frame, msg *objects.Message) {
	// Check Hash in Object List
	switch db.Contains(msg.TxidHash) {
	// If Not in List, Store and objects.BROADCAST
	case db.NOTFOUND:
		err := db.AddMessage(config.Log, msg)
		if err != nil {
			config.Log <- fmt.Sprintf("Error adding message to database: %s", err)
			break
		}
		if frame.Header.Type == objects.BROADCAST {
			config.SendQueue <- *objects.MakeFrame(objects.MSG, objects.BROADCAST, msg)
		}

		config.MessageRegister <- *msg

	// If found as PURGE, reply with PURGE
	case db.PURGE:
		sending := objects.MakeFrame(objects.PURGE, objects.REPLY, db.GetPurge(config.Log, msg.TxidHash))
		sending.Peer = frame.Peer
		config.SendQueue <- *sending
	}
} // End fMSG

// Handle Purge Broadcasts
func fPURGE(config *ApiConfig, frame quibit.Frame, purge *objects.Purge) {
	var err error
	txidHash := objects.MakeHash(purge.Txid[:])

	// Check Hash in Object List
	switch db.Contains(txidHash) {
	// Delete Stored Messages
	case db.MSG:
		err = db.RemoveHash(config.Log, txidHash)
		if err != nil {
			config.Log <- fmt.Sprintf("Error removing message from database: %s", err)
			break
		}
		fallthrough
	// Add to database
	case db.NOTFOUND:
		err = db.AddPurge(config.Log, *purge)
		if err != nil {
			config.Log <- fmt.Sprintf("Error adding purge to database: ", err)
			break
		}

		// Re-objects.BROADCAST if necessary
		if frame.Header.Type == objects.BROADCAST {
			config.SendQueue <- *objects.MakeFrame(objects.PURGE, objects.BROADCAST, purge)
		}
		config.PurgeRegister <- purge.Txid
	} // End Switch
} // End fPURGE
<commit_msg>Case of no peers<commit_after>package api

import (
	"emp/db"
	"emp/objects"
	"fmt"
	"quibit"
	"time"
)

// Handle a Version Request or Reply
func fVERSION(config *ApiConfig, frame quibit.Frame, version *objects.Version) {

	// Verify not objects.BROADCAST
	if frame.Header.Type == objects.BROADCAST {
		// SHUN THE NODE! SHUN IT WITH FIRE!
		config.Log <- "Node sent a version message as a broadcast. Disconnecting..."
		quibit.KillPeer(frame.Peer)
		return
	}

	// Verify Protcol Version, else Disconnect
	if version.Version != objects.LOCAL_VERSION {
		config.Log <- fmt.Sprintf("Peer protocol version does not match local version: %d", version.Version)
		quibit.KillPeer(frame.Peer)
		return
	}

	// Verify Timestamp (5 minute window), else Disconnect
	dur := time.Since(version.Timestamp)
	if dur.Minutes()+5 > 10 {
		config.Log <- fmt.Sprintf("Peer timestamp too far off local time: %s", dur.String())
		quibit.KillPeer(frame.Peer)
		return
	}

	// If backbone node, verify IP
	backbone := false
	for _, b := range []byte(version.IpAddress) {
		if b != 0 {
			backbone = true
		}
	}

	if backbone {
		testIP := quibit.GetPeer(frame.Peer).IP
		if version.IpAddress.String() != testIP.String() {
			config.Log <- fmt.Sprintf("Backbone node broadcast incorrect IP: %s", version.IpAddress.String())
			quibit.KillPeer(frame.Peer)
			return
		}

		// Add to Master Node List
		var node objects.Node

		node.IP = version.IpAddress
		node.Port = version.Port
		node.LastSeen = time.Now().Round(time.Second)
		config.NodeList.Nodes[node.String()] = node
	}

	var sending *quibit.Frame
	if frame.Header.Type == objects.REQUEST {
		// If a objects.REQUEST, send local version as a objects.REPLY
		config.LocalVersion.Timestamp = time.Now().Round(time.Second)
		sending = objects.MakeFrame(objects.VERSION, objects.REPLY, &config.LocalVersion)
	} else {
		// If a objects.REPLY, send a peer list as a objects.REQUEST
		sending = objects.MakeFrame(objects.PEER, objects.REQUEST, &config.NodeList)
	}
	sending.Peer = frame.Peer
	config.SendQueue <- *sending
} // End fVERSION

// Handle Peer List Requests or Replies
func fPEER(config *ApiConfig, frame quibit.Frame, nodeList *objects.NodeList) {

	// Verify not objects.BROADCAST
	if frame.Header.Type == objects.BROADCAST {
		// SHUN THE NODE! SHUN IT WITH FIRE!
		config.Log <- "Node sent a peer frame as a broadcast. Disconnecting..."
		quibit.KillPeer(frame.Peer)
		return
	}

	var sending *quibit.Frame
	if frame.Header.Type == objects.REQUEST {
		// If a objects.REQUEST, send back peer objects.REPLY
		sending = objects.MakeFrame(objects.PEER, objects.REPLY, &config.NodeList)
	} else {
		// If a objects.REPLY, send an object list as a objects.REQUEST
		sending = objects.MakeFrame(objects.OBJ, objects.REQUEST, db.ObjList())
	}

	sending.Peer = frame.Peer

	config.SendQueue <- *sending

	if nodeList != nil {

		// Merge incoming list with current list
		for key, node := range nodeList.Nodes {
			_, ok := config.NodeList.Nodes[key]
			if !ok {
				config.NodeList.Nodes[key] = node
				p := new(quibit.Peer)
				p.IP = node.IP
				p.Port = node.Port
				config.PeerQueue <- *p
				time.Sleep(time.Millisecond)
				newVer := objects.MakeFrame(objects.VERSION, objects.REQUEST, &config.LocalVersion)
				config.SendQueue <- *newVer
			} // End if
		} // End for

	}
} // End fPEER

// Handle Object Vector Requests or Replies
func fOBJ(config *ApiConfig, frame quibit.Frame, obj *objects.Obj) {
	var sending *quibit.Frame

	// Verify not objects.BROADCAST
	if frame.Header.Type == objects.BROADCAST {
		// SHUN THE NODE! SHUN IT WITH FIRE!
		config.Log <- "Node sent an obj frame as a broadcast. Disconnecting..."
		quibit.KillPeer(frame.Peer)
		return
	}

	if frame.Header.Type == objects.REQUEST {
		// If a objects.REQUEST, send local object list as objects.REPLY
		sending = objects.MakeFrame(objects.OBJ, objects.REPLY, db.ObjList())
		sending.Peer = frame.Peer
		config.SendQueue <- *sending
	}

	// For each object in object list:
	// If object not stored locally, send GETOBJ objects.REQUEST
	for _, hash := range obj.HashList {
		if db.Contains(hash) == db.NOTFOUND {
			sending = objects.MakeFrame(objects.GETOBJ, objects.REQUEST, &hash)
			sending.Peer = frame.Peer
			config.SendQueue <- *sending
		}
	}
}

// Handle Object Detail Requests
func fGETOBJ(config *ApiConfig, frame quibit.Frame, hash *objects.Hash) {
	// Verify not objects.BROADCAST
	if frame.Header.Type == objects.BROADCAST {
		// SHUN THE NODE! SHUN IT WITH FIRE!
		config.Log <- "Node sent a getobj message as a broadcast. Disconnecting..."
		quibit.KillPeer(frame.Peer)
		return
	}

	// If object stored locally, send object as a objects.REPLY
	var sending *quibit.Frame
	if frame.Header.Type == objects.REQUEST {
		switch db.Contains(*hash) {
		case db.PUBKEY:
			sending = objects.MakeFrame(objects.PUBKEY, objects.REPLY, db.GetPubkey(config.Log, *hash))
		case db.PURGE:
			sending = objects.MakeFrame(objects.PURGE, objects.REPLY, db.GetPurge(config.Log, *hash))
		case db.MSG:
			sending = objects.MakeFrame(objects.MSG, objects.REPLY, db.GetMessage(config.Log, *hash))
		case db.PUBKEYRQ:
			sending = objects.MakeFrame(objects.PUBKEY_REQUEST, objects.REPLY, hash)
		default:
			sending = objects.MakeFrame(objects.GETOBJ, objects.REPLY, new(objects.NilPayload))
		} // End switch
		sending.Peer = frame.Peer
		config.SendQueue <- *sending
	} // End if
} // End fGETOBJ

// Handle Public Key Request Broadcasts
func fPUBKEY_REQUEST(config *ApiConfig, frame quibit.Frame, pubHash *objects.Hash) {
	// Check Hash in Object List

	switch db.Contains(*pubHash) {
	// If request is Not in List, store the request
	case db.NOTFOUND:
		// If a objects.BROADCAST, send out another objects.BROADCAST
		db.Add(*pubHash, db.PUBKEYRQ)
		if frame.Header.Type == objects.BROADCAST {
			config.SendQueue <- *objects.MakeFrame(objects.PUBKEY_REQUEST, objects.BROADCAST, pubHash)
		}

	// If request is a Public Key in List:
	case db.PUBKEY:
		// Send out the PUBKEY as a objects.BROADCAST
		config.SendQueue <- *objects.MakeFrame(objects.PUBKEY, objects.BROADCAST, db.GetPubkey(config.Log, *pubHash))
	}
}

// Handle Public Key Broadcasts
func fPUBKEY(config *ApiConfig, frame quibit.Frame, pubkey *objects.EncryptedPubkey) {
	// Check Hash in Object List
	switch db.Contains(pubkey.AddrHash) {
	// If request is a Pubkey Request, remove the pubkey request
	case db.PUBKEYRQ:
		db.Delete(pubkey.AddrHash)
		fallthrough
	case db.NOTFOUND:
		// Add Pubkey to database
		err := db.AddPubkey(config.Log, *pubkey)
		if err != nil {
			config.Log <- fmt.Sprintf("Error adding pubkey to database: %s", err)
			break
		}
		// If a objects.BROADCAST, send a objects.BROADCAST
		if frame.Header.Type == objects.BROADCAST {
			config.SendQueue <- *objects.MakeFrame(objects.PUBKEY, objects.BROADCAST, pubkey)
		}

		config.PubkeyRegister <- pubkey.AddrHash
	}
} // End fPUBKEY

// Handle Encrypted Message Broadcasts
func fMSG(config *ApiConfig, frame quibit.Frame, msg *objects.Message) {
	// Check Hash in Object List
	switch db.Contains(msg.TxidHash) {
	// If Not in List, Store and objects.BROADCAST
	case db.NOTFOUND:
		err := db.AddMessage(config.Log, msg)
		if err != nil {
			config.Log <- fmt.Sprintf("Error adding message to database: %s", err)
			break
		}
		if frame.Header.Type == objects.BROADCAST {
			config.SendQueue <- *objects.MakeFrame(objects.MSG, objects.BROADCAST, msg)
		}

		config.MessageRegister <- *msg

	// If found as PURGE, reply with PURGE
	case db.PURGE:
		sending := objects.MakeFrame(objects.PURGE, objects.REPLY, db.GetPurge(config.Log, msg.TxidHash))
		sending.Peer = frame.Peer
		config.SendQueue <- *sending
	}
} // End fMSG

// Handle Purge Broadcasts
func fPURGE(config *ApiConfig, frame quibit.Frame, purge *objects.Purge) {
	var err error
	txidHash := objects.MakeHash(purge.Txid[:])

	// Check Hash in Object List
	switch db.Contains(txidHash) {
	// Delete Stored Messages
	case db.MSG:
		err = db.RemoveHash(config.Log, txidHash)
		if err != nil {
			config.Log <- fmt.Sprintf("Error removing message from database: %s", err)
			break
		}
		fallthrough
	// Add to database
	case db.NOTFOUND:
		err = db.AddPurge(config.Log, *purge)
		if err != nil {
			config.Log <- fmt.Sprintf("Error adding purge to database: ", err)
			break
		}

		// Re-objects.BROADCAST if necessary
		if frame.Header.Type == objects.BROADCAST {
			config.SendQueue <- *objects.MakeFrame(objects.PURGE, objects.BROADCAST, purge)
		}
		config.PurgeRegister <- purge.Txid
	} // End Switch
} // End fPURGE
<|endoftext|>


<commit_before>package engine

import (
    "net/http"
)

func Main() {
    go world.HandleConnections()
    http.HandleFunc("/ws", serveWs)
    http.ListenAndServe(":8080", nil)
}<commit_msg>Change webserver port to 8888<commit_after>package engine

import (
    "net/http"
)

func Main() {
    go world.HandleConnections()
    http.HandleFunc("/ws", serveWs)
    http.ListenAndServe(":8888", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"math/rand"
	"strings"

	"github.com/animenotifier/arn"

	"github.com/animenotifier/arn/search"
	"github.com/bwmarrin/discordgo"
)

// OnMessageCreate is called every time a new message is created on any channel.
func OnMessageCreate(s *discordgo.Session, msg *discordgo.MessageCreate) {
	// Ignore all messages created by the bot itself
	if msg.Author.ID == s.State.User.ID {
		return
	}

	if msg.Content == "!help" || msg.Content == "!commands" {
		s.ChannelMessageSend(msg.ChannelID, `
**!a** [anime search term]
**!animelist** [username]
**!play** [status text]
**!randomquote**
**!source**`)
	}

	// Has the bot been mentioned?
	for _, user := range msg.Mentions {
		if user.ID == discord.State.User.ID {
			s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" :heart:")
			return
		}
	}

	// Anime search
	if strings.HasPrefix(msg.Content, "!a ") {
		term := msg.Content[len("!a "):]
		animes := search.Anime(term, 3)
		message := ""

		for _, anime := range animes {
			message += "https://notify.moe" + anime.Link() + "\n"
		}

		if len(animes) == 0 {
			message = "Sorry, I couldn't find anything using that term."
		}

		s.ChannelMessageSend(msg.ChannelID, message)
		return
	}

	// Anime list of user
	if strings.HasPrefix(msg.Content, "!animelist ") {
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe/+"+strings.Split(msg.Content, " ")[1]+"/animelist")
		return
	}

	// Play status
	if strings.HasPrefix(msg.Content, "!play ") {
		s.UpdateStatus(0, msg.Content[len("!play "):])
		return
	}

	// Random quote
	if msg.Content == "!randomquote" {
		allQuotes := arn.AllQuotes()
		quote := allQuotes[rand.Intn(len(allQuotes))]
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe"+quote.Link())
		return
	}

	// GitHub source of the bot
	if msg.Content == "!source" {
		s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" B-baaaaaaaka! Y..you...you want to...TOUCH MY CODE?!\n\nhttps://github.com/animenotifier/notify.moe/tree/go/bots/discord")
		return
	}
}
<commit_msg>Fix randomquote bot command<commit_after>package main

import (
	"math/rand"
	"strings"

	"github.com/animenotifier/arn"

	"github.com/animenotifier/arn/search"
	"github.com/bwmarrin/discordgo"
)

// OnMessageCreate is called every time a new message is created on any channel.
func OnMessageCreate(s *discordgo.Session, msg *discordgo.MessageCreate) {
	// Ignore all messages created by the bot itself
	if msg.Author.ID == s.State.User.ID {
		return
	}

	if msg.Content == "!help" || msg.Content == "!commands" {
		s.ChannelMessageSend(msg.ChannelID, `
**!a** [anime search term]
**!animelist** [username]
**!play** [status text]
**!randomquote**
**!source**`)
	}

	// Has the bot been mentioned?
	for _, user := range msg.Mentions {
		if user.ID == discord.State.User.ID {
			s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" :heart:")
			return
		}
	}

	// Anime search
	if strings.HasPrefix(msg.Content, "!a ") {
		term := msg.Content[len("!a "):]
		animes := search.Anime(term, 3)
		message := ""

		for _, anime := range animes {
			message += "https://notify.moe" + anime.Link() + "\n"
		}

		if len(animes) == 0 {
			message = "Sorry, I couldn't find anything using that term."
		}

		s.ChannelMessageSend(msg.ChannelID, message)
		return
	}

	// Anime list of user
	if strings.HasPrefix(msg.Content, "!animelist ") {
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe/+"+strings.Split(msg.Content, " ")[1]+"/animelist")
		return
	}

	// Play status
	if strings.HasPrefix(msg.Content, "!play ") {
		s.UpdateStatus(0, msg.Content[len("!play "):])
		return
	}

	// Random quote
	if msg.Content == "!randomquote" {
		allQuotes := arn.FilterQuotes(func(quote *arn.Quote) bool {
			return !quote.IsDraft && quote.IsValid()
		})

		quote := allQuotes[rand.Intn(len(allQuotes))]
		s.ChannelMessageSend(msg.ChannelID, "https://notify.moe"+quote.Link())
		return
	}

	// GitHub source of the bot
	if msg.Content == "!source" {
		s.ChannelMessageSend(msg.ChannelID, msg.Author.Mention()+" B-baaaaaaaka! Y..you...you want to...TOUCH MY CODE?!\n\nhttps://github.com/animenotifier/notify.moe/tree/go/bots/discord")
		return
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"fmt"
	"net/url"
	"time"

	"github.com/authelia/authelia/internal/authentication"
	"github.com/authelia/authelia/internal/authorization"
	"github.com/authelia/authelia/internal/middlewares"
	"github.com/authelia/authelia/internal/regulation"
	"github.com/authelia/authelia/internal/session"
)

// FirstFactorPost is the handler performing the first factory.
func FirstFactorPost(ctx *middlewares.AutheliaCtx) {
	bodyJSON := firstFactorRequestBody{}
	err := ctx.ParseBody(&bodyJSON)

	if err != nil {
		ctx.Error(err, authenticationFailedMessage)
		return
	}

	bannedUntil, err := ctx.Providers.Regulator.Regulate(bodyJSON.Username)

	if err != nil {
		if err == regulation.ErrUserIsBanned {
			ctx.Error(fmt.Errorf("User %s is banned until %s", bodyJSON.Username, bannedUntil), userBannedMessage)
			return
		}
		ctx.Error(fmt.Errorf("Unable to regulate authentication: %s", err), authenticationFailedMessage)
		return
	}

	userPasswordOk, err := ctx.Providers.UserProvider.CheckUserPassword(bodyJSON.Username, bodyJSON.Password)

	if err != nil {
		ctx.Logger.Debugf("Mark authentication attempt made by user %s", bodyJSON.Username)
		ctx.Providers.Regulator.Mark(bodyJSON.Username, false)

		ctx.Error(fmt.Errorf("Error while checking password for user %s: %s", bodyJSON.Username, err.Error()), authenticationFailedMessage)
		return
	}

	if !userPasswordOk {
		ctx.Logger.Debugf("Mark authentication attempt made by user %s", bodyJSON.Username)
		ctx.Providers.Regulator.Mark(bodyJSON.Username, false)

		ctx.ReplyError(fmt.Errorf("Credentials are wrong for user %s", bodyJSON.Username), authenticationFailedMessage)
		return
	}

	ctx.Logger.Debugf("Credentials validation of user %s is ok", bodyJSON.Username)

	ctx.Logger.Debugf("Mark authentication attempt made by user %s", bodyJSON.Username)
	err = ctx.Providers.Regulator.Mark(bodyJSON.Username, true)

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to mark authentication: %s", err), authenticationFailedMessage)
		return
	}

	// Reset all values from previous session before regenerating the cookie.
	err = ctx.SaveSession(session.NewDefaultUserSession())

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to reset the session for user %s: %s", bodyJSON.Username, err), authenticationFailedMessage)
		return
	}

	err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx)

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to regenerate session for user %s: %s", bodyJSON.Username, err), authenticationFailedMessage)
		return
	}

	// and avoid the cookie to expire if "Remember me" was ticked.
	if *bodyJSON.KeepMeLoggedIn {
		err = ctx.Providers.SessionProvider.UpdateExpiration(ctx.RequestCtx, time.Duration(0))
		if err != nil {
			ctx.Error(fmt.Errorf("Unable to update expiration timer for user %s: %s", bodyJSON.Username, err), authenticationFailedMessage)
			return
		}
	}

	// Get the details of the given user from the user provider.
	userDetails, err := ctx.Providers.UserProvider.GetDetails(bodyJSON.Username)

	if err != nil {
		ctx.Error(fmt.Errorf("Error while retrieving details from user %s: %s", bodyJSON.Username, err.Error()), authenticationFailedMessage)
		return
	}

	ctx.Logger.Tracef("Details for user %s => groups: %s, emails %s", bodyJSON.Username, userDetails.Groups, userDetails.Emails)

	// And set those information in the new session.
	userSession := ctx.GetSession()
	userSession.Username = bodyJSON.Username
	userSession.Groups = userDetails.Groups
	userSession.Emails = userDetails.Emails
	userSession.AuthenticationLevel = authentication.OneFactor
	userSession.LastActivity = time.Now().Unix()
	userSession.KeepMeLoggedIn = *bodyJSON.KeepMeLoggedIn
	err = ctx.SaveSession(userSession)

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to save session of user %s", bodyJSON.Username), authenticationFailedMessage)
		return
	}

	if bodyJSON.TargetURL != "" {
		targetURL, err := url.ParseRequestURI(bodyJSON.TargetURL)
		if err != nil {
			ctx.Error(fmt.Errorf("Unable to parse target URL %s: %s", bodyJSON.TargetURL, err), authenticationFailedMessage)
			return
		}
		requiredLevel := ctx.Providers.Authorizer.GetRequiredLevel(authorization.Subject{
			Username: userSession.Username,
			Groups:   userSession.Groups,
			IP:       ctx.RemoteIP(),
		}, *targetURL)

		ctx.Logger.Debugf("Required level for the URL %s is %d", targetURL.String(), requiredLevel)

		safeRedirection := isRedirectionSafe(*targetURL, ctx.Configuration.Session.Domain)

		if safeRedirection && requiredLevel <= authorization.OneFactor {
			ctx.Logger.Debugf("Redirection is safe, redirecting...")
			response := redirectResponse{bodyJSON.TargetURL}
			ctx.SetJSONBody(response)
		} else {
			ctx.ReplyOK()
		}
	} else {
		ctx.ReplyOK()
	}
}
<commit_msg>Fixes Remember Me functionality<commit_after>package handlers

import (
	"fmt"
	"net/url"
	"time"

	"github.com/authelia/authelia/internal/authentication"
	"github.com/authelia/authelia/internal/authorization"
	"github.com/authelia/authelia/internal/middlewares"
	"github.com/authelia/authelia/internal/regulation"
	"github.com/authelia/authelia/internal/session"
)

// FirstFactorPost is the handler performing the first factory.
func FirstFactorPost(ctx *middlewares.AutheliaCtx) {
	bodyJSON := firstFactorRequestBody{}
	err := ctx.ParseBody(&bodyJSON)

	if err != nil {
		ctx.Error(err, authenticationFailedMessage)
		return
	}

	bannedUntil, err := ctx.Providers.Regulator.Regulate(bodyJSON.Username)

	if err != nil {
		if err == regulation.ErrUserIsBanned {
			ctx.Error(fmt.Errorf("User %s is banned until %s", bodyJSON.Username, bannedUntil), userBannedMessage)
			return
		}
		ctx.Error(fmt.Errorf("Unable to regulate authentication: %s", err), authenticationFailedMessage)
		return
	}

	userPasswordOk, err := ctx.Providers.UserProvider.CheckUserPassword(bodyJSON.Username, bodyJSON.Password)

	if err != nil {
		ctx.Logger.Debugf("Mark authentication attempt made by user %s", bodyJSON.Username)
		ctx.Providers.Regulator.Mark(bodyJSON.Username, false)

		ctx.Error(fmt.Errorf("Error while checking password for user %s: %s", bodyJSON.Username, err.Error()), authenticationFailedMessage)
		return
	}

	if !userPasswordOk {
		ctx.Logger.Debugf("Mark authentication attempt made by user %s", bodyJSON.Username)
		ctx.Providers.Regulator.Mark(bodyJSON.Username, false)

		ctx.ReplyError(fmt.Errorf("Credentials are wrong for user %s", bodyJSON.Username), authenticationFailedMessage)
		return
	}

	ctx.Logger.Debugf("Credentials validation of user %s is ok", bodyJSON.Username)

	ctx.Logger.Debugf("Mark authentication attempt made by user %s", bodyJSON.Username)
	err = ctx.Providers.Regulator.Mark(bodyJSON.Username, true)

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to mark authentication: %s", err), authenticationFailedMessage)
		return
	}

	// Reset all values from previous session before regenerating the cookie.
	err = ctx.SaveSession(session.NewDefaultUserSession())

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to reset the session for user %s: %s", bodyJSON.Username, err), authenticationFailedMessage)
		return
	}

	err = ctx.Providers.SessionProvider.RegenerateSession(ctx.RequestCtx)

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to regenerate session for user %s: %s", bodyJSON.Username, err), authenticationFailedMessage)
		return
	}

	// set the cookie to expire in 1 year if "Remember me" was ticked.
	if *bodyJSON.KeepMeLoggedIn {
		err = ctx.Providers.SessionProvider.UpdateExpiration(ctx.RequestCtx, time.Duration(31556952 * time.Second))
		if err != nil {
			ctx.Error(fmt.Errorf("Unable to update expiration timer for user %s: %s", bodyJSON.Username, err), authenticationFailedMessage)
			return
		}
	}

	// Get the details of the given user from the user provider.
	userDetails, err := ctx.Providers.UserProvider.GetDetails(bodyJSON.Username)

	if err != nil {
		ctx.Error(fmt.Errorf("Error while retrieving details from user %s: %s", bodyJSON.Username, err.Error()), authenticationFailedMessage)
		return
	}

	ctx.Logger.Tracef("Details for user %s => groups: %s, emails %s", bodyJSON.Username, userDetails.Groups, userDetails.Emails)

	// And set those information in the new session.
	userSession := ctx.GetSession()
	userSession.Username = bodyJSON.Username
	userSession.Groups = userDetails.Groups
	userSession.Emails = userDetails.Emails
	userSession.AuthenticationLevel = authentication.OneFactor
	userSession.LastActivity = time.Now().Unix()
	userSession.KeepMeLoggedIn = *bodyJSON.KeepMeLoggedIn
	err = ctx.SaveSession(userSession)

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to save session of user %s", bodyJSON.Username), authenticationFailedMessage)
		return
	}

	if bodyJSON.TargetURL != "" {
		targetURL, err := url.ParseRequestURI(bodyJSON.TargetURL)
		if err != nil {
			ctx.Error(fmt.Errorf("Unable to parse target URL %s: %s", bodyJSON.TargetURL, err), authenticationFailedMessage)
			return
		}
		requiredLevel := ctx.Providers.Authorizer.GetRequiredLevel(authorization.Subject{
			Username: userSession.Username,
			Groups:   userSession.Groups,
			IP:       ctx.RemoteIP(),
		}, *targetURL)

		ctx.Logger.Debugf("Required level for the URL %s is %d", targetURL.String(), requiredLevel)

		safeRedirection := isRedirectionSafe(*targetURL, ctx.Configuration.Session.Domain)

		if safeRedirection && requiredLevel <= authorization.OneFactor {
			ctx.Logger.Debugf("Redirection is safe, redirecting...")
			response := redirectResponse{bodyJSON.TargetURL}
			ctx.SetJSONBody(response)
		} else {
			ctx.ReplyOK()
		}
	} else {
		ctx.ReplyOK()
	}
}
<|endoftext|>


<commit_before>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2020] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package bbolt_test

import (
	"github.com/lastbackend/lastbackend/internal/pkg/storage"
	"github.com/lastbackend/lastbackend/internal/pkg/storage/bbolt"
	"os"
	"path"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestStorage_Get(t *testing.T) {
	stg, err := bbolt.New(bbolt.Options{Path: path.Join(os.TempDir(), ".lb_test")})
	assert.NoError(t, err, "storage initialize err")
	storage.StorageGetAssets(t, stg)
}

func TestStorage_List(t *testing.T) {
	stg, err := bbolt.New(bbolt.Options{Path: path.Join(os.TempDir(), ".lb_test")})
	assert.NoError(t, err, "storage initialize err")
	storage.StorageListAssets(t, stg)
}

func TestStorage_Put(t *testing.T) {
	stg, err := bbolt.New(bbolt.Options{Path: path.Join(os.TempDir(), ".lb_test")})
	assert.NoError(t, err, "storage initialize err")
	storage.StoragePutAssets(t, stg)
}

func TestStorage_Set(t *testing.T) {
	stg, err := bbolt.New(bbolt.Options{Path: path.Join(os.TempDir(), ".lb_test")})
	assert.NoError(t, err, "storage initialize err")
	storage.StorageSetAssets(t, stg)
}

func TestStorage_Del(t *testing.T) {
	stg, err := bbolt.New(bbolt.Options{Path: path.Join(os.TempDir(), ".lb_test")})
	assert.NoError(t, err, "storage initialize err")
	storage.StorageDelAssets(t, stg)
}
<commit_msg>update storage test<commit_after>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2020] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package bbolt_test

import (
	"github.com/lastbackend/lastbackend/internal/pkg/storage"
	"github.com/lastbackend/lastbackend/internal/pkg/storage/bbolt"
	"os"
	"path"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestStorage_Get(t *testing.T) {
	stg, err := bbolt.New(bbolt.Options{Path: path.Join(os.TempDir(), ".lb_test")})
	assert.NoError(t, err, "storage initialize err")
	storage.StorageGetAssets(t, stg)
	stg.Close()
}

func TestStorage_List(t *testing.T) {
	stg, err := bbolt.New(bbolt.Options{Path: path.Join(os.TempDir(), ".lb_test")})
	assert.NoError(t, err, "storage initialize err")
	storage.StorageListAssets(t, stg)
	stg.Close()
}

func TestStorage_Put(t *testing.T) {
	stg, err := bbolt.New(bbolt.Options{Path: path.Join(os.TempDir(), ".lb_test")})
	assert.NoError(t, err, "storage initialize err")
	storage.StoragePutAssets(t, stg)
	stg.Close()
}

func TestStorage_Set(t *testing.T) {
	stg, err := bbolt.New(bbolt.Options{Path: path.Join(os.TempDir(), ".lb_test")})
	assert.NoError(t, err, "storage initialize err")
	storage.StorageSetAssets(t, stg)
	stg.Close()
}

func TestStorage_Del(t *testing.T) {
	stg, err := bbolt.New(bbolt.Options{Path: path.Join(os.TempDir(), ".lb_test")})
	assert.NoError(t, err, "storage initialize err")
	storage.StorageDelAssets(t, stg)
	stg.Close()
}
<|endoftext|>


<commit_before>// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 对象复用池.
package gpool

import (
    "time"
    "errors"
    "gitee.com/johng/gf/g/os/gtime"
    "gitee.com/johng/gf/g/container/glist"
    "gitee.com/johng/gf/g/container/gtype"
    "math"
)

// 对象池
type Pool struct {
    list       *glist.List                // 可用/闲置的文件指针链表
    closed     *gtype.Bool                // 连接池是否已关闭
    Expire     int64                      // (毫秒)闲置最大时间，超过该时间则被系统回收
    NewFunc    func()(interface{}, error) // 创建对象的方法定义
    ExpireFunc func(interface{})          // 对象的过期销毁方法(当池对象销毁需要执行额外的销毁操作时，需要定义该方法)
                                          // 例如: net.Conn, os.File等对象都需要执行额外关闭操作
}

// 对象池数据项
type poolItem struct {
    expire int64               // (毫秒)过期时间
    value  interface{}         // 对象值
}

// 创建一个对象池，为保证执行效率，过期时间一旦设定之后无法修改
// expire = 0表示不过期，expire < 0表示使用完立即回收，expire > 0表示超时回收
// 注意过期时间单位为**毫秒**
func New(expire int, newFunc...func() (interface{}, error)) *Pool {
    if expire == 0 {
        expire = math.MaxInt64
    }
    r := &Pool {
        list    : glist.New(),
        closed  : gtype.NewBool(),
        Expire  : int64(expire),
    }
    if len(newFunc) > 0 {
        r.NewFunc = newFunc[0]
    }
    go r.expireCheckingLoop()
    return r
}

// 设置对象过期销毁时的关闭方法
func (p *Pool) SetExpireFunc(expireFunc func(interface{})) {
    p.ExpireFunc = expireFunc
}

// 放一个临时对象到池中
func (p *Pool) Put(item interface{}) {
    p.list.PushBack(&poolItem {
        expire : gtime.Millisecond() + p.Expire,
        value  : item,
    })
}

// 从池中获得一个临时对象
func (p *Pool) Get() (interface{}, error) {
    for !p.closed.Val() {
        if r := p.list.PopFront(); r != nil {
            f := r.(*poolItem)
            if f.expire > gtime.Millisecond() {
                return f.value, nil
            }
        } else {
            break
        }
    }
    if p.NewFunc != nil {
        return p.NewFunc()
    }
    return nil, errors.New("pool is empty")
}

// 查询当前池中的对象数量
func (p *Pool) Size() int {
    return p.list.Len()
}

// 关闭池
func (p *Pool) Close() {
    p.closed.Set(true)
}

// 超时检测循环
func (p *Pool) expireCheckingLoop() {
    for !p.closed.Val() {
        for {
            if r := p.list.PopFront(); r != nil {
                item := r.(*poolItem)
                if item.expire > gtime.Millisecond() {
                    p.list.PushFront(item)
                    break
                }
                if p.ExpireFunc != nil {
                    p.ExpireFunc(item.value)
                }
            }
        }
        time.Sleep(time.Second)
    }
}<commit_msg>修复gpool过期处理异步循环的CPU资源占用问题<commit_after>// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 对象复用池.
package gpool

import (
    "time"
    "errors"
    "gitee.com/johng/gf/g/os/gtime"
    "gitee.com/johng/gf/g/container/glist"
    "gitee.com/johng/gf/g/container/gtype"
    "math"
)

// 对象池
type Pool struct {
    list       *glist.List                // 可用/闲置的文件指针链表
    closed     *gtype.Bool                // 连接池是否已关闭
    Expire     int64                      // (毫秒)闲置最大时间，超过该时间则被系统回收
    NewFunc    func()(interface{}, error) // 创建对象的方法定义
    ExpireFunc func(interface{})          // 对象的过期销毁方法(当池对象销毁需要执行额外的销毁操作时，需要定义该方法)
                                          // 例如: net.Conn, os.File等对象都需要执行额外关闭操作
}

// 对象池数据项
type poolItem struct {
    expire int64               // (毫秒)过期时间
    value  interface{}         // 对象值
}

// 创建一个对象池，为保证执行效率，过期时间一旦设定之后无法修改
// expire = 0表示不过期，expire < 0表示使用完立即回收，expire > 0表示超时回收
// 注意过期时间单位为**毫秒**
func New(expire int, newFunc...func() (interface{}, error)) *Pool {
    if expire == 0 {
        expire = math.MaxInt64
    }
    r := &Pool {
        list    : glist.New(),
        closed  : gtype.NewBool(),
        Expire  : int64(expire),
    }
    if len(newFunc) > 0 {
        r.NewFunc = newFunc[0]
    }
    go r.expireCheckingLoop()
    return r
}

// 设置对象过期销毁时的关闭方法
func (p *Pool) SetExpireFunc(expireFunc func(interface{})) {
    p.ExpireFunc = expireFunc
}

// 放一个临时对象到池中
func (p *Pool) Put(item interface{}) {
    p.list.PushBack(&poolItem {
        expire : gtime.Millisecond() + p.Expire,
        value  : item,
    })
}

// 从池中获得一个临时对象
func (p *Pool) Get() (interface{}, error) {
    for !p.closed.Val() {
        if r := p.list.PopFront(); r != nil {
            f := r.(*poolItem)
            if f.expire > gtime.Millisecond() {
                return f.value, nil
            }
        } else {
            break
        }
    }
    if p.NewFunc != nil {
        return p.NewFunc()
    }
    return nil, errors.New("pool is empty")
}

// 查询当前池中的对象数量
func (p *Pool) Size() int {
    return p.list.Len()
}

// 关闭池
func (p *Pool) Close() {
    p.closed.Set(true)
}

// 超时检测循环
func (p *Pool) expireCheckingLoop() {
    for !p.closed.Val() {
        for {
            if r := p.list.PopFront(); r != nil {
                item := r.(*poolItem)
                if item.expire > gtime.Millisecond() {
                    p.list.PushFront(item)
                    break
                }
                if p.ExpireFunc != nil {
                    p.ExpireFunc(item.value)
                }
            } else {
                break
            }
        }
        time.Sleep(time.Second)
    }
}<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/jingweno/jqplay/config"
	"github.com/jingweno/jqplay/jq"
	"golang.org/x/net/context"
	"gopkg.in/gin-gonic/gin.v1"
)

const (
	JSONPayloadLimit   = JSONPayloadLimitMB * OneMB
	JSONPayloadLimitMB = 10
	OneMB              = 1024000
)

type JQHandlerContext struct {
	*config.Config
	JQ string
}

func (c *JQHandlerContext) Asset(path string) string {
	return fmt.Sprintf("%s/%s", c.AssetHost, path)
}

func (c *JQHandlerContext) ShouldInitJQ() bool {
	return c.JQ != ""
}

type JQHandler struct {
	Config *config.Config
}

func (h *JQHandler) handleIndex(c *gin.Context) {
	c.HTML(200, "index.tmpl", &JQHandlerContext{Config: h.Config})
}

func (h *JQHandler) handleJqPost(c *gin.Context) {
	l, _ := c.Get("logger")
	logger := l.(*logrus.Entry)

	if c.Request.ContentLength > JSONPayloadLimit {
		size := float64(c.Request.ContentLength) / OneMB
		err := fmt.Errorf("JSON payload size is %.1fMB, larger than limit %dMB.", size, JSONPayloadLimitMB)
		logger.WithError(err).WithField("size", size).Infof(err.Error())
		c.String(http.StatusExpectationFailed, err.Error())
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 3*time.Second)
	defer cancel()

	c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, JSONPayloadLimit)

	var jq *jq.JQ
	err := c.BindJSON(&jq)
	if err != nil {
		err = fmt.Errorf("error parsing JSON: %s", err)
		logger.WithError(err).Infof("error parsing JSON: %s", err)
		c.String(422, err.Error())
		return
	}

	// Evaling into ResponseWriter sets the status code to 200
	// appending error message in the end if there's any
	err = jq.Eval(ctx, c.Writer)
	if err != nil {
		logger.WithError(err).Infof("jq error: %s", err)
		fmt.Fprint(c.Writer, err.Error())
	}
}

func (h *JQHandler) handleJqGet(c *gin.Context) {
	jq := &jq.JQ{
		J: c.Query("j"),
		Q: c.Query("q"),
	}

	var jqData string
	if err := jq.Validate(); err == nil {
		d, err := json.Marshal(jq)
		if err == nil {
			jqData = string(d)
		}
	}

	c.HTML(200, "index.tmpl", &JQHandlerContext{Config: h.Config, JQ: jqData})
}
<commit_msg>Clean up logging<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/jingweno/jqplay/config"
	"github.com/jingweno/jqplay/jq"
	"golang.org/x/net/context"
	"gopkg.in/gin-gonic/gin.v1"
)

const (
	JSONPayloadLimit   = JSONPayloadLimitMB * OneMB
	JSONPayloadLimitMB = 10
	OneMB              = 1024000
)

type JQHandlerContext struct {
	*config.Config
	JQ string
}

func (c *JQHandlerContext) Asset(path string) string {
	return fmt.Sprintf("%s/%s", c.AssetHost, path)
}

func (c *JQHandlerContext) ShouldInitJQ() bool {
	return c.JQ != ""
}

type JQHandler struct {
	Config *config.Config
}

func (h *JQHandler) handleIndex(c *gin.Context) {
	c.HTML(200, "index.tmpl", &JQHandlerContext{Config: h.Config})
}

func (h *JQHandler) handleJqPost(c *gin.Context) {
	l, _ := c.Get("logger")
	logger := l.(*logrus.Entry)

	if c.Request.ContentLength > JSONPayloadLimit {
		size := float64(c.Request.ContentLength) / OneMB
		err := fmt.Errorf("JSON payload size is %.1fMB, larger than limit %dMB.", size, JSONPayloadLimitMB)
		logger.WithField("size", size).WithError(err)
		c.String(http.StatusExpectationFailed, err.Error())
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 3*time.Second)
	defer cancel()

	c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, JSONPayloadLimit)

	var jq *jq.JQ
	err := c.BindJSON(&jq)
	if err != nil {
		err = fmt.Errorf("error parsing JSON: %s", err)
		logger.WithError(err).Infof("error parsing JSON")
		c.String(422, err.Error())
		return
	}

	// Evaling into ResponseWriter sets the status code to 200
	// appending error message in the end if there's any
	err = jq.Eval(ctx, c.Writer)
	if err != nil {
		logger.WithError(err).Infof("jq error")
		fmt.Fprint(c.Writer, err.Error())
	}
}

func (h *JQHandler) handleJqGet(c *gin.Context) {
	jq := &jq.JQ{
		J: c.Query("j"),
		Q: c.Query("q"),
	}

	var jqData string
	if err := jq.Validate(); err == nil {
		d, err := json.Marshal(jq)
		if err == nil {
			jqData = string(d)
		}
	}

	c.HTML(200, "index.tmpl", &JQHandlerContext{Config: h.Config, JQ: jqData})
}
<|endoftext|>