<commit_before>/*
The statsd package provides a Statsd client. It supports all commands supported
by the Etsy statsd server implementation and automatically buffers stats into
512 byte packets.
*/
package statsd

import (
	"bytes"
	"fmt"
	"math/rand"
	"net"
	"regexp"
	"strconv"
	"sync"
	"time"
)

var (
	nonAlphaNum = regexp.MustCompile(`[^\w]+`)
)

type Client interface {
	Flush() error
	Count(bucket string, value float64, sampleRate float64)
	Gauge(bucket string, value float64)
	Timing(bucket string, value time.Duration)
	CountUnique(bucket string, value string)
}

type statsdClient struct {
	// Maximum size of sent UDP packets, in bytes. A value of 0 or less will
	// cause all stats to be sent immediately.
	PacketSize int

	// Prefix for all metric names. If non-blank, this should include the
	// trailing period.
	prefix string

	// UDP connection to Statsd
	conn net.Conn

	// Wrap buffer writes with a mutex to prevent goroutines from stomping on
	// each other.
	mutex sync.Mutex

	// Buffer metrics up to a certain buffer size here.
	buffer bytes.Buffer
}

// -- emptyClient

type emptyClient struct{}

func (c emptyClient) Flush() error                   { return nil }
func (c emptyClient) Count(string, float64, float64) {}
func (c emptyClient) Gauge(string, float64)          {}
func (c emptyClient) Timing(string, time.Duration)   {}
func (c emptyClient) CountUnique(string, string)     {}

// -- Client

// New is the same as calling NewWithPacketSize with a 512 byte packet size.
func New(statsdUrl string) (Client, error) {
	return NewWithPacketSize(statsdUrl, 512)
}

// NewWithPacketSize creates a new Client that will direct stats to a Statsd
// server. If the given URL has a path component (eg. "/my.prefix"), all metric
// names will be prepended with that prefix.
//
// The packet size parameter is the maximum size (in bytes) that will be
// buffered before being sent. A value of 0 or less will cause each stat to be
// sent immediately, as it is received.
//
// If there is an error resolving the host, NewWithPacketSize will return an
// error as well as a no-op StatsReporter so that code mixed with statsd calls
// can continue to run without errors.
func NewWithPacketSize(statsdUrl string, packetSize int) (Client, error) {
	// Seed random number generator for dealing with sample rates.
	rand.Seed(time.Now().UnixNano())

	host, prefix, err := parseUrl(statsdUrl)
	connection, err := net.DialTimeout("udp", host, time.Second)
	if err != nil {
		return &emptyClient{}, err
	}

	return &statsdClient{
		PacketSize: packetSize,
		conn:       connection,
		prefix:     prefix,
	}, nil
}

func (c *statsdClient) record(sampleRate float64, bucket, value, kind string) {
	if sampleRate < 1 && sampleRate <= rand.Float64() {
		return
	}

	suffix := ""
	if sampleRate != 1 {
		suffix = fmt.Sprintf("|@%g", sampleRate)
	}

	c.send(fmt.Sprintf("%s%s:%s|%s%s", c.prefix, bucket, value, kind, suffix))
}

func (c *statsdClient) send(data string) error {
	if c.PacketSize <= 0 {
		c.writeToBuffer(data)
		c.Flush()
	} else {
		if c.buffer.Len()+len(data)+1 > c.PacketSize {
			err := c.Flush()
			if err != nil {
				return err
			}
		}
		c.writeToBuffer(data)
	}

	return nil
}

func (c *statsdClient) writeToBuffer(data string) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if c.buffer.Len() > 0 {
		c.buffer.WriteRune('\n')
	}
	c.buffer.WriteString(data)
}

// Flush sends all buffered data to the statsd server, if there is any in the
// buffer.
func (c *statsdClient) Flush() error {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if c.buffer.Len() > 0 {
		_, err := c.conn.Write(c.buffer.Bytes())
		c.buffer.Reset()
		if err != nil {
			return err
		}
	}
	return nil
}

// Gauge sets an arbitrary value. Only the value of the gauge at flush time is
// stored by statsd.
func (c *statsdClient) Gauge(bucket string, value float64) {
	valueString := strconv.FormatFloat(value, 'f', -1, 64)
	c.record(1, bucket, valueString, "g")
}

// Count increments (or decrements) the value in a counter. Counters are
// recorded and then reset to 0 when Statsd flushes.
func (c *statsdClient) Count(bucket string, value float64, sampleRate float64) {
	valueString := strconv.FormatFloat(value, 'f', -1, 64)
	c.record(sampleRate, bucket, valueString, "c")
}

// Timing records a time interval (in milliseconds). The percentiles, mean,
// standard deviation, sum, and lower and upper bounds are calculated by the
// Statsd server.
func (c *statsdClient) Timing(bucket string, value time.Duration) {
	valueString := strconv.FormatFloat(float64(value/time.Millisecond), 'f', -1, 64)
	c.record(1, bucket, valueString, "ms")
}

// Unique records the number of unique values received between flushes using
// Statsd Sets.
func (c *statsdClient) CountUnique(bucket string, value string) {
	cleanValue := nonAlphaNum.ReplaceAllString(value, "_")
	c.record(1, bucket, cleanValue, "s")
}
<commit_msg>Don't lock buffer if we don't need to.<commit_after>/*
The statsd package provides a Statsd client. It supports all commands supported
by the Etsy statsd server implementation and automatically buffers stats into
512 byte packets.
*/
package statsd

import (
	"bytes"
	"fmt"
	"math/rand"
	"net"
	"regexp"
	"strconv"
	"sync"
	"time"
)

var (
	nonAlphaNum = regexp.MustCompile(`[^\w]+`)
)

type Client interface {
	Flush() error
	Count(bucket string, value float64, sampleRate float64)
	Gauge(bucket string, value float64)
	Timing(bucket string, value time.Duration)
	CountUnique(bucket string, value string)
}

type statsdClient struct {
	// Maximum size of sent UDP packets, in bytes. A value of 0 or less will
	// cause all stats to be sent immediately.
	PacketSize int

	// Prefix for all metric names. If non-blank, this should include the
	// trailing period.
	prefix string

	// UDP connection to Statsd
	conn net.Conn

	// Wrap buffer writes with a mutex to prevent goroutines from stomping on
	// each other.
	mutex sync.Mutex

	// Buffer metrics up to a certain buffer size here.
	buffer bytes.Buffer
}

// -- emptyClient

type emptyClient struct{}

func (c emptyClient) Flush() error                   { return nil }
func (c emptyClient) Count(string, float64, float64) {}
func (c emptyClient) Gauge(string, float64)          {}
func (c emptyClient) Timing(string, time.Duration)   {}
func (c emptyClient) CountUnique(string, string)     {}

// -- Client

// New is the same as calling NewWithPacketSize with a 512 byte packet size.
func New(statsdUrl string) (Client, error) {
	return NewWithPacketSize(statsdUrl, 512)
}

// NewWithPacketSize creates a new Client that will direct stats to a Statsd
// server. If the given URL has a path component (eg. "/my.prefix"), all metric
// names will be prepended with that prefix.
//
// The packet size parameter is the maximum size (in bytes) that will be
// buffered before being sent. A value of 0 or less will cause each stat to be
// sent immediately, as it is received.
//
// If there is an error resolving the host, NewWithPacketSize will return an
// error as well as a no-op StatsReporter so that code mixed with statsd calls
// can continue to run without errors.
func NewWithPacketSize(statsdUrl string, packetSize int) (Client, error) {
	// Seed random number generator for dealing with sample rates.
	rand.Seed(time.Now().UnixNano())

	host, prefix, err := parseUrl(statsdUrl)
	connection, err := net.DialTimeout("udp", host, time.Second)
	if err != nil {
		return &emptyClient{}, err
	}

	return &statsdClient{
		PacketSize: packetSize,
		conn:       connection,
		prefix:     prefix,
	}, nil
}

func (c *statsdClient) record(sampleRate float64, bucket, value, kind string) {
	if sampleRate < 1 && sampleRate <= rand.Float64() {
		return
	}

	suffix := ""
	if sampleRate != 1 {
		suffix = fmt.Sprintf("|@%g", sampleRate)
	}

	c.send(fmt.Sprintf("%s%s:%s|%s%s", c.prefix, bucket, value, kind, suffix))
}

func (c *statsdClient) send(data string) error {
	if c.PacketSize <= 0 {
		c.writeToBuffer(data)
		c.Flush()
	} else {
		if c.buffer.Len()+len(data)+1 > c.PacketSize {
			err := c.Flush()
			if err != nil {
				return err
			}
		}
		c.writeToBuffer(data)
	}

	return nil
}

func (c *statsdClient) writeToBuffer(data string) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if c.buffer.Len() > 0 {
		c.buffer.WriteRune('\n')
	}
	c.buffer.WriteString(data)
}

// Flush sends all buffered data to the statsd server, if there is any in the
// buffer.
func (c *statsdClient) Flush() error {
	if c.buffer.Len() > 0 {
		c.mutex.Lock()
		defer c.mutex.Unlock()

		_, err := c.conn.Write(c.buffer.Bytes())
		c.buffer.Reset()
		if err != nil {
			return err
		}
	}

	return nil
}

// Gauge sets an arbitrary value. Only the value of the gauge at flush time is
// stored by statsd.
func (c *statsdClient) Gauge(bucket string, value float64) {
	valueString := strconv.FormatFloat(value, 'f', -1, 64)
	c.record(1, bucket, valueString, "g")
}

// Count increments (or decrements) the value in a counter. Counters are
// recorded and then reset to 0 when Statsd flushes.
func (c *statsdClient) Count(bucket string, value float64, sampleRate float64) {
	valueString := strconv.FormatFloat(value, 'f', -1, 64)
	c.record(sampleRate, bucket, valueString, "c")
}

// Timing records a time interval (in milliseconds). The percentiles, mean,
// standard deviation, sum, and lower and upper bounds are calculated by the
// Statsd server.
func (c *statsdClient) Timing(bucket string, value time.Duration) {
	valueString := strconv.FormatFloat(float64(value/time.Millisecond), 'f', -1, 64)
	c.record(1, bucket, valueString, "ms")
}

// Unique records the number of unique values received between flushes using
// Statsd Sets.
func (c *statsdClient) CountUnique(bucket string, value string) {
	cleanValue := nonAlphaNum.ReplaceAllString(value, "_")
	c.record(1, bucket, cleanValue, "s")
}
<|endoftext|>


<commit_before>package project

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/fische/gaoler/project/dependency"
)

func CleanVendor(vendorPath string, dependencies []*dependency.Dependency) error {
	for _, dep := range dependencies {
		root := filepath.Clean(fmt.Sprintf("%s/%s/", vendorPath, dep.RootPackage))
		err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if info.IsDir() {
				rel, err := filepath.Rel(vendorPath, path)
				if err != nil {
					return err
				}
				if !(dep.HasPackage(rel)) && rel != dep.RootPackage {
					err := os.RemoveAll(path)
					if err != nil {
						return err
					}
					return filepath.SkipDir
				}
				//TODO Remove files from root
			}
			return nil
		})
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg><commit_after>package project

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/fische/gaoler/project/dependency"
)

func CleanVendor(vendorPath string, dependencies []*dependency.Dependency) error {
	for _, dep := range dependencies {
		root := filepath.Clean(fmt.Sprintf("%s/%s/", vendorPath, dep.RootPackage))
		removeRootFiles := !dep.HasPackage(dep.RootPackage)
		err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if info.IsDir() {
				var rel string
				rel, err = filepath.Rel(vendorPath, path)
				if err != nil {
					return err
				}
				if rel != dep.RootPackage && !dep.HasPackage(rel) {
					err = os.RemoveAll(path)
					if err != nil {
						return err
					}
					return filepath.SkipDir
				}
			} else if removeRootFiles && path == filepath.Clean(fmt.Sprintf("%s/%s", root, info.Name())) {
				err = os.Remove(path)
				if err != nil {
					return err
				}
			}
			return nil
		})
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


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

package stun

import (
	"errors"
	"net"
)

// Padding the length of the byte slice to multiple of 4.
func padding(bytes []byte) []byte {
	length := uint16(len(bytes))
	return append(bytes, make([]byte, align(length)-length)...)
}

// Align the uint16 number to the smallest multiple of 4, which is larger than
// or equal to the uint16 number.
func align(n uint16) uint16 {
	return (n + 3) & 0xfffc
}

func sendBindingReq(serverAddr string) (*packet, string, error) {
	conn, err := net.Dial("udp", serverAddr)
	if err != nil {
		return nil, "", err
	}
	// Construct packet.
	packet := newPacket()
	packet.types = type_BINDING_REQUEST
	attribute := newSoftwareAttribute(packet, DefaultSoftwareName)
	packet.addAttribute(*attribute)
	attribute = newFingerprintAttribute(packet)
	packet.addAttribute(*attribute)
	// Send packet.
	localAddr := conn.LocalAddr().String()
	packet, err = packet.send(conn)
	if err != nil {
		return nil, "", err
	}
	err = conn.Close()
	return packet, localAddr, err
}

func sendChangeReq(serverAddr string, changeIP bool, changePort bool) (*packet, error) {
	conn, err := net.Dial("udp", serverAddr)
	if err != nil {
		return nil, err
	}
	// Construct packet.
	packet := newPacket()
	packet.types = type_BINDING_REQUEST
	attribute := newSoftwareAttribute(packet, DefaultSoftwareName)
	packet.addAttribute(*attribute)
	attribute = newChangeReqAttribute(packet, changeIP, changePort)
	packet.addAttribute(*attribute)
	attribute = newFingerprintAttribute(packet)
	packet.addAttribute(*attribute)
	// Send packet.
	packet, err = packet.send(conn)
	if err != nil {
		return nil, err
	}
	err = conn.Close()
	return packet, err
}

func test1(serverAddr string) (*packet, string, bool, *Host, error) {
	packet, localAddr, err := sendBindingReq(serverAddr)
	if err != nil {
		return nil, "", false, nil, err
	}
	if packet == nil {
		return nil, "", false, nil, nil
	}

	// RFC 3489 doesn't require the server return XOR mapped address.
	hostMappedAddr := packet.xorMappedAddr()
	if hostMappedAddr == nil {
		hostMappedAddr = packet.mappedAddr()
		if hostMappedAddr == nil {
			return nil, "", false, nil, errors.New("No mapped address.")
		}
	}

	hostChangedAddr := packet.changedAddr()
	if hostChangedAddr == nil {
		return nil, "", false, nil, errors.New("No changed address.")
	}
	changeAddr := hostChangedAddr.TransportAddr()
	identical := localAddr == hostMappedAddr.TransportAddr()
	return packet, changeAddr, identical, hostMappedAddr, nil
}

func test2(serverAddr string) (*packet, error) {
	return sendChangeReq(serverAddr, true, true)
}

func test3(serverAddr string) (*packet, error) {
	return sendChangeReq(serverAddr, false, true)
}

// Follow RFC 3489 and RFC 5389.
func discover(serverAddr string) (NATType, *Host, error) {
	packet, changeAddr, identical, host, err := test1(serverAddr)
	if err != nil {
		return NAT_ERROR, nil, err
	}
	if packet == nil {
		return NAT_BLOCKED, nil, err
	}
	if identical {
		packet, err = test2(serverAddr)
		if err != nil {
			return NAT_ERROR, host, err
		}
		if packet != nil {
			return NAT_NONE, host, nil
		}
		return NAT_SYMETRIC_UDP_FIREWALL, host, nil
	} else {
		packet, err = test2(serverAddr)
		if err != nil {
			return NAT_ERROR, host, err
		}
		if packet != nil {
			return NAT_FULL, host, nil
		} else {
			packet, _, identical, _, err := test1(changeAddr)
			if err != nil {
				return NAT_ERROR, host, err
			}
			if packet == nil {
				// It should be NAT_BLOCKED, but will be
				// detected in the first step. So this will
				// never happen.
				return NAT_UNKNOWN, host, nil
			}
			if identical {
				packet, err = test3(serverAddr)
				if err != nil {
					return NAT_ERROR, host, err
				}
				if packet == nil {
					return NAT_PORT_RESTRICTED, host, nil
				} else {
					return NAT_RESTRICTED, host, nil
				}
			} else {
				return NAT_SYMETRIC, host, nil
			}
		}
	}
}
<commit_msg>clean code<commit_after>// Copyright 2013, Cong Ding. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Cong Ding <dinggnu@gmail.com>

package stun

import (
	"errors"
	"net"
)

// Padding the length of the byte slice to multiple of 4.
func padding(bytes []byte) []byte {
	length := uint16(len(bytes))
	return append(bytes, make([]byte, align(length)-length)...)
}

// Align the uint16 number to the smallest multiple of 4, which is larger than
// or equal to the uint16 number.
func align(n uint16) uint16 {
	return (n + 3) & 0xfffc
}

func sendBindingReq(serverAddr string) (*packet, string, error) {
	conn, err := net.Dial("udp", serverAddr)
	if err != nil {
		return nil, "", err
	}
	// Construct packet.
	packet := newPacket()
	packet.types = type_BINDING_REQUEST
	attribute := newSoftwareAttribute(packet, DefaultSoftwareName)
	packet.addAttribute(*attribute)
	attribute = newFingerprintAttribute(packet)
	packet.addAttribute(*attribute)
	// Send packet.
	localAddr := conn.LocalAddr().String()
	packet, err = packet.send(conn)
	if err != nil {
		return nil, "", err
	}
	err = conn.Close()
	return packet, localAddr, err
}

func sendChangeReq(serverAddr string, changeIP bool, changePort bool) (*packet, error) {
	conn, err := net.Dial("udp", serverAddr)
	if err != nil {
		return nil, err
	}
	// Construct packet.
	packet := newPacket()
	packet.types = type_BINDING_REQUEST
	attribute := newSoftwareAttribute(packet, DefaultSoftwareName)
	packet.addAttribute(*attribute)
	attribute = newChangeReqAttribute(packet, changeIP, changePort)
	packet.addAttribute(*attribute)
	attribute = newFingerprintAttribute(packet)
	packet.addAttribute(*attribute)
	// Send packet.
	packet, err = packet.send(conn)
	if err != nil {
		return nil, err
	}
	err = conn.Close()
	return packet, err
}

func test1(serverAddr string) (*packet, string, bool, *Host, error) {
	packet, localAddr, err := sendBindingReq(serverAddr)
	if err != nil {
		return nil, "", false, nil, err
	}
	if packet == nil {
		return nil, "", false, nil, nil
	}

	// RFC 3489 doesn't require the server return XOR mapped address.
	hostMappedAddr := packet.xorMappedAddr()
	if hostMappedAddr == nil {
		hostMappedAddr = packet.mappedAddr()
		if hostMappedAddr == nil {
			return nil, "", false, nil, errors.New("No mapped address.")
		}
	}

	hostChangedAddr := packet.changedAddr()
	if hostChangedAddr == nil {
		return nil, "", false, nil, errors.New("No changed address.")
	}
	changeAddr := hostChangedAddr.TransportAddr()
	identical := localAddr == hostMappedAddr.TransportAddr()
	return packet, changeAddr, identical, hostMappedAddr, nil
}

func test2(serverAddr string) (*packet, error) {
	return sendChangeReq(serverAddr, true, true)
}

func test3(serverAddr string) (*packet, error) {
	return sendChangeReq(serverAddr, false, true)
}

// Follow RFC 3489 and RFC 5389.
func discover(serverAddr string) (NATType, *Host, error) {
	packet, changeAddr, identical, host, err := test1(serverAddr)
	if err != nil {
		return NAT_ERROR, nil, err
	}
	if packet == nil {
		return NAT_BLOCKED, nil, err
	}
	if identical {
		packet, err = test2(serverAddr)
		if err != nil {
			return NAT_ERROR, host, err
		}
		if packet != nil {
			return NAT_NONE, host, nil
		}
		return NAT_SYMETRIC_UDP_FIREWALL, host, nil
	}
	packet, err = test2(serverAddr)
	if err != nil {
		return NAT_ERROR, host, err
	}
	if packet != nil {
		return NAT_FULL, host, nil
	}
	packet, _, identical, _, err = test1(changeAddr)
	if err != nil {
		return NAT_ERROR, host, err
	}
	if packet == nil {
		// It should be NAT_BLOCKED, but will be detected in the first
		// step. So this will never happen.
		return NAT_UNKNOWN, host, nil
	}
	if identical {
		packet, err = test3(serverAddr)
		if err != nil {
			return NAT_ERROR, host, err
		}
		if packet == nil {
			return NAT_PORT_RESTRICTED, host, nil
		}
		return NAT_RESTRICTED, host, nil
	}
	return NAT_SYMETRIC, host, nil
}
<|endoftext|>


<commit_before>package main

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

	"github.com/gin-gonic/gin"
)

const VERSION = "0.1.0"

type Response struct {
	Success bool   `json:"success"`
	Message string `json:"message"`
}

type FileEntry struct {
	Filename string `json:"filename"`
	IsDir    bool   `json:"directory"`
}

var (
	// Regular expression to match all supported video files
	Extensions = regexp.MustCompile(".(avi|mpg|mov|flv|wmv|asf|mpeg|m4v|divx|mp4|mkv)$")

	// OMXPlayer control commands, these are piped via STDIN to omxplayer process
	Commands = map[string]string{
		"pause":             "p",            // Pause/continue playback
		"stop":              "q",            // Stop playback and exit
		"volume_up":         "+",            // Change volume by +3dB
		"volume_down":       "-",            // Change volume by -3dB
		"subtitles":         "s",            // Enable/disable subtitles
		"seek_back":         "\x1b\x5b\x44", // Seek -30 seconds
		"seek_back_fast":    "\x1b\x5b\x42", // Seek -600 second
		"seek_forward":      "\x1b\x5b\x43", // Seek +30 second
		"seek_forward_fast": "\x1b\x5b\x41", // Seek +600 seconds
	}

	// Path where all media files are stored
	MediaPath string

	// Path to omxplayer executable
	OmxPath string

	// Child process for spawning omxplayer
	Omx *exec.Cmd

	// Child process STDIN pipe to send commands
	OmxIn io.WriteCloser

	// Channel to pass along commands to the player routine
	Command chan string
)

// Returns true if specified file exists
func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

// Scan given path for all directories and matching video files.
// If nothing was found it will return an empty slice.
func scanPath(path string) []FileEntry {
	entries := make([]FileEntry, 0)

	files, err := ioutil.ReadDir(path)
	if err != nil {
		return entries
	}

	for _, file := range files {
		entry := FileEntry{
			Filename: file.Name(),
			IsDir:    file.IsDir(),
		}

		// Do not include non-video files in the list
		if !file.IsDir() && !omxCanPlay(file.Name()) {
			continue
		}

		entries = append(entries, entry)
	}

	return entries
}

// Determine the full path to omxplayer executable. Returns error if not found.
func omxDetect() error {
	buff, err := exec.Command("which", "omxplayer").Output()
	if err != nil {
		return err
	}

	// Set path in global variable
	OmxPath = strings.TrimSpace(string(buff))

	return nil
}

// Start command listener. Commands are coming in through a channel.
func omxListen() {
	Command = make(chan string)

	for {
		command := <-Command

		// Skip command handling of omx player is not active
		if Omx == nil {
			continue
		}

		// Send command to the player
		omxWrite(command)

		// Attempt to kill the process if stop command is requested
		if command == "stop" {
			Omx.Process.Kill()
		}
	}
}

// Start omxplayer playback for a given video file. Returns error if start fails.
func omxPlay(file string) error {
	Omx = exec.Command(
		OmxPath,     // path to omxplayer executable
		"--refresh", // adjust framerate/resolution to video
		"--blank",   // set background to black
		"--adev",    // audio out device
		"hdmi",      // using hdmi for audio/video
		file,        // path to video file
	)

	// Grab child process STDIN
	stdin, err := Omx.StdinPipe()
	if err != nil {
		return err
	}

	defer stdin.Close()

	// Redirect output for debugging purposes
	Omx.Stdout = os.Stdout

	// Start omxplayer execution.
	// If successful, something will appear on HDMI display.
	err = Omx.Start()
	if err != nil {
		return err
	}

	// Make child's STDIN globally available
	OmxIn = stdin

	// Wait until child process is finished
	err = Omx.Wait()
	if err != nil {
		fmt.Fprintln(os.Stdout, "Process exited with error:", err)
	}

	omxCleanup()

	return nil
}

// Write a command string to the omxplayer process's STDIN
func omxWrite(command string) {
	if OmxIn != nil {
		io.WriteString(OmxIn, Commands[command])
	}
}

// Terminate any running omxplayer processes. Fixes random hangs.
func omxKill() {
	exec.Command("killall", "omxplayer.bin").Output()
	exec.Command("killall", "omxplayer").Output()
}

// Reset internal state and stop any running processes
func omxCleanup() {
	Omx = nil
	OmxIn = nil

	omxKill()
}

// Check if player is currently active
func omxIsActive() bool {
	return Omx != nil
}

// Check if player can play the file
func omxCanPlay(path string) bool {
	if Extensions.Match([]byte(path)) {
		return true
	}

	return false
}

func httpBrowse(c *gin.Context) {
	path := c.Request.FormValue("path")

	if path != "" {
		path = fmt.Sprintf("%s/%s", MediaPath, path)
	} else {
		path = MediaPath
	}

	c.JSON(200, scanPath(path))
}

func httpCommand(c *gin.Context) {
	val := c.Params.ByName("command")

	if _, ok := Commands[val]; !ok {
		c.JSON(400, Response{false, "Invalid command"})
		return
	}

	fmt.Println("Received command:", val)

	// Handle requested commmand
	Command <- val

	c.JSON(200, Response{true, "OK"})
}

func httpPlay(c *gin.Context) {
	if omxIsActive() {
		c.JSON(400, Response{false, "Player is already running"})
		return
	}

	file := c.Request.FormValue("file")
	if file == "" {
		c.JSON(400, Response{false, "File is required"})
		return
	}

	file = fmt.Sprintf("%s/%s", MediaPath, file)

	if !fileExists(file) {
		c.JSON(400, Response{false, "File does not exist"})
		return
	}

	if !omxCanPlay(file) {
		c.JSON(400, Response{false, "File cannot be played"})
		return
	}

	go omxPlay(file)

	c.JSON(200, Response{true, "OK"})
}

func httpStatus(c *gin.Context) {
	c.String(200, fmt.Sprintf(`{"running":%v}`, omxIsActive()))
}

func httpIndex(c *gin.Context) {
	data, err := Asset("static/index.html")

	if err != nil {
		c.String(400, err.Error())
		return
	}

	c.Data(200, "text/html; charset=utf-8", data)
}

func terminate(message string, code int) {
	fmt.Println(message)
	os.Exit(code)
}

func usage() {
	terminate("Usage: omxremote path/to/media/dir", 0)
}

func main() {
	fmt.Printf("omxremote v%v\n", VERSION)

	if len(os.Args) < 2 {
		usage()
	}

	// Get path from arguments and remove trailing slash
	MediaPath = strings.TrimRight(os.Args[1], "/")

	if !fileExists(MediaPath) {
		terminate(fmt.Sprintf("Directory does not exist: %s", MediaPath), 1)
	}

	// Check if player is installed
	if omxDetect() != nil {
		terminate("omxplayer is not installed", 1)
	}

	// Make sure nothing is running
	omxCleanup()

	// Start a remote command listener
	go omxListen()

	// Disable debugging mode
	gin.SetMode("release")

	// Setup HTTP server
	router := gin.Default()

	router.GET("/", httpIndex)
	router.GET("/status", httpStatus)
	router.GET("/browse", httpBrowse)
	router.GET("/play", httpPlay)
	router.GET("/command/:command", httpCommand)

	fmt.Println("Starting server on 0.0.0.0:8080")
	router.Run(":8080")
}
<commit_msg>Add fileToTitle method to convert media filenames to titles<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/gin-gonic/gin"
)

const VERSION = "0.1.0"

type Response struct {
	Success bool   `json:"success"`
	Message string `json:"message"`
}

type FileEntry struct {
	Filename string `json:"filename"`
	IsDir    bool   `json:"directory"`
}

var (
	// Regular expression to match all supported video files
	RegexFormats = regexp.MustCompile(`.(avi|mpg|mov|flv|wmv|asf|mpeg|m4v|divx|mp4|mkv)$`)

	// Regular expression to convert filenames to titles
	RegexBrackets = regexp.MustCompile(`[\(\[\]\)]`)
	RegexYear     = regexp.MustCompile(`((19|20)[\d]{2})`)
	RegexJunk     = regexp.MustCompile(`(?i)(1080p|720p|3d|brrip|bluray|webrip|x264|aac)`)
	RegexSpace    = regexp.MustCompile(`\s{2,}`)

	// OMXPlayer control commands, these are piped via STDIN to omxplayer process
	Commands = map[string]string{
		"pause":             "p",            // Pause/continue playback
		"stop":              "q",            // Stop playback and exit
		"volume_up":         "+",            // Change volume by +3dB
		"volume_down":       "-",            // Change volume by -3dB
		"subtitles":         "s",            // Enable/disable subtitles
		"seek_back":         "\x1b\x5b\x44", // Seek -30 seconds
		"seek_back_fast":    "\x1b\x5b\x42", // Seek -600 second
		"seek_forward":      "\x1b\x5b\x43", // Seek +30 second
		"seek_forward_fast": "\x1b\x5b\x41", // Seek +600 seconds
	}

	// Path where all media files are stored
	MediaPath string

	// Path to omxplayer executable
	OmxPath string

	// Child process for spawning omxplayer
	Omx *exec.Cmd

	// Child process STDIN pipe to send commands
	OmxIn io.WriteCloser

	// Channel to pass along commands to the player routine
	Command chan string

	// Currently playing media file name
	CurrentFile string
)

// Returns true if specified file exists
func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

// Scan given path for all directories and matching video files.
// If nothing was found it will return an empty slice.
func scanPath(path string) []FileEntry {
	entries := make([]FileEntry, 0)

	files, err := ioutil.ReadDir(path)
	if err != nil {
		return entries
	}

	for _, file := range files {
		entry := FileEntry{
			Filename: file.Name(),
			IsDir:    file.IsDir(),
		}

		// Do not include non-video files in the list
		if !file.IsDir() && !omxCanPlay(file.Name()) {
			continue
		}

		entries = append(entries, entry)
	}

	return entries
}

// Convert media filename to regular title
func fileToTitle(name string) string {
	// Remove file extension from name
	name = strings.Replace(name, filepath.Ext(name), "", 1)

	// Replace all dots with white space
	name = strings.Replace(name, ".", " ", -1)

	// Replace parenteses and brackets
	name = RegexBrackets.ReplaceAllString(name, "")

	// Check if name has a typical torrent format: "name year format etc"
	pos := RegexYear.FindStringIndex(name)
	if len(pos) > 0 {
		name = name[0:pos[0]]
	}

	// Remove junk stuff
	name = RegexJunk.ReplaceAllString(name, "")

	// Remove any extra white space
	name = RegexSpace.ReplaceAllString(name, "")

	// Truncate space
	name = strings.TrimSpace(name)

	return name
}

// Determine the full path to omxplayer executable. Returns error if not found.
func omxDetect() error {
	buff, err := exec.Command("which", "omxplayer").Output()
	if err != nil {
		return err
	}

	// Set path in global variable
	OmxPath = strings.TrimSpace(string(buff))

	return nil
}

// Start command listener. Commands are coming in through a channel.
func omxListen() {
	Command = make(chan string)

	for {
		command := <-Command

		// Skip command handling of omx player is not active
		if Omx == nil {
			continue
		}

		// Send command to the player
		omxWrite(command)

		// Attempt to kill the process if stop command is requested
		if command == "stop" {
			Omx.Process.Kill()
		}
	}
}

// Start omxplayer playback for a given video file. Returns error if start fails.
func omxPlay(file string) error {
	Omx = exec.Command(
		OmxPath,     // path to omxplayer executable
		"--refresh", // adjust framerate/resolution to video
		"--blank",   // set background to black
		"--adev",    // audio out device
		"hdmi",      // using hdmi for audio/video
		file,        // path to video file
	)

	// Grab child process STDIN
	stdin, err := Omx.StdinPipe()
	if err != nil {
		return err
	}

	defer stdin.Close()

	// Redirect output for debugging purposes
	Omx.Stdout = os.Stdout

	// Start omxplayer execution.
	// If successful, something will appear on HDMI display.
	err = Omx.Start()
	if err != nil {
		return err
	}

	// Make child's STDIN globally available
	OmxIn = stdin

	// Wait until child process is finished
	err = Omx.Wait()
	if err != nil {
		fmt.Fprintln(os.Stdout, "Process exited with error:", err)
	}

	omxCleanup()

	return nil
}

// Write a command string to the omxplayer process's STDIN
func omxWrite(command string) {
	if OmxIn != nil {
		io.WriteString(OmxIn, Commands[command])
	}
}

// Terminate any running omxplayer processes. Fixes random hangs.
func omxKill() {
	exec.Command("killall", "omxplayer.bin").Output()
	exec.Command("killall", "omxplayer").Output()
}

// Reset internal state and stop any running processes
func omxCleanup() {
	Omx = nil
	OmxIn = nil

	omxKill()
}

// Check if player is currently active
func omxIsActive() bool {
	return Omx != nil
}

// Check if player can play the file
func omxCanPlay(path string) bool {
	if RegexFormats.Match([]byte(path)) {
		return true
	}

	return false
}

func httpBrowse(c *gin.Context) {
	path := c.Request.FormValue("path")

	if path != "" {
		path = fmt.Sprintf("%s/%s", MediaPath, path)
	} else {
		path = MediaPath
	}

	c.JSON(200, scanPath(path))
}

func httpCommand(c *gin.Context) {
	val := c.Params.ByName("command")

	if _, ok := Commands[val]; !ok {
		c.JSON(400, Response{false, "Invalid command"})
		return
	}

	fmt.Println("Received command:", val)

	// Handle requested commmand
	Command <- val

	c.JSON(200, Response{true, "OK"})
}

func httpPlay(c *gin.Context) {
	if omxIsActive() {
		c.JSON(400, Response{false, "Player is already running"})
		return
	}

	file := c.Request.FormValue("file")
	if file == "" {
		c.JSON(400, Response{false, "File is required"})
		return
	}

	file = fmt.Sprintf("%s/%s", MediaPath, file)

	if !fileExists(file) {
		c.JSON(400, Response{false, "File does not exist"})
		return
	}

	if !omxCanPlay(file) {
		c.JSON(400, Response{false, "File cannot be played"})
		return
	}

	go omxPlay(file)

	c.JSON(200, Response{true, "OK"})
}

func httpStatus(c *gin.Context) {
	c.String(200, fmt.Sprintf(`{"running":%v}`, omxIsActive()))
}

func httpIndex(c *gin.Context) {
	data, err := Asset("static/index.html")

	if err != nil {
		c.String(400, err.Error())
		return
	}

	c.Data(200, "text/html; charset=utf-8", data)
}

func terminate(message string, code int) {
	fmt.Println(message)
	os.Exit(code)
}

func usage() {
	terminate("Usage: omxremote path/to/media/dir", 0)
}

func main() {
	fmt.Printf("omxremote v%v\n", VERSION)

	if len(os.Args) < 2 {
		usage()
	}

	// Get path from arguments and remove trailing slash
	MediaPath = strings.TrimRight(os.Args[1], "/")

	if !fileExists(MediaPath) {
		terminate(fmt.Sprintf("Directory does not exist: %s", MediaPath), 1)
	}

	// Check if player is installed
	if omxDetect() != nil {
		terminate("omxplayer is not installed", 1)
	}

	// Make sure nothing is running
	omxCleanup()

	// Start a remote command listener
	go omxListen()

	// Disable debugging mode
	gin.SetMode("release")

	// Setup HTTP server
	router := gin.Default()

	router.GET("/", httpIndex)
	router.GET("/status", httpStatus)
	router.GET("/browse", httpBrowse)
	router.GET("/play", httpPlay)
	router.GET("/command/:command", httpCommand)

	fmt.Println("Starting server on 0.0.0.0:8080")
	router.Run(":8080")
}
<|endoftext|>


<commit_before>package filter

import (
	"fmt"
	"encoding/json"
	"io/ioutil"
	"strings"
)

type FilterConfig struct {
	Ignore []string `json:"ignore"`
}

func GetFilterConfig(fileName string) (FilterConfig, error) {
	/* Takes a json file name as a string. Reads the file, unmarshals json.
	Returns the unmarshalled FilterConfig object
	See configurator.ReadConfig for an example
	In detail:
	1. Read all of the file with ioutil.ReadFile
	2. Instantiate an empty config object.
	3. Unmarshal the byte array which is the file's contents as json into the
	new config object using jsom.Unmarshal.
	4. Return the config object.
	If there is an error, return it.
	*/

	config := FilterConfig{}
	data, err := ioutil.ReadFile(fileName)
	if err != nil {
		return config, err
	}
	err = json.Unmarshal(data, &config)
	if err != nil {
		return config, err
	}

	return config, nil

	// Dummy implementation for testing. Remove later
	// newFilterConf := FilterConfig{Ignore: []string{"/dev/"}}
	// return newFilterConf, nil
}

type Filter interface {
	Start(c FilterConfig, sending chan<- []byte, receiving <-chan []byte)
}

type FSFilter struct{}

type NOPFilter struct{}

type ZachsInotifyData struct {
	Date     string `json:"DATE"`
	Event    string `json:"EVENT"`
	FilePath string `json:"PATH"`
	Type     string `json:"TYPE"`
}

func StartFilterStream(sending chan<- []byte, receiving <-chan []byte) {
	fsFilter := FSFilter{}
	nopFilter := NOPFilter{}
	conf := FilterConfig{Ignore: []string{"/dev/"}}
	link := make(chan []byte)
	go fsFilter.Start(conf, link, receiving)
	go nopFilter.Start(conf, sending, link)
}

func (f FSFilter) Start(c FilterConfig, sending chan<- []byte, receiving <-chan []byte) {
	for {
		message := <-receiving
		zid := ZachsInotifyData{}
		err := json.Unmarshal(message, &zid)
		if err != nil {
			fmt.Println("Error unmarshalling message: ", string(message),
				err.Error(), message)
			fmt.Println("Silently dropping the message")
			//panic(err)
		}
		notblacklisted := true
		for _, i := range c.Ignore {
			if strings.HasPrefix(zid.FilePath, i) {
				notblacklisted = false
				break
			}
		}
		if notblacklisted {
			sending <- message
		}
	}
}

func (N NOPFilter) Start(c FilterConfig, sending chan<- []byte, receiving <-chan []byte) {
	for {
		message := <-receiving
		sending <- message
	}
}
<commit_msg>HACK: correct for bug in znotify<commit_after>package filter

import (
	"fmt"
	"encoding/json"
	"io/ioutil"
	"strings"
)

type FilterConfig struct {
	Ignore []string `json:"ignore"`
}

// Correct for a bug in znotify.
func filterLowBytes(contaminated []byte) []byte {
	// Remove all non-printing ascii characters
	for i, item := range contaminated {
		if item < byte(' ') {
			contaminated = append(contaminated[:i], contaminated[i+1:]...)
		}
	}
	// It's been cleaned
	return contaminated
}

func GetFilterConfig(fileName string) (FilterConfig, error) {
	/* Takes a json file name as a string. Reads the file, unmarshals json.
	Returns the unmarshalled FilterConfig object
	See configurator.ReadConfig for an example
	In detail:
	1. Read all of the file with ioutil.ReadFile
	2. Instantiate an empty config object.
	3. Unmarshal the byte array which is the file's contents as json into the
	new config object using jsom.Unmarshal.
	4. Return the config object.
	If there is an error, return it.
	*/

	config := FilterConfig{}
	data, err := ioutil.ReadFile(fileName)
	if err != nil {
		return config, err
	}
	err = json.Unmarshal(data, &config)
	if err != nil {
		return config, err
	}

	return config, nil

	// Dummy implementation for testing. Remove later
	// newFilterConf := FilterConfig{Ignore: []string{"/dev/"}}
	// return newFilterConf, nil
}

type Filter interface {
	Start(c FilterConfig, sending chan<- []byte, receiving <-chan []byte)
}

type FSFilter struct{}

type NOPFilter struct{}

type ZachsInotifyData struct {
	Date     string `json:"DATE"`
	Event    string `json:"EVENT"`
	FilePath string `json:"PATH"`
	Type     string `json:"TYPE"`
}

func StartFilterStream(sending chan<- []byte, receiving <-chan []byte) {
	fsFilter := FSFilter{}
	nopFilter := NOPFilter{}
	conf := FilterConfig{Ignore: []string{"/dev/"}}
	link := make(chan []byte)
	go fsFilter.Start(conf, link, receiving)
	go nopFilter.Start(conf, sending, link)
}

func (f FSFilter) Start(c FilterConfig, sending chan<- []byte, receiving <-chan []byte) {
	for {
		message := <-receiving
		message = filterLowBytes(message)
		zid := ZachsInotifyData{}
		err := json.Unmarshal(message, &zid)
		if err != nil {
			fmt.Println("Error unmarshalling message: ", string(message),
				err.Error(), message)
			fmt.Println("Silently dropping the message")
			//panic(err)
		}
		notblacklisted := true
		for _, i := range c.Ignore {
			if strings.HasPrefix(zid.FilePath, i) {
				notblacklisted = false
				break
			}
		}
		if notblacklisted {
			sending <- message
		}
	}
}

func (N NOPFilter) Start(c FilterConfig, sending chan<- []byte, receiving <-chan []byte) {
	for {
		message := <-receiving
		sending <- message
	}
}
<|endoftext|>


<commit_before>package scans

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/ion-channel/ionic/vulnerabilities"
)

// UntranslatedResults represents a result of a specific type that has not been
// translated for use in reports
type UntranslatedResults struct {
	AboutYML                *AboutYMLResults                `json:"about_yml,omitempty"`
	Community               *CommunityResults               `json:"community,omitempty"`
	Coverage                *CoverageResults                `json:"coverage,omitempty"`
	Dependency              *DependencyResults              `json:"dependency,omitempty"`
	Difference              *DifferenceResults              `json:"difference,omitempty"`
	Ecosystem               *EcosystemResults               `json:"ecosystems,omitempty"`
	ExternalVulnerabilities *ExternalVulnerabilitiesResults `json:"external_vulnerability,omitempty"`
	License                 *LicenseResults                 `json:"license,omitempty"`
	Virus                   *VirusResults                   `json:"clamav,omitempty"`
	Vulnerability           *VulnerabilityResults           `json:"vulnerabilities,omitempty"`
}

// Translate moves information from the particular sub-struct, IE
// AboutYMLResults or LicenseResults into a generic, Data struct
func (u *UntranslatedResults) Translate() *TranslatedResults {
	var tr TranslatedResults
	// There is an argument to be made that the following "if" clauses
	// could be simplified with introspection since they all do
	// basically the same thing. I've (dmiles) chosen to writ it all
	// out in the name of explicit, easily-readable code.
	if u.AboutYML != nil {
		tr.Type = "about_yml"
		tr.Data = u.AboutYML
	}
	if u.Community != nil {
		tr.Type = "community"
		tr.Data = u.Community
	}
	if u.Coverage != nil {
		tr.Type = "coverage"
		tr.Data = u.Coverage
	}
	if u.Dependency != nil {
		tr.Type = "dependency"
		tr.Data = u.Dependency
	}
	if u.Difference != nil {
		tr.Type = "difference"
		tr.Data = u.Difference
	}
	if u.Ecosystem != nil {
		tr.Type = "ecosystems"
		tr.Data = u.Ecosystem
	}
	if u.ExternalVulnerabilities != nil {
		tr.Type = "external_vulnerability"
		tr.Data = u.ExternalVulnerabilities
	}
	if u.License != nil {
		tr.Type = "license"
		tr.Data = u.License
	}
	if u.Virus != nil {
		tr.Type = "virus"
		tr.Data = u.Virus
	}
	if u.Vulnerability != nil {
		tr.Type = "vulnerability"
		tr.Data = u.Vulnerability
	}
	return &tr
}

// TranslatedResults represents a result of a specific type that has been
// translated for use in reports
type TranslatedResults struct {
	Type string      `json:"type" xml:"type"`
	Data interface{} `json:"data,omitempty" xml:"data,omitempty"`
}

type translatedResults struct {
	Type    string          `json:"type"`
	RawData json.RawMessage `json:"data"`
}

// UnmarshalJSON is a custom JSON unmarshaller implementation for the standard
// go json package to know how to properly interpret ScanSummaryResults from
// JSON.
func (r *TranslatedResults) UnmarshalJSON(b []byte) error {
	var tr translatedResults
	err := json.Unmarshal(b, &tr)
	if err != nil {
		return err
	}

	r.Type = tr.Type

	switch strings.ToLower(tr.Type) {
	case "about_yml":
		var a AboutYMLResults
		err := json.Unmarshal(tr.RawData, &a)
		if err != nil {
			return fmt.Errorf("failed to unmarshall about yml results: %v", err)
		}

		r.Data = a
	case "community":
		var c CommunityResults
		err := json.Unmarshal(tr.RawData, &c)
		if err != nil {
			// Note: Could be a slice, needs to be fixed
			if strings.Contains(err.Error(), "cannot unmarshal array") {
				var sliceOfCommunityResults []CommunityResults
				err := json.Unmarshal(tr.RawData, &sliceOfCommunityResults)
				if err == nil {
					c = sliceOfCommunityResults[0]
					break
				}
			}
			return fmt.Errorf("failed to unmarshall community results: %v", err)
		}

		r.Data = c
	case "coverage", "external_coverage":
		var c CoverageResults
		err := json.Unmarshal(tr.RawData, &c)
		if err != nil {
			return fmt.Errorf("failed to unmarshall coverage results: %v", err)
		}

		r.Data = c
	case "dependency":
		var d DependencyResults
		err := json.Unmarshal(tr.RawData, &d)
		if err != nil {
			return fmt.Errorf("failed to unmarshall dependency results: %v", err)
		}

		r.Data = d
	case "ecosystems":
		var e EcosystemResults
		err := json.Unmarshal(tr.RawData, &e)
		if err != nil {
			return fmt.Errorf("failed to unmarshall ecosystems results: %v", err)
		}

		r.Data = e
	case "license":
		var l LicenseResults
		err := json.Unmarshal(tr.RawData, &l)
		if err != nil {
			return fmt.Errorf("failed to unmarshall license results: %v", err)
		}

		r.Data = l
	case "virus", "clamav":
		var v VirusResults
		err := json.Unmarshal(tr.RawData, &v)
		if err != nil {
			return fmt.Errorf("failed to unmarshall virus results: %v", err)
		}

		r.Data = v
	case "vulnerability":
		var v VulnerabilityResults
		err := json.Unmarshal(tr.RawData, &v)
		if err != nil {
			return fmt.Errorf("failed to unmarshall vulnerability results: %v", err)
		}

		r.Data = v
	case "external_vulnerability":
		var v ExternalVulnerabilitiesResults
		err := json.Unmarshal(tr.RawData, &v)
		if err != nil {
			return fmt.Errorf("failed to unmarshall external vulnerabilities results: %v", err)
		}

		r.Data = v
	case "difference":
		var v DifferenceResults
		err := json.Unmarshal(tr.RawData, &v)
		if err != nil {
			return fmt.Errorf("failed to unmarshall difference results: %v", err)
		}

		r.Data = v
	default:
		return fmt.Errorf("unsupported results type found: %v", tr.Type)
	}

	return nil
}

// AboutYMLResults represents the data collected from the AboutYML scan.  It
// includes a message and whether or not the About YML file found was valid or
// not.
type AboutYMLResults struct {
	Message string `json:"message" xml:"message"`
	Valid   bool   `json:"valid" xml:"valid"`
	Content string `json:"content" xml:"content"`
}

// CommunityResults represents the data collected from a community scan.  It
// represents all known data regarding the open community of a software project
type CommunityResults struct {
	Committers int    `json:"committers" xml:"committers"`
	Name       string `json:"name" xml:"name"`
	URL        string `json:"url" xml:"url"`
}

// CoverageResults represents the data collected from a code coverage scan.  It
// includes the value of the code coverage seen for the project.
type CoverageResults struct {
	Value float64 `json:"value" xml:"value"`
}

// Dependency represents data for an individual requirement resolution
type Dependency struct {
	LatestVersion string `json:"latest_version" xml:"latest_version"`
	Org           string `json:"org" xml:"org"`
	Name          string `json:"name" xml:"name"`
	Type          string `json:"type" xml:"type"`
	Package       string `json:"package" xml:"package"`
	Version       string `json:"version" xml:"version"`
	Scope         string `json:"scope" xml:"scope"`
}

// DependencyMeta represents data for a summary of all dependencies resolved
type DependencyMeta struct {
	FirstDegreeCount     int `json:"first_degree_count" xml:"first_degree_count"`
	NoVersionCount       int `json:"no_version_count" xml:"no_version_count"`
	TotalUniqueCount     int `json:"total_unique_count" xml:"total_unique_count"`
	UpdateAvailableCount int `json:"update_available_count" xml:"update_available_count"`
}

// DependencyResults represents the data collected from a dependency scan.  It
// includes a list of the dependencies seen and meta data counts about those
// dependencies seen.
type DependencyResults struct {
	Dependencies []Dependency   `json:"dependencies" xml:"dependencies"`
	Meta         DependencyMeta `json:"meta" xml:"meta"`
}

// DifferenceResults represents the checksum of a project.  It includes a checksum
// and flag indicating if there was a difference detected within that last 5 scans
type DifferenceResults struct {
	Checksum   string `json:"checksum" xml:"checksum"`
	Difference bool   `json:"difference" xml:"difference"`
}

// EcosystemResults represents the data collected from an ecosystems scan.  It
// include the name of the ecosystem and the number of lines seen for the given
// ecosystem.
type EcosystemResults struct {
	Ecosystems map[string]int `json:"ecosystems" xml:"ecosystems"`
}

// ExternalVulnerabilitiesResults represents the data collected from an external
// vulnerability scan.  It includes the number of each vulnerability criticality
// seen within the project.
type ExternalVulnerabilitiesResults struct {
	Critical int `json:"critical" xml:"critical"`
	High     int `json:"high" xml:"high"`
	Medium   int `json:"medium" xml:"medium"`
	Low      int `json:"low" xml:"low"`
}

// LicenseResults represents the data collected from a license scan.  It
// includes the name and type of each license seen within the project.
type LicenseResults struct {
	*License `json:"license" xml:"license"`
}

// License represents a name and slice of types of licenses seen in a given file
type License struct {
	Name string        `json:"name" xml:"name"`
	Type []LicenseType `json:"type" xml:"type"`
}

// LicenseType represents a type of license such as MIT, Apache 2.0, etc
type LicenseType struct {
	Name string `json:"name" xml:"name"`
}

// VirusResults represents the data collected from a virus scan.  It includes
// information of the viruses seen and the virus scanner used.
type VirusResults struct {
	KnownViruses       int    `json:"known_viruses" xml:"known_viruses"`
	EngineVersion      string `json:"engine_version" xml:"engine_version"`
	ScannedDirectories int    `json:"scanned_directories" xml:"scanned_directories"`
	ScannedFiles       int    `json:"scanned_files" xml:"scanned_files"`
	InfectedFiles      int    `json:"infected_files" xml:"infected_files"`
	DataScanned        string `json:"data_scanned" xml:"data_scanned"`
	DataRead           string `json:"data_read" xml:"data_read"`
	Time               string `json:"time" xml:"time"`
	FileNotes          struct {
		EmptyFiles []string `json:"empty_file" xml:"empty_file"`
	} `json:"file_notes" xml:"file_notes"`
	ClamavDetails struct {
		ClamavVersion   string `json:"clamav_version" xml:"clamav_version"`
		ClamavDbVersion string `json:"clamav_db_version" xml:"clamav_db_version"`
	} `json:"clam_av_details" xml:"clam_av_details"`
}

//VulnerabilityResults represents the data collected from a vulnerability scan.  It includes
// information of the vulnerabilities seen.
type VulnerabilityResults struct {
	Vulnerabilities []struct {
		ID              int                             `json:"id" xml:"id"`
		ExternalID      string                          `json:"external_id" xml:"external_id"`
		SourceID        int                             `json:"source_id" xml:"source_id"`
		Title           string                          `json:"title" xml:"title"`
		Name            string                          `json:"name" xml:"name"`
		Org             string                          `json:"org" xml:"org"`
		Version         string                          `json:"version" xml:"version"`
		Up              interface{}                     `json:"up" xml:"up"`
		Edition         interface{}                     `json:"edition" xml:"edition"`
		Aliases         []string                        `json:"aliases" xml:"aliases"`
		CreatedAt       time.Time                       `json:"created_at" xml:"created_at"`
		UpdatedAt       time.Time                       `json:"updated_at" xml:"updated_at"`
		References      interface{}                     `json:"references" xml:"references"`
		Part            interface{}                     `json:"part" xml:"part"`
		Language        interface{}                     `json:"language" xml:"language"`
		Vulnerabilities []vulnerabilities.Vulnerability `json:"vulnerabilities" xml:"vulnerabilities"`
	} `json:"vulnerabilities" xml:"vulnerabilities"`
	Meta struct {
		VulnerabilityCount int `json:"vulnerability_count" xml:"vulnerability_count"`
	} `json:"meta" xml:"meta"`
}
<commit_msg>ecosystems has probably been broken for a long while<commit_after>package scans

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/ion-channel/ionic/vulnerabilities"
)

// UntranslatedResults represents a result of a specific type that has not been
// translated for use in reports
type UntranslatedResults struct {
	AboutYML                *AboutYMLResults                `json:"about_yml,omitempty"`
	Community               *CommunityResults               `json:"community,omitempty"`
	Coverage                *CoverageResults                `json:"coverage,omitempty"`
	Dependency              *DependencyResults              `json:"dependency,omitempty"`
	Difference              *DifferenceResults              `json:"difference,omitempty"`
	Ecosystem               *EcosystemResults               `json:"ecosystems,omitempty"`
	ExternalVulnerabilities *ExternalVulnerabilitiesResults `json:"external_vulnerability,omitempty"`
	License                 *LicenseResults                 `json:"license,omitempty"`
	Virus                   *VirusResults                   `json:"clamav,omitempty"`
	Vulnerability           *VulnerabilityResults           `json:"vulnerabilities,omitempty"`
}

// Translate moves information from the particular sub-struct, IE
// AboutYMLResults or LicenseResults into a generic, Data struct
func (u *UntranslatedResults) Translate() *TranslatedResults {
	var tr TranslatedResults
	// There is an argument to be made that the following "if" clauses
	// could be simplified with introspection since they all do
	// basically the same thing. I've (dmiles) chosen to writ it all
	// out in the name of explicit, easily-readable code.
	if u.AboutYML != nil {
		tr.Type = "about_yml"
		tr.Data = u.AboutYML
	}
	if u.Community != nil {
		tr.Type = "community"
		tr.Data = u.Community
	}
	if u.Coverage != nil {
		tr.Type = "coverage"
		tr.Data = u.Coverage
	}
	if u.Dependency != nil {
		tr.Type = "dependency"
		tr.Data = u.Dependency
	}
	if u.Difference != nil {
		tr.Type = "difference"
		tr.Data = u.Difference
	}
	if u.Ecosystem != nil {
		tr.Type = "ecosystems"
		tr.Data = u.Ecosystem
	}
	if u.ExternalVulnerabilities != nil {
		tr.Type = "external_vulnerability"
		tr.Data = u.ExternalVulnerabilities
	}
	if u.License != nil {
		tr.Type = "license"
		tr.Data = u.License
	}
	if u.Virus != nil {
		tr.Type = "virus"
		tr.Data = u.Virus
	}
	if u.Vulnerability != nil {
		tr.Type = "vulnerability"
		tr.Data = u.Vulnerability
	}
	return &tr
}

// TranslatedResults represents a result of a specific type that has been
// translated for use in reports
type TranslatedResults struct {
	Type string      `json:"type" xml:"type"`
	Data interface{} `json:"data,omitempty" xml:"data,omitempty"`
}

type translatedResults struct {
	Type    string          `json:"type"`
	RawData json.RawMessage `json:"data"`
}

// UnmarshalJSON is a custom JSON unmarshaller implementation for the standard
// go json package to know how to properly interpret ScanSummaryResults from
// JSON.
func (r *TranslatedResults) UnmarshalJSON(b []byte) error {
	var tr translatedResults
	err := json.Unmarshal(b, &tr)
	if err != nil {
		return err
	}

	r.Type = tr.Type

	switch strings.ToLower(tr.Type) {
	case "about_yml":
		var a AboutYMLResults
		err := json.Unmarshal(tr.RawData, &a)
		if err != nil {
			return fmt.Errorf("failed to unmarshall about yml results: %v", err)
		}

		r.Data = a
	case "community":
		var c CommunityResults
		err := json.Unmarshal(tr.RawData, &c)
		if err != nil {
			// Note: Could be a slice, needs to be fixed
			if strings.Contains(err.Error(), "cannot unmarshal array") {
				var sliceOfCommunityResults []CommunityResults
				err := json.Unmarshal(tr.RawData, &sliceOfCommunityResults)
				if err == nil {
					c = sliceOfCommunityResults[0]
					break
				}
			}
			return fmt.Errorf("failed to unmarshall community results: %v", err)
		}

		r.Data = c
	case "coverage", "external_coverage":
		var c CoverageResults
		err := json.Unmarshal(tr.RawData, &c)
		if err != nil {
			return fmt.Errorf("failed to unmarshall coverage results: %v", err)
		}

		r.Data = c
	case "dependency":
		var d DependencyResults
		err := json.Unmarshal(tr.RawData, &d)
		if err != nil {
			return fmt.Errorf("failed to unmarshall dependency results: %v", err)
		}

		r.Data = d
	case "ecosystems":
		var e EcosystemResults
		err := json.Unmarshal(tr.RawData, &e)
		if err != nil {
			return fmt.Errorf("failed to unmarshall ecosystems results: %v", err)
		}

		r.Data = e
	case "license":
		var l LicenseResults
		err := json.Unmarshal(tr.RawData, &l)
		if err != nil {
			return fmt.Errorf("failed to unmarshall license results: %v", err)
		}

		r.Data = l
	case "virus", "clamav":
		var v VirusResults
		err := json.Unmarshal(tr.RawData, &v)
		if err != nil {
			return fmt.Errorf("failed to unmarshall virus results: %v", err)
		}

		r.Data = v
	case "vulnerability":
		var v VulnerabilityResults
		err := json.Unmarshal(tr.RawData, &v)
		if err != nil {
			return fmt.Errorf("failed to unmarshall vulnerability results: %v", err)
		}

		r.Data = v
	case "external_vulnerability":
		var v ExternalVulnerabilitiesResults
		err := json.Unmarshal(tr.RawData, &v)
		if err != nil {
			return fmt.Errorf("failed to unmarshall external vulnerabilities results: %v", err)
		}

		r.Data = v
	case "difference":
		var v DifferenceResults
		err := json.Unmarshal(tr.RawData, &v)
		if err != nil {
			return fmt.Errorf("failed to unmarshall difference results: %v", err)
		}

		r.Data = v
	default:
		return fmt.Errorf("unsupported results type found: %v", tr.Type)
	}

	return nil
}

// AboutYMLResults represents the data collected from the AboutYML scan.  It
// includes a message and whether or not the About YML file found was valid or
// not.
type AboutYMLResults struct {
	Message string `json:"message" xml:"message"`
	Valid   bool   `json:"valid" xml:"valid"`
	Content string `json:"content" xml:"content"`
}

// CommunityResults represents the data collected from a community scan.  It
// represents all known data regarding the open community of a software project
type CommunityResults struct {
	Committers int    `json:"committers" xml:"committers"`
	Name       string `json:"name" xml:"name"`
	URL        string `json:"url" xml:"url"`
}

// CoverageResults represents the data collected from a code coverage scan.  It
// includes the value of the code coverage seen for the project.
type CoverageResults struct {
	Value float64 `json:"value" xml:"value"`
}

// Dependency represents data for an individual requirement resolution
type Dependency struct {
	LatestVersion string `json:"latest_version" xml:"latest_version"`
	Org           string `json:"org" xml:"org"`
	Name          string `json:"name" xml:"name"`
	Type          string `json:"type" xml:"type"`
	Package       string `json:"package" xml:"package"`
	Version       string `json:"version" xml:"version"`
	Scope         string `json:"scope" xml:"scope"`
}

// DependencyMeta represents data for a summary of all dependencies resolved
type DependencyMeta struct {
	FirstDegreeCount     int `json:"first_degree_count" xml:"first_degree_count"`
	NoVersionCount       int `json:"no_version_count" xml:"no_version_count"`
	TotalUniqueCount     int `json:"total_unique_count" xml:"total_unique_count"`
	UpdateAvailableCount int `json:"update_available_count" xml:"update_available_count"`
}

// DependencyResults represents the data collected from a dependency scan.  It
// includes a list of the dependencies seen and meta data counts about those
// dependencies seen.
type DependencyResults struct {
	Dependencies []Dependency   `json:"dependencies" xml:"dependencies"`
	Meta         DependencyMeta `json:"meta" xml:"meta"`
}

// DifferenceResults represents the checksum of a project.  It includes a checksum
// and flag indicating if there was a difference detected within that last 5 scans
type DifferenceResults struct {
	Checksum   string `json:"checksum" xml:"checksum"`
	Difference bool   `json:"difference" xml:"difference"`
}

// EcosystemResults represents the data collected from an ecosystems scan.  It
// include the name of the ecosystem and the number of lines seen for the given
// ecosystem.
type EcosystemResults struct {
	Ecosystems map[string]int `json:"ecosystems" xml:"ecosystems"`
}

// UnmarshalJSON meets the unmarshaller interface to custom wrangle the
// ecosystem scan into an ecosystem result
func (e *EcosystemResults) UnmarshalJSON(b []byte) error {
	var m map[string]int
	err := json.Unmarshal(b, &m)
	if err != nil {
		return fmt.Errorf("failed to unmarshal ecosystem result: %v", err.Error())
	}

	e.Ecosystems = m
	return nil
}

// ExternalVulnerabilitiesResults represents the data collected from an external
// vulnerability scan.  It includes the number of each vulnerability criticality
// seen within the project.
type ExternalVulnerabilitiesResults struct {
	Critical int `json:"critical" xml:"critical"`
	High     int `json:"high" xml:"high"`
	Medium   int `json:"medium" xml:"medium"`
	Low      int `json:"low" xml:"low"`
}

// LicenseResults represents the data collected from a license scan.  It
// includes the name and type of each license seen within the project.
type LicenseResults struct {
	*License `json:"license" xml:"license"`
}

// License represents a name and slice of types of licenses seen in a given file
type License struct {
	Name string        `json:"name" xml:"name"`
	Type []LicenseType `json:"type" xml:"type"`
}

// LicenseType represents a type of license such as MIT, Apache 2.0, etc
type LicenseType struct {
	Name string `json:"name" xml:"name"`
}

// VirusResults represents the data collected from a virus scan.  It includes
// information of the viruses seen and the virus scanner used.
type VirusResults struct {
	KnownViruses       int    `json:"known_viruses" xml:"known_viruses"`
	EngineVersion      string `json:"engine_version" xml:"engine_version"`
	ScannedDirectories int    `json:"scanned_directories" xml:"scanned_directories"`
	ScannedFiles       int    `json:"scanned_files" xml:"scanned_files"`
	InfectedFiles      int    `json:"infected_files" xml:"infected_files"`
	DataScanned        string `json:"data_scanned" xml:"data_scanned"`
	DataRead           string `json:"data_read" xml:"data_read"`
	Time               string `json:"time" xml:"time"`
	FileNotes          struct {
		EmptyFiles []string `json:"empty_file" xml:"empty_file"`
	} `json:"file_notes" xml:"file_notes"`
	ClamavDetails struct {
		ClamavVersion   string `json:"clamav_version" xml:"clamav_version"`
		ClamavDbVersion string `json:"clamav_db_version" xml:"clamav_db_version"`
	} `json:"clam_av_details" xml:"clam_av_details"`
}

//VulnerabilityResults represents the data collected from a vulnerability scan.  It includes
// information of the vulnerabilities seen.
type VulnerabilityResults struct {
	Vulnerabilities []struct {
		ID              int                             `json:"id" xml:"id"`
		ExternalID      string                          `json:"external_id" xml:"external_id"`
		SourceID        int                             `json:"source_id" xml:"source_id"`
		Title           string                          `json:"title" xml:"title"`
		Name            string                          `json:"name" xml:"name"`
		Org             string                          `json:"org" xml:"org"`
		Version         string                          `json:"version" xml:"version"`
		Up              interface{}                     `json:"up" xml:"up"`
		Edition         interface{}                     `json:"edition" xml:"edition"`
		Aliases         []string                        `json:"aliases" xml:"aliases"`
		CreatedAt       time.Time                       `json:"created_at" xml:"created_at"`
		UpdatedAt       time.Time                       `json:"updated_at" xml:"updated_at"`
		References      interface{}                     `json:"references" xml:"references"`
		Part            interface{}                     `json:"part" xml:"part"`
		Language        interface{}                     `json:"language" xml:"language"`
		Vulnerabilities []vulnerabilities.Vulnerability `json:"vulnerabilities" xml:"vulnerabilities"`
	} `json:"vulnerabilities" xml:"vulnerabilities"`
	Meta struct {
		VulnerabilityCount int `json:"vulnerability_count" xml:"vulnerability_count"`
	} `json:"meta" xml:"meta"`
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux

package syscall_test

import (
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"syscall"
	"testing"
)

// Check if we are in a chroot by checking if the inode of / is
// different from 2 (there is no better test available to non-root on
// linux).
func isChrooted(t *testing.T) bool {
	root, err := os.Stat("/")
	if err != nil {
		t.Fatalf("cannot stat /: %v", err)
	}
	return root.Sys().(*syscall.Stat_t).Ino != 2
}

func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd {
	if _, err := os.Stat("/proc/self/ns/user"); err != nil {
		if os.IsNotExist(err) {
			t.Skip("kernel doesn't support user namespaces")
		}
		if os.IsPermission(err) {
			t.Skip("unable to test user namespaces due to permissions")
		}
		t.Fatalf("Failed to stat /proc/self/ns/user: %v", err)
	}
	if isChrooted(t) {
		// create_user_ns in the kernel (see
		// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/user_namespace.c)
		// forbids the creation of user namespaces when chrooted.
		t.Skip("cannot create user namespaces when chrooted")
	}
	// On some systems, there is a sysctl setting.
	if os.Getuid() != 0 {
		data, errRead := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
		if errRead == nil && data[0] == '0' {
			t.Skip("kernel prohibits user namespace in unprivileged process")
		}
	}
	// When running under the Go continuous build, skip tests for
	// now when under Kubernetes. (where things are root but not quite)
	// Both of these are our own environment variables.
	// See Issue 12815.
	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
	}
	cmd := exec.Command("whoami")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUSER,
		UidMappings: []syscall.SysProcIDMap{
			{ContainerID: 0, HostID: uid, Size: 1},
		},
		GidMappings: []syscall.SysProcIDMap{
			{ContainerID: 0, HostID: gid, Size: 1},
		},
		GidMappingsEnableSetgroups: setgroups,
	}
	return cmd
}

func testNEWUSERRemap(t *testing.T, uid, gid int, setgroups bool) {
	cmd := whoamiCmd(t, uid, gid, setgroups)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
	}
	sout := strings.TrimSpace(string(out))
	want := "root"
	if sout != want {
		t.Fatalf("whoami = %q; want %q", out, want)
	}
}

func TestCloneNEWUSERAndRemapRootDisableSetgroups(t *testing.T) {
	if os.Getuid() != 0 {
		t.Skip("skipping root only test")
	}
	testNEWUSERRemap(t, 0, 0, false)
}

func TestCloneNEWUSERAndRemapRootEnableSetgroups(t *testing.T) {
	if os.Getuid() != 0 {
		t.Skip("skipping root only test")
	}
	testNEWUSERRemap(t, 0, 0, false)
}

func TestCloneNEWUSERAndRemapNoRootDisableSetgroups(t *testing.T) {
	if os.Getuid() == 0 {
		t.Skip("skipping unprivileged user only test")
	}
	testNEWUSERRemap(t, os.Getuid(), os.Getgid(), false)
}

func TestCloneNEWUSERAndRemapNoRootSetgroupsEnableSetgroups(t *testing.T) {
	if os.Getuid() == 0 {
		t.Skip("skipping unprivileged user only test")
	}
	cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), true)
	err := cmd.Run()
	if err == nil {
		t.Skip("probably old kernel without security fix")
	}
	if !os.IsPermission(err) {
		t.Fatalf("Unprivileged gid_map rewriting with GidMappingsEnableSetgroups must fail")
	}
}

func TestEmptyCredGroupsDisableSetgroups(t *testing.T) {
	cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), false)
	cmd.SysProcAttr.Credential = &syscall.Credential{}
	if err := cmd.Run(); err != nil {
		t.Fatal(err)
	}
}

func TestUnshare(t *testing.T) {
	// Make sure we are running as root so we have permissions to use unshare
	// and create a network namespace.
	if os.Getuid() != 0 {
		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
	}

	// When running under the Go continuous build, skip tests for
	// now when under Kubernetes. (where things are root but not quite)
	// Both of these are our own environment variables.
	// See Issue 12815.
	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
	}

	cmd := exec.Command("ip", "a")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Unshare: syscall.CLONE_NEWNET,
	}
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
	}

	// Check there is only the local network interface
	sout := strings.TrimSpace(string(out))
	if !strings.Contains(sout, "lo") {
		t.Fatalf("Expected lo network interface to exist, got %s", sout)
	}

	lines := strings.Split(sout, "\n")
	if len(lines) != 2 {
		t.Fatalf("Expected 2 lines of output, got %d", len(lines))
	}
}
<commit_msg>syscall: fix unshare test on mips<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux

package syscall_test

import (
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"syscall"
	"testing"
)

// Check if we are in a chroot by checking if the inode of / is
// different from 2 (there is no better test available to non-root on
// linux).
func isChrooted(t *testing.T) bool {
	root, err := os.Stat("/")
	if err != nil {
		t.Fatalf("cannot stat /: %v", err)
	}
	return root.Sys().(*syscall.Stat_t).Ino != 2
}

func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd {
	if _, err := os.Stat("/proc/self/ns/user"); err != nil {
		if os.IsNotExist(err) {
			t.Skip("kernel doesn't support user namespaces")
		}
		if os.IsPermission(err) {
			t.Skip("unable to test user namespaces due to permissions")
		}
		t.Fatalf("Failed to stat /proc/self/ns/user: %v", err)
	}
	if isChrooted(t) {
		// create_user_ns in the kernel (see
		// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/user_namespace.c)
		// forbids the creation of user namespaces when chrooted.
		t.Skip("cannot create user namespaces when chrooted")
	}
	// On some systems, there is a sysctl setting.
	if os.Getuid() != 0 {
		data, errRead := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
		if errRead == nil && data[0] == '0' {
			t.Skip("kernel prohibits user namespace in unprivileged process")
		}
	}
	// When running under the Go continuous build, skip tests for
	// now when under Kubernetes. (where things are root but not quite)
	// Both of these are our own environment variables.
	// See Issue 12815.
	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
	}
	cmd := exec.Command("whoami")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUSER,
		UidMappings: []syscall.SysProcIDMap{
			{ContainerID: 0, HostID: uid, Size: 1},
		},
		GidMappings: []syscall.SysProcIDMap{
			{ContainerID: 0, HostID: gid, Size: 1},
		},
		GidMappingsEnableSetgroups: setgroups,
	}
	return cmd
}

func testNEWUSERRemap(t *testing.T, uid, gid int, setgroups bool) {
	cmd := whoamiCmd(t, uid, gid, setgroups)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
	}
	sout := strings.TrimSpace(string(out))
	want := "root"
	if sout != want {
		t.Fatalf("whoami = %q; want %q", out, want)
	}
}

func TestCloneNEWUSERAndRemapRootDisableSetgroups(t *testing.T) {
	if os.Getuid() != 0 {
		t.Skip("skipping root only test")
	}
	testNEWUSERRemap(t, 0, 0, false)
}

func TestCloneNEWUSERAndRemapRootEnableSetgroups(t *testing.T) {
	if os.Getuid() != 0 {
		t.Skip("skipping root only test")
	}
	testNEWUSERRemap(t, 0, 0, false)
}

func TestCloneNEWUSERAndRemapNoRootDisableSetgroups(t *testing.T) {
	if os.Getuid() == 0 {
		t.Skip("skipping unprivileged user only test")
	}
	testNEWUSERRemap(t, os.Getuid(), os.Getgid(), false)
}

func TestCloneNEWUSERAndRemapNoRootSetgroupsEnableSetgroups(t *testing.T) {
	if os.Getuid() == 0 {
		t.Skip("skipping unprivileged user only test")
	}
	cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), true)
	err := cmd.Run()
	if err == nil {
		t.Skip("probably old kernel without security fix")
	}
	if !os.IsPermission(err) {
		t.Fatalf("Unprivileged gid_map rewriting with GidMappingsEnableSetgroups must fail")
	}
}

func TestEmptyCredGroupsDisableSetgroups(t *testing.T) {
	cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), false)
	cmd.SysProcAttr.Credential = &syscall.Credential{}
	if err := cmd.Run(); err != nil {
		t.Fatal(err)
	}
}

func TestUnshare(t *testing.T) {
	// Make sure we are running as root so we have permissions to use unshare
	// and create a network namespace.
	if os.Getuid() != 0 {
		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
	}

	// When running under the Go continuous build, skip tests for
	// now when under Kubernetes. (where things are root but not quite)
	// Both of these are our own environment variables.
	// See Issue 12815.
	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
	}

	cmd := exec.Command("cat", "/proc/net/dev")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Unshare: syscall.CLONE_NEWNET,
	}
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
	}

	// Check there is only the local network interface
	sout := strings.TrimSpace(string(out))
	if !strings.Contains(sout, "lo:") {
		t.Fatalf("Expected lo network interface to exist, got %s", sout)
	}

	lines := strings.Split(sout, "\n")
	if len(lines) != 3 {
		t.Fatalf("Expected 3 lines of output, got %d", len(lines))
	}
}
<|endoftext|>


<commit_before>// +build integration,!no-etcd

package integration

import (
	"crypto/tls"
	"net/http"
	"testing"

	configapi "github.com/openshift/origin/pkg/cmd/server/api"
	testutil "github.com/openshift/origin/test/util"
)

var (
	stableWebConsoleEndpoints     = []string{"healthz", "login"}
	switchableWebConsoleEndpoints = []string{"console", "console/", "console/java"}
)

func tryAccessURL(t *testing.T, url string, expectedStatus int) {
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}

	req, err := http.NewRequest("GET", url, nil)
	req.Header.Set("Accept", "text/html")
	resp, err := transport.RoundTrip(req)
	if err != nil {
		t.Errorf("Unexpected error while accessing %s: %v", url, err)
	}
	if resp.StatusCode != expectedStatus {
		t.Errorf("Expected status %d for %s, got %d", expectedStatus, url, resp.StatusCode)
	}
}

func TestAccessOriginWebConsole(t *testing.T) {
	masterOptions, err := testutil.DefaultMasterOptions()
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if _, err = testutil.StartConfiguredMaster(masterOptions); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	allEndpoints := append(stableWebConsoleEndpoints, switchableWebConsoleEndpoints...)
	for _, endpoint := range allEndpoints {
		url := masterOptions.AssetConfig.MasterPublicURL + "/" + endpoint
		expectedStatus := http.StatusOK
		if endpoint == "console" {
			expectedStatus = http.StatusMovedPermanently
		}
		tryAccessURL(t, url, expectedStatus)
	}
}

func TestAccessDisabledWebConsole(t *testing.T) {
	masterOptions, err := testutil.DefaultMasterOptions()
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	masterOptions.DisabledFeatures.Add(configapi.FeatureWebConsole)
	if _, err := testutil.StartConfiguredMaster(masterOptions); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	for _, endpoint := range stableWebConsoleEndpoints {
		url := masterOptions.AssetConfig.MasterPublicURL + "/" + endpoint
		tryAccessURL(t, url, http.StatusOK)
	}

	for _, endpoint := range switchableWebConsoleEndpoints {
		url := masterOptions.AssetConfig.MasterPublicURL + "/" + endpoint
		tryAccessURL(t, url, http.StatusForbidden)
	}
}
<commit_msg>Test improvements<commit_after>// +build integration,!no-etcd

package integration

import (
	"crypto/tls"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"testing"

	configapi "github.com/openshift/origin/pkg/cmd/server/api"
	testutil "github.com/openshift/origin/test/util"
)

var (
	stableWebConsoleEndpoints = map[string]int{
		"healthz": http.StatusOK,
		"login":   http.StatusOK,
	}
	switchableWebConsoleEndpoints = map[string]int{
		"console":      http.StatusMovedPermanently,
		"console/":     http.StatusOK,
		"console/java": http.StatusOK,
	}
)

func tryAccessURL(t *testing.T, url string, expectedStatus int, expectedRedirectLocation string) *http.Response {
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}

	req, err := http.NewRequest("GET", url, nil)
	req.Header.Set("Accept", "text/html")
	resp, err := transport.RoundTrip(req)
	if err != nil {
		t.Errorf("Unexpected error while accessing %s: %v", url, err)
		return nil
	}
	if resp.StatusCode != expectedStatus {
		t.Errorf("Expected status %d for %s, got %d", expectedStatus, url, resp.StatusCode)
	} else {
		if expectedRedirectLocation != "" {
			if resp.Header.Get("Location") != expectedRedirectLocation {
				t.Errorf("Expected %s for %s, got %s", expectedRedirectLocation, url, resp.Header.Get("Location"))
			}
		}
	}
	return resp
}

func TestAccessOriginWebConsole(t *testing.T) {
	masterOptions, err := testutil.DefaultMasterOptions()
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if _, err = testutil.StartConfiguredMaster(masterOptions); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	tryAccessURL(t, masterOptions.AssetConfig.MasterPublicURL+"/", http.StatusFound, masterOptions.AssetConfig.PublicURL)

	accessEndpoints := func(endpoints map[string]int) {
		for endpoint, expectedStatus := range endpoints {
			url := masterOptions.AssetConfig.MasterPublicURL + "/" + endpoint
			tryAccessURL(t, url, expectedStatus, "")
		}
	}

	accessEndpoints(stableWebConsoleEndpoints)
	accessEndpoints(switchableWebConsoleEndpoints)
}

func TestAccessDisabledWebConsole(t *testing.T) {
	masterOptions, err := testutil.DefaultMasterOptions()
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	masterOptions.DisabledFeatures.Add(configapi.FeatureWebConsole)
	if _, err := testutil.StartConfiguredMaster(masterOptions); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	resp := tryAccessURL(t, masterOptions.AssetConfig.MasterPublicURL+"/", http.StatusOK, "")
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Errorf("failed to read reposponse's body: %v", err)
	} else {
		var value interface{}
		if err = json.Unmarshal(body, &value); err != nil {
			t.Errorf("expected json body which couldn't be parsed: %v, got: %s", err, body)
		}
	}

	for endpoint, expectedStatus := range stableWebConsoleEndpoints {
		url := masterOptions.AssetConfig.MasterPublicURL + "/" + endpoint
		tryAccessURL(t, url, expectedStatus, "")
	}

	for endpoint := range switchableWebConsoleEndpoints {
		url := masterOptions.AssetConfig.MasterPublicURL + "/" + endpoint
		tryAccessURL(t, url, http.StatusForbidden, "")
	}
}
<|endoftext|>


<commit_before>package dnsv2

import (
	//"errors"
	//"fmt"
	//"reflect"
	//"strings"
	"sync"
	//"time"

	"github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1"
)

var (
	zoneWriteLock sync.Mutex
)

// Zone represents a DNS zone
/*{
    "zone": "river.com",
    "type": "secondary",
    "masters": [
        "1.2.3.4",
        "1.2.3.5"
    ],
    "comment": "Adding bodies of water"
}

{
    "activationState": "ACTIVE",
    "contractId": "C-1FRYVV3",
    "lastActivationDate": "2018-03-20T06:49:30Z",
    "lastModifiedBy": "vwwuq65mjvsrbvcr",
    "lastModifiedDate": "2019-01-28T12:05:13Z",
    "signAndServe": false,
    "type": "PRIMARY",
    "versionId": "2e9aa959-5e99-405c-b233-360639449fa1",
    "zone": "akamaideveloper.net"
}

*/
type ZoneQueryString struct {
  ContractId         string   `json:"contractid,omitempty"`
  Gid                string   `json:"lastactivationdate,omitempty"`
}

type ZoneCreate struct {
	Zone               string   `json:"zone,omitempty"`
	Type               string   `json:"type,omitempty"`
	Masters            []string `json:"masters,omitempty"`
	Comment            string   `json:"comment,omitempty"`
//	ContractId         string   `json:"contractid,omitempty"`
//	Gid                string   `json:"lastactivationdate,omitempty"`
}

type ZoneResponse struct {
	Zone               string   `json:"zone,omitempty"`
	Type               string   `json:"type,omitempty"`
	Masters            []string `json:"masters,omitempty"`
	Comment            string   `json:"comment,omitempty"`
	ActivationState    string   `json:"activationstate,omitempty"`
	ContractId         string   `json:"contractid,omitempty"`
	LastActivationDate string   `json:"lastactivationdate,omitempty"`
	LastModifiedBy     string   `json:"lastmodifiedby,omitempty"`
	LastModifiedDate   string   `json:"lastmodifieddate,omitempty"`
	SignAndServe       bool   `json:"signandserve,omitempty"`
	VersionId          string   `json:"versionid,omitempty"`
}

// NewZone creates a new Zone
//func NewZone(contractid string, hostname string) *Zone {
func NewZone(params ZoneCreate) *ZoneCreate {
	//zone := &Zone(ContractId: contractid, Zone: hostname}
	zone := &ZoneCreate{Zone: params.Zone,Type: params.Type, Masters: params.Masters, Comment: params.Comment}
	return zone
}

func NewZoneResponse(params ZoneCreate, paramsquerystring ZoneQueryString) *ZoneResponse {
	//zone := &Zone(ContractId: contractid, Zone: hostname}
	zone := &ZoneResponse{ContractId: paramsquerystring.ContractId,Zone: params.Zone}
	return zone
}

func NewZoneQueryString(ContractId string, gid string) *ZoneQueryString {
	//zone := &Zone(ContractId: contractid, Zone: hostname}
	zonequerystring := &ZoneQueryString{ContractId: ContractId, Gid: gid }
	return zonequerystring
}

// GetZone retrieves a DNS Zone for a given hostname
//func GetZone(contractid string,hostname string) (*ZoneResponse, error) {
func GetZone(params ZoneCreate, paramsquerystring ZoneQueryString) (*ZoneResponse, error) {
	//zone := NewZone(contractid,hostname)
	zone := NewZoneResponse(params,paramsquerystring)
	req, err := client.NewRequest(
		Config,
		"GET",
		"/config-dns/v2/zones/"+zone.Zone,
		nil,
	)
	if err != nil {
		return nil, err
	}

	res, err := client.Do(Config, req)
	if err != nil {
		return nil, err
	}

	if client.IsError(res) && res.StatusCode != 404 {
		return nil, client.NewAPIError(res)
	} else if res.StatusCode == 404 {
		return nil, &ZoneError{zoneName: params.Zone}
	} else {
		err = client.BodyJSON(res, zone)
		if err != nil {
			return nil, err
		}

		return zone, nil
	}
}

// Save updates the Zone
func (zone *ZoneCreate ) Save(zonequerystring ZoneQueryString) error {
	// This lock will restrict the concurrency of API calls
	// to 1 save request at a time. This is needed for the Soa.Serial value which
	// is required to be incremented for every subsequent update to a zone
	// so we have to save just one request at a time to ensure this is always
	// incremented properly
	zoneWriteLock.Lock()
	defer zoneWriteLock.Unlock()

	req, err := client.NewJSONRequest(
		Config,
		"POST",
		"/config-dns/v2/zones/?contractId="+zonequerystring.ContractId+"&gid="+zonequerystring.Gid,
		zone,
	)
	if err != nil {
		return err
	}

	res, err := client.Do(Config, req)

	// Network error
	if err != nil {
		return &ZoneError{
			zoneName:         zone.Zone,
			httpErrorMessage: err.Error(),
			err:              err,
		}
	}

	// API error
	if client.IsError(res) {
		err := client.NewAPIError(res)
		return &ZoneError{zoneName: zone.Zone, apiErrorMessage: err.Detail, err: err}
	}

	/*for {
		updatedZone, err := GetZone(zone.ContractId,zone.Zone)
		if err != nil {
			return err
		}

		if updatedZone.VersionId != zone.VersionId {
			*zone = *updatedZone
			break
		}
		time.Sleep(time.Second)
	}*/

	return nil
}

func (zone *ZoneCreate) Delete(zonequerystring ZoneQueryString) error {
	// remove all the records except for SOA
	// which is required and save the zone
	/*zone.Zone.A = nil
	zone.Zone.Aaaa = nil
	zone.Zone.Afsdb = nil
	zone.Zone.Cname = nil
	zone.Zone.Dnskey = nil
	zone.Zone.Ds = nil
	zone.Zone.Hinfo = nil
	zone.Zone.Loc = nil
	zone.Zone.Mx = nil
	zone.Zone.Naptr = nil
	zone.Zone.Ns = nil
	zone.Zone.Nsec3 = nil
	zone.Zone.Nsec3param = nil
	zone.Zone.Ptr = nil
	zone.Zone.Rp = nil
	zone.Zone.Rrsig = nil
	zone.Zone.Spf = nil
	zone.Zone.Srv = nil
	zone.Zone.Sshfp = nil
	zone.Zone.Txt = nil
*/
	return zone.Save(zonequerystring)
}
<commit_msg>[AT-3][New] Add support for V2 DNS api remove redundant code<commit_after>package dnsv2

import (
	//"errors"
	//"fmt"
	//"reflect"
	//"strings"
	"sync"
	//"time"

	"github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1"
)

var (
	zoneWriteLock sync.Mutex
)

// Zone represents a DNS zone
/*{
    "zone": "river.com",
    "type": "secondary",
    "masters": [
        "1.2.3.4",
        "1.2.3.5"
    ],
    "comment": "Adding bodies of water"
}

{
    "activationState": "ACTIVE",
    "contractId": "C-1FRYVV3",
    "lastActivationDate": "2018-03-20T06:49:30Z",
    "lastModifiedBy": "vwwuq65mjvsrbvcr",
    "lastModifiedDate": "2019-01-28T12:05:13Z",
    "signAndServe": false,
    "type": "PRIMARY",
    "versionId": "2e9aa959-5e99-405c-b233-360639449fa1",
    "zone": "akamaideveloper.net"
}

*/
type ZoneQueryString struct {
  ContractId         string   `json:"contractid,omitempty"`
  Gid                string   `json:"lastactivationdate,omitempty"`
}

type ZoneCreate struct {
	Zone               string   `json:"zone,omitempty"`
	Type               string   `json:"type,omitempty"`
	Masters            []string `json:"masters,omitempty"`
	Comment            string   `json:"comment,omitempty"`
//	ContractId         string   `json:"contractid,omitempty"`
//	Gid                string   `json:"lastactivationdate,omitempty"`
}

type ZoneResponse struct {
	Zone               string   `json:"zone,omitempty"`
	Type               string   `json:"type,omitempty"`
	Masters            []string `json:"masters,omitempty"`
	Comment            string   `json:"comment,omitempty"`
	ActivationState    string   `json:"activationstate,omitempty"`
	ContractId         string   `json:"contractid,omitempty"`
	LastActivationDate string   `json:"lastactivationdate,omitempty"`
	LastModifiedBy     string   `json:"lastmodifiedby,omitempty"`
	LastModifiedDate   string   `json:"lastmodifieddate,omitempty"`
	SignAndServe       bool   `json:"signandserve,omitempty"`
	VersionId          string   `json:"versionid,omitempty"`
}

// NewZone creates a new Zone
//func NewZone(contractid string, hostname string) *Zone {
func NewZone(params ZoneCreate) *ZoneCreate {
	//zone := &Zone(ContractId: contractid, Zone: hostname}
	zone := &ZoneCreate{Zone: params.Zone,Type: params.Type, Masters: params.Masters, Comment: params.Comment}
	return zone
}

func NewZoneResponse(params ZoneCreate) *ZoneResponse {
	//zone := &Zone(ContractId: contractid, Zone: hostname}
	zone := &ZoneResponse{Zone: params.Zone}
	return zone
}

func NewZoneQueryString(ContractId string, gid string) *ZoneQueryString {
	//zone := &Zone(ContractId: contractid, Zone: hostname}
	zonequerystring := &ZoneQueryString{ContractId: ContractId, Gid: gid }
	return zonequerystring
}

// GetZone retrieves a DNS Zone for a given hostname
//func GetZone(contractid string,hostname string) (*ZoneResponse, error) {
func GetZone(params ZoneCreate) (*ZoneResponse, error) {
	//zone := NewZone(contractid,hostname)
	zone := NewZoneResponse(params)
	req, err := client.NewRequest(
		Config,
		"GET",
		"/config-dns/v2/zones/"+zone.Zone,
		nil,
	)
	if err != nil {
		return nil, err
	}

	res, err := client.Do(Config, req)
	if err != nil {
		return nil, err
	}

	if client.IsError(res) && res.StatusCode != 404 {
		return nil, client.NewAPIError(res)
	} else if res.StatusCode == 404 {
		return nil, &ZoneError{zoneName: params.Zone}
	} else {
		err = client.BodyJSON(res, zone)
		if err != nil {
			return nil, err
		}

		return zone, nil
	}
}

// Save updates the Zone
func (zone *ZoneCreate ) Save(zonequerystring ZoneQueryString) error {
	// This lock will restrict the concurrency of API calls
	// to 1 save request at a time. This is needed for the Soa.Serial value which
	// is required to be incremented for every subsequent update to a zone
	// so we have to save just one request at a time to ensure this is always
	// incremented properly
	zoneWriteLock.Lock()
	defer zoneWriteLock.Unlock()

	req, err := client.NewJSONRequest(
		Config,
		"POST",
		"/config-dns/v2/zones/?contractId="+zonequerystring.ContractId+"&gid="+zonequerystring.Gid,
		zone,
	)
	if err != nil {
		return err
	}

	res, err := client.Do(Config, req)

	// Network error
	if err != nil {
		return &ZoneError{
			zoneName:         zone.Zone,
			httpErrorMessage: err.Error(),
			err:              err,
		}
	}

	// API error
	if client.IsError(res) {
		err := client.NewAPIError(res)
		return &ZoneError{zoneName: zone.Zone, apiErrorMessage: err.Detail, err: err}
	}

	/*for {
		updatedZone, err := GetZone(zone.ContractId,zone.Zone)
		if err != nil {
			return err
		}

		if updatedZone.VersionId != zone.VersionId {
			*zone = *updatedZone
			break
		}
		time.Sleep(time.Second)
	}*/

	return nil
}

func (zone *ZoneCreate) Delete(zonequerystring ZoneQueryString) error {
	// remove all the records except for SOA
	// which is required and save the zone
	/*zone.Zone.A = nil
	zone.Zone.Aaaa = nil
	zone.Zone.Afsdb = nil
	zone.Zone.Cname = nil
	zone.Zone.Dnskey = nil
	zone.Zone.Ds = nil
	zone.Zone.Hinfo = nil
	zone.Zone.Loc = nil
	zone.Zone.Mx = nil
	zone.Zone.Naptr = nil
	zone.Zone.Ns = nil
	zone.Zone.Nsec3 = nil
	zone.Zone.Nsec3param = nil
	zone.Zone.Ptr = nil
	zone.Zone.Rp = nil
	zone.Zone.Rrsig = nil
	zone.Zone.Spf = nil
	zone.Zone.Srv = nil
	zone.Zone.Sshfp = nil
	zone.Zone.Txt = nil
*/
	return zone.Save(zonequerystring)
}
<|endoftext|>


<commit_before>package ergo

import (
	"strings"
)

var (
	MaxMemory int64 = 32 << 20 // 32 MB
)

type OperationMap map[string]*Operation

func (om OperationMap) GetOperation(method string) (*Operation, bool) {
	o, ok := om[strings.ToUpper(method)]
	if !ok {
		o, ok = om[""]
	}
	return o, ok
}

// Operation

type Operation struct {
	method      string
	name        string
	description string
	handler     Handler
	params      map[string]*Param
	schemes     []string
	consumes    []string
	produces    []string
}

func NewOperation(handler Handler) *Operation {
	return &Operation{
		handler: handler,
		params:  map[string]*Param{},
	}
}

func ANY(function HandlerFunc) *Operation {
	return HandleANY(HandlerFunc(function))
}

func HandleANY(handler Handler) *Operation {
	return NewOperation(handler).Method(METHOD_ANY)
}

func GET(function HandlerFunc) *Operation {
	return HandleGET(HandlerFunc(function))
}

func HandleGET(handler Handler) *Operation {
	return NewOperation(handler).Method(METHOD_GET)
}

func POST(function HandlerFunc) *Operation {
	return HandlePOST(HandlerFunc(function))
}

func HandlePOST(handler Handler) *Operation {
	return NewOperation(handler).Method(METHOD_POST)
}

func PUT(function HandlerFunc) *Operation {
	return HandlePUT(HandlerFunc(function))
}

func HandlePUT(handler Handler) *Operation {
	return NewOperation(handler).Method(METHOD_PUT)
}

func DELETE(function HandlerFunc) *Operation {
	return HandleDELETE(HandlerFunc(function))
}

func HandleDELETE(handler Handler) *Operation {
	return NewOperation(handler).Method(METHOD_DELETE)
}

func (o *Operation) Description(description string) *Operation {
	o.description = description
	return o
}

func (o *Operation) GetDescription() string {
	return o.description
}

func (o *Operation) Method(method string) *Operation {
	o.method = method
	return o
}

func (o *Operation) GetMethod() string {
	return o.method
}

// Schemes is not additive, meaning that it'll reset the schemes
// already defined with what it's been given if they are valid.
func (o *Operation) Schemes(s ...string) *Operation {
	schemes(o, s)
	return o
}

func (o *Operation) Consumes(mimes ...string) *Operation {
	consumes(o, mimes)
	return o
}

func (o *Operation) Produces(mimes ...string) *Operation {
	produces(o, mimes)
	return o
}

func (o *Operation) GetSchemes() []string {
	return o.schemes
}

func (o *Operation) GetConsumes() []string {
	return o.consumes
}

func (o *Operation) GetProduces() []string {
	return o.produces
}

func (o *Operation) Params(params ...*Param) *Operation {
	addParams(o, params...)
	return o
}

func (o *Operation) GetParams() map[string]*Param {
	return o.params
}

func (o *Operation) GetParamsSlice() []*Param {
	var params []*Param
	for _, p := range o.params {
		params = append(params, p)
	}
	return params
}

func (o *Operation) ResetParams(params ...*Param) *Operation {
	o.setParamsSlice(params...)
	return o
}

func (o *Operation) SetParams(params map[string]*Param) *Operation {
	o.setParams(params)
	return o
}

func (o *Operation) IgnoreParams(params ...string) *Operation {
	ignoreParams(o, params...)
	return o
}

func (o *Operation) IgnoreParamsBut(params ...string) *Operation {
	ignoreParamsBut(o, params...)
	return o
}

func (o *Operation) ServeHTTP(res *Response, req *Request) {
}

func (o *Operation) setSchemes(schemes []string) {
	o.schemes = schemes
}

func (o *Operation) setConsumes(mimes []string) {
	o.consumes = mimes
}

func (o *Operation) setProduces(mimes []string) {
	o.produces = mimes
}

func (o *Operation) setParams(params map[string]*Param) {
	if params == nil {
		params = make(map[string]*Param)
	}
	o.params = params
}

func (o *Operation) setParamsSlice(params ...*Param) {
	paramsMap := map[string]*Param{}
	for _, p := range params {
		o.params[p.name] = p
	}
	o.setParams(paramsMap)
}
<commit_msg>Restructured Operation struct<commit_after>package ergo

import (
	"strings"

	"github.com/wlMalk/ergo/constants"
)

var (
	MaxMemory int64 = 32 << 20 // 32 MB
)

type OperationMap map[string]*Operation

func (om OperationMap) GetOperation(method string) (*Operation, bool) {
	o, ok := om[strings.ToUpper(method)]
	if !ok {
		o, ok = om[""]
	}
	return o, ok
}

// Operation

type Operation struct {
	root          *Ergo
	route         *Route
	method        string
	name          string
	description   string
	handler       Handler
	params        map[string]*Param
	schemes       []string
	consumes      []string
	produces      []string
	containsFiles bool
	bodyParams    bool
}

func NewOperation(handler Handler) *Operation {
	if handler == nil {
		panic("Handler cannot be nil")
	}
	return &Operation{
		handler: handler,
		params:  map[string]*Param{},
	}
}

func ANY(function HandlerFunc) *Operation {
	return HandleANY(HandlerFunc(function))
}

func HandleANY(handler Handler) *Operation {
	return NewOperation(handler).Method(constants.METHOD_ANY)
}

func GET(function HandlerFunc) *Operation {
	return HandleGET(HandlerFunc(function))
}

func HandleGET(handler Handler) *Operation {
	return NewOperation(handler).Method(constants.METHOD_GET)
}

func POST(function HandlerFunc) *Operation {
	return HandlePOST(HandlerFunc(function))
}

func HandlePOST(handler Handler) *Operation {
	return NewOperation(handler).Method(constants.METHOD_POST)
}

func PUT(function HandlerFunc) *Operation {
	return HandlePUT(HandlerFunc(function))
}

func HandlePUT(handler Handler) *Operation {
	return NewOperation(handler).Method(constants.METHOD_PUT)
}

func DELETE(function HandlerFunc) *Operation {
	return HandleDELETE(HandlerFunc(function))
}

func HandleDELETE(handler Handler) *Operation {
	return NewOperation(handler).Method(constants.METHOD_DELETE)
}

// Name sets the name of the operation.
func (o *Operation) Name(name string) *Operation {
	o.name = name
	return o
}

// GetName returns the name of the operation.
func (o *Operation) GetName() string {
	return o.name
}

// GetName returns the description of the operation.
func (o *Operation) GetDescription() string {
	return o.description
}

// Description sets the description of the operation.
func (o *Operation) Description(description string) *Operation {
	o.description = description
	return o
}

func (o *Operation) Method(method string) *Operation {
	if method == constants.METHOD_ANY ||
		method == constants.METHOD_GET ||
		method == constants.METHOD_POST ||
		method == constants.METHOD_PUT ||
		method == constants.METHOD_DELETE {
		o.method = method
	}
	return o
}

func (o *Operation) GetMethod() string {
	return o.method
}

// Schemes is not additive, meaning that it'll reset the schemes
// already defined with what it's been given if they are valid.
func (o *Operation) Schemes(s ...string) *Operation {
	schemes(o, s)
	return o
}

func (o *Operation) Consumes(mimes ...string) *Operation {
	consumes(o, mimes)
	return o
}

func (o *Operation) Produces(mimes ...string) *Operation {
	produces(o, mimes)
	return o
}

func (o *Operation) GetSchemes() []string {
	return o.schemes
}

func (o *Operation) GetConsumes() []string {
	return o.consumes
}

func (o *Operation) GetProduces() []string {
	return o.produces
}

func (o *Operation) Params(params ...*Param) *Operation {
	addParams(o, params...)
	return o
}

func (o *Operation) GetParams() map[string]*Param {
	return o.params
}

func (o *Operation) GetParamsSlice() []*Param {
	var params []*Param
	for _, p := range o.params {
		params = append(params, p)
	}
	return params
}

func (o *Operation) ResetParams(params ...*Param) *Operation {
	o.setParamsSlice(params...)
	return o
}

func (o *Operation) SetParams(params map[string]*Param) *Operation {
	o.setParams(params)
	return o
}

func (o *Operation) IgnoreParams(params ...string) *Operation {
	ignoreParams(o, params...)
	return o
}

func (o *Operation) IgnoreParamsBut(params ...string) *Operation {
	ignoreParamsBut(o, params...)
	return o
}

func (o *Operation) ServeHTTP(res *Response, req *Request) {
}

func (o *Operation) setSchemes(schemes []string) {
	o.schemes = schemes
}

func (o *Operation) setConsumes(mimes []string) {
	o.consumes = mimes
}

func (o *Operation) setProduces(mimes []string) {
	o.produces = mimes
}

func (o *Operation) setParams(params map[string]*Param) {
	if params == nil {
		params = make(map[string]*Param)
	}
	o.params = params
}

func (o *Operation) setParamsSlice(params ...*Param) {
	paramsMap := map[string]*Param{}
	for _, p := range params {
		o.params[p.name] = p
	}
	o.setParams(paramsMap)
}
<|endoftext|>