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

package logger

import (
	"encoding/json"
	_ "fmt"
	"log"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/venicegeo/pz-gocommon/elasticsearch"
	"github.com/venicegeo/pz-gocommon/gocommon"
)

type LockedAdminStats struct {
	sync.Mutex
	LoggerAdminStats
}

type LogData struct {
	sync.Mutex
	esIndex elasticsearch.IIndex
	id      int
}

const schema = "LogData2"

type LoggerService struct {
	stats   LockedAdminStats
	logData LogData
}

func (logger *LoggerService) Init(sys *piazza.SystemConfig, esIndex elasticsearch.IIndex) {
	var err error

	logger.stats.CreatedOn = time.Now()
	/***
	err = esIndex.Delete()
	if err != nil {
		log.Fatal(err)
	}
	if esIndex.IndexExists() {
		log.Fatal("index still exists")
	}
	err = esIndex.Create()
	if err != nil {
		log.Fatal(err)
	}
	***/

	if !esIndex.IndexExists() {
		err = esIndex.Create()
		if err != nil {
			log.Fatal(err)
		}

		mapping :=
			`{
			"LogData2":{
				"properties":{
					"service":{
						"type": "string",
						"store": true
					},
					"address":{
						"type": "string",
						"store": true
					},
					"createdOn":{
						"type": "date",
						"store": true
					},
					"severity":{
						"type": "string",
						"store": true
					},
					"message":{
						"type": "string",
						"store": true
					}
				}
			}
		}`

		err = esIndex.SetMapping(schema, piazza.JsonString(mapping))
		if err != nil {
			log.Fatal(err)
		}
	}

	logger.logData.esIndex = esIndex
}

func (logger *LoggerService) GetRoot() *piazza.JsonResponse {
	resp := &piazza.JsonResponse{
		StatusCode: 200,
		Data:       "Hi. I'm pz-logger.",
	}
	return resp
}

func (logger *LoggerService) PostMessage(mssg *Message) *piazza.JsonResponse {
	err := mssg.Validate()
	if err != nil {
		return &piazza.JsonResponse{StatusCode: http.StatusBadRequest, Message: err.Error()}
	}

	logger.logData.Lock()
	idStr := strconv.Itoa(logger.logData.id)
	logger.logData.id++
	logger.logData.Unlock()

	_, err = logger.logData.esIndex.PostData(schema, idStr, mssg)
	if err != nil {
		//log.Printf("POST failed (1): %#v %#v", err, indexResult)
		return &piazza.JsonResponse{
			StatusCode: http.StatusInternalServerError,
			Message:    err.Error(),
		}
	}

	/*	if !indexResult.Created {
		log.Printf("POST failed (2): %#v", *indexResult)
		resp := &piazza.JsonResponse{
			StatusCode: http.StatusInternalServerError,
			Message:    "POST of log data failed",
		}
		return resp
	}*/

	logger.stats.LoggerAdminStats.NumMessages++

	resp := &piazza.JsonResponse{
		StatusCode: http.StatusOK,
		Data:       mssg,
	}

	return resp
}

func (logger *LoggerService) GetStats() *piazza.JsonResponse {
	logger.logData.Lock()
	t := logger.stats.LoggerAdminStats
	logger.logData.Unlock()
	resp := &piazza.JsonResponse{
		StatusCode: http.StatusOK,
		Data:       t,
	}
	return resp
}

func (logger *LoggerService) GetMessage(params *piazza.HttpQueryParams) *piazza.JsonResponse {
	var err error

	var formalPagination *piazza.JsonPagination
	{
		defaults := &piazza.JsonPagination{
			PerPage: 10,
			Page:    0,
			Order:   piazza.PaginationOrderDescending,
			SortBy:  "createdOn",
		}
		formalPagination, err = piazza.NewJsonPagination(params, defaults)
		if err != nil {
			return &piazza.JsonResponse{StatusCode: http.StatusBadRequest, Message: err.Error()}
		}
	}

	filterParams := logger.parseFilterParams(params)

	//log.Printf("size %d, from %d, key %s, format %v",
	//	format.Size, format.From, format.Key, format.Order)

	//log.Printf("filterParams: %v\n", filterParams)

	var searchResult *elasticsearch.SearchResult

	if len(filterParams) == 0 {
		searchResult, err = logger.logData.esIndex.FilterByMatchAll(schema, formalPagination)
	} else {
		var jsonString = logger.createQueryDslAsString(formalPagination, filterParams)
		searchResult, err = logger.logData.esIndex.SearchByJSON(schema, jsonString)
	}

	if err != nil {
		resp := &piazza.JsonResponse{
			StatusCode: http.StatusInternalServerError,
			Message:    "query failed: " + err.Error(),
		}
		return resp
	}

	// TODO: unsafe truncation
	count := int(searchResult.TotalHits())
	matched := int(searchResult.NumberMatched())
	lines := make([]Message, count)

	i := 0
	for _, hit := range *searchResult.GetHits() {
		if hit == nil {
			log.Printf("null source hit")
			continue
		}
		src := *hit.Source
		//log.Printf("source hit: %s", string(src))

		tmp := &Message{}
		err = json.Unmarshal(src, tmp)
		if err != nil {
			log.Printf("UNABLE TO PARSE: %s", string(*hit.Source))
			resp := &piazza.JsonResponse{
				StatusCode: http.StatusInternalServerError,
				Message:    "unmarshall failed: " + err.Error(),
			}
			return resp
		}

		// still needed?
		err = tmp.Validate()
		if err != nil {
			log.Printf("UNABLE TO VALIDATE: %s", string(*hit.Source))
			continue
		}

		lines[i] = *tmp
		i++
	}

	bar := make([]interface{}, len(lines))

	for i, e := range lines {
		bar[i] = e
	}

	formalPagination.Count = matched
	resp := &piazza.JsonResponse{
		StatusCode: http.StatusOK,
		Data:       bar,
		Pagination: formalPagination,
	}

	return resp
}

func (logger *LoggerService) parseFilterParams(params *piazza.HttpQueryParams) map[string]interface{} {

	var filterParams = map[string]interface{}{}

	before := params.Get("before")

	if before != "" {
		num, err := strconv.Atoi(before)
		if err == nil {
			filterParams["before"] = num
		}
	}

	after := params.Get("after")

	if after != "" {
		num, err := strconv.Atoi(after)
		if err == nil {
			filterParams["after"] = num
		}
	}

	service := params.Get("service")

	if service != "" {
		filterParams["service"] = service
	}

	contains := params.Get("contains")

	if contains != "" {
		filterParams["contains"] = contains
	}

	return filterParams
}

func (logger *LoggerService) createQueryDslAsString(
	format *piazza.JsonPagination,
	params map[string]interface{},
) string {
	// fmt.Printf("%d\n", len(params))

	must := []map[string]interface{}{}

	if params["service"] != nil {
		must = append(must, map[string]interface{}{
			"match": map[string]interface{}{
				"service": params["service"],
			},
		})
	}

	if params["contains"] != nil {
		must = append(must, map[string]interface{}{
			"multi_match": map[string]interface{}{
				"query":  params["contains"],
				"fields": []string{"address", "message", "service", "severity"},
			},
		})
	}

	if params["after"] != nil || params["before"] != nil {
		rangeParams := map[string]int{}

		if params["after"] != nil {
			rangeParams["gte"] = params["after"].(int)
		}

		if params["before"] != nil {
			rangeParams["lte"] = params["before"].(int)
		}

		must = append(must, map[string]interface{}{
			"range": map[string]interface{}{
				"stamp": rangeParams,
			},
		})
	}

	dsl := map[string]interface{}{
		"query": map[string]interface{}{
			"filtered": map[string]interface{}{
				"query": map[string]interface{}{
					"bool": map[string]interface{}{
						"must": must,
					},
				},
			},
		},
		"size": format.PerPage,
		"from": format.PerPage * format.Page,
	}

	dsl["sort"] = map[string]string{
		format.SortBy: string(format.Order),
	}

	output, _ := json.Marshal(dsl)
	return string(output)
}
<commit_msg>Use Create() with no settings<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package logger

import (
	"encoding/json"
	_ "fmt"
	"log"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/venicegeo/pz-gocommon/elasticsearch"
	"github.com/venicegeo/pz-gocommon/gocommon"
)

type LockedAdminStats struct {
	sync.Mutex
	LoggerAdminStats
}

type LogData struct {
	sync.Mutex
	esIndex elasticsearch.IIndex
	id      int
}

const schema = "LogData2"

type LoggerService struct {
	stats   LockedAdminStats
	logData LogData
}

func (logger *LoggerService) Init(sys *piazza.SystemConfig, esIndex elasticsearch.IIndex) {
	var err error

	logger.stats.CreatedOn = time.Now()
	/***
	err = esIndex.Delete()
	if err != nil {
		log.Fatal(err)
	}
	if esIndex.IndexExists() {
		log.Fatal("index still exists")
	}
	err = esIndex.Create()
	if err != nil {
		log.Fatal(err)
	}
	***/

	if !esIndex.IndexExists() {
		err = esIndex.Create("")
		if err != nil {
			log.Fatal(err)
		}

		mapping :=
			`{
			"LogData2":{
				"properties":{
					"service":{
						"type": "string",
						"store": true
					},
					"address":{
						"type": "string",
						"store": true
					},
					"createdOn":{
						"type": "date",
						"store": true
					},
					"severity":{
						"type": "string",
						"store": true
					},
					"message":{
						"type": "string",
						"store": true
					}
				}
			}
		}`

		err = esIndex.SetMapping(schema, piazza.JsonString(mapping))
		if err != nil {
			log.Fatal(err)
		}
	}

	logger.logData.esIndex = esIndex
}

func (logger *LoggerService) GetRoot() *piazza.JsonResponse {
	resp := &piazza.JsonResponse{
		StatusCode: 200,
		Data:       "Hi. I'm pz-logger.",
	}
	return resp
}

func (logger *LoggerService) PostMessage(mssg *Message) *piazza.JsonResponse {
	err := mssg.Validate()
	if err != nil {
		return &piazza.JsonResponse{StatusCode: http.StatusBadRequest, Message: err.Error()}
	}

	logger.logData.Lock()
	idStr := strconv.Itoa(logger.logData.id)
	logger.logData.id++
	logger.logData.Unlock()

	_, err = logger.logData.esIndex.PostData(schema, idStr, mssg)
	if err != nil {
		//log.Printf("POST failed (1): %#v %#v", err, indexResult)
		return &piazza.JsonResponse{
			StatusCode: http.StatusInternalServerError,
			Message:    err.Error(),
		}
	}

	/*	if !indexResult.Created {
		log.Printf("POST failed (2): %#v", *indexResult)
		resp := &piazza.JsonResponse{
			StatusCode: http.StatusInternalServerError,
			Message:    "POST of log data failed",
		}
		return resp
	}*/

	logger.stats.LoggerAdminStats.NumMessages++

	resp := &piazza.JsonResponse{
		StatusCode: http.StatusOK,
		Data:       mssg,
	}

	return resp
}

func (logger *LoggerService) GetStats() *piazza.JsonResponse {
	logger.logData.Lock()
	t := logger.stats.LoggerAdminStats
	logger.logData.Unlock()
	resp := &piazza.JsonResponse{
		StatusCode: http.StatusOK,
		Data:       t,
	}
	return resp
}

func (logger *LoggerService) GetMessage(params *piazza.HttpQueryParams) *piazza.JsonResponse {
	var err error

	var formalPagination *piazza.JsonPagination
	{
		defaults := &piazza.JsonPagination{
			PerPage: 10,
			Page:    0,
			Order:   piazza.PaginationOrderDescending,
			SortBy:  "createdOn",
		}
		formalPagination, err = piazza.NewJsonPagination(params, defaults)
		if err != nil {
			return &piazza.JsonResponse{StatusCode: http.StatusBadRequest, Message: err.Error()}
		}
	}

	filterParams := logger.parseFilterParams(params)

	//log.Printf("size %d, from %d, key %s, format %v",
	//	format.Size, format.From, format.Key, format.Order)

	//log.Printf("filterParams: %v\n", filterParams)

	var searchResult *elasticsearch.SearchResult

	if len(filterParams) == 0 {
		searchResult, err = logger.logData.esIndex.FilterByMatchAll(schema, formalPagination)
	} else {
		var jsonString = logger.createQueryDslAsString(formalPagination, filterParams)
		searchResult, err = logger.logData.esIndex.SearchByJSON(schema, jsonString)
	}

	if err != nil {
		resp := &piazza.JsonResponse{
			StatusCode: http.StatusInternalServerError,
			Message:    "query failed: " + err.Error(),
		}
		return resp
	}

	// TODO: unsafe truncation
	count := int(searchResult.TotalHits())
	matched := int(searchResult.NumberMatched())
	lines := make([]Message, count)

	i := 0
	for _, hit := range *searchResult.GetHits() {
		if hit == nil {
			log.Printf("null source hit")
			continue
		}
		src := *hit.Source
		//log.Printf("source hit: %s", string(src))

		tmp := &Message{}
		err = json.Unmarshal(src, tmp)
		if err != nil {
			log.Printf("UNABLE TO PARSE: %s", string(*hit.Source))
			resp := &piazza.JsonResponse{
				StatusCode: http.StatusInternalServerError,
				Message:    "unmarshall failed: " + err.Error(),
			}
			return resp
		}

		// still needed?
		err = tmp.Validate()
		if err != nil {
			log.Printf("UNABLE TO VALIDATE: %s", string(*hit.Source))
			continue
		}

		lines[i] = *tmp
		i++
	}

	bar := make([]interface{}, len(lines))

	for i, e := range lines {
		bar[i] = e
	}

	formalPagination.Count = matched
	resp := &piazza.JsonResponse{
		StatusCode: http.StatusOK,
		Data:       bar,
		Pagination: formalPagination,
	}

	return resp
}

func (logger *LoggerService) parseFilterParams(params *piazza.HttpQueryParams) map[string]interface{} {

	var filterParams = map[string]interface{}{}

	before := params.Get("before")

	if before != "" {
		num, err := strconv.Atoi(before)
		if err == nil {
			filterParams["before"] = num
		}
	}

	after := params.Get("after")

	if after != "" {
		num, err := strconv.Atoi(after)
		if err == nil {
			filterParams["after"] = num
		}
	}

	service := params.Get("service")

	if service != "" {
		filterParams["service"] = service
	}

	contains := params.Get("contains")

	if contains != "" {
		filterParams["contains"] = contains
	}

	return filterParams
}

func (logger *LoggerService) createQueryDslAsString(
	format *piazza.JsonPagination,
	params map[string]interface{},
) string {
	// fmt.Printf("%d\n", len(params))

	must := []map[string]interface{}{}

	if params["service"] != nil {
		must = append(must, map[string]interface{}{
			"match": map[string]interface{}{
				"service": params["service"],
			},
		})
	}

	if params["contains"] != nil {
		must = append(must, map[string]interface{}{
			"multi_match": map[string]interface{}{
				"query":  params["contains"],
				"fields": []string{"address", "message", "service", "severity"},
			},
		})
	}

	if params["after"] != nil || params["before"] != nil {
		rangeParams := map[string]int{}

		if params["after"] != nil {
			rangeParams["gte"] = params["after"].(int)
		}

		if params["before"] != nil {
			rangeParams["lte"] = params["before"].(int)
		}

		must = append(must, map[string]interface{}{
			"range": map[string]interface{}{
				"stamp": rangeParams,
			},
		})
	}

	dsl := map[string]interface{}{
		"query": map[string]interface{}{
			"filtered": map[string]interface{}{
				"query": map[string]interface{}{
					"bool": map[string]interface{}{
						"must": must,
					},
				},
			},
		},
		"size": format.PerPage,
		"from": format.PerPage * format.Page,
	}

	dsl["sort"] = map[string]string{
		format.SortBy: string(format.Order),
	}

	output, _ := json.Marshal(dsl)
	return string(output)
}
<|endoftext|>


<commit_before>package node

import (
	"bytes"
	"fmt"
	"github.com/AutoRoute/l2"
)

// The layer two protocol takes a layer two device and returns the hash of the
// Public Key of all neighbors it can find.
type NeighborFinder interface {
	Find(l2.FrameReadWriter) <-chan string
}

type NeighborData struct {
	pk PublicKey
}

func NewNeighborData(pk PublicKey) NeighborData {
	return NeighborData{pk}
}
func (n NeighborData) Find(mac string, frw l2.FrameReadWriter) (<-chan string, error) {
	c := make(chan string)
	// Broadcast Hash
	broadcastAddr, errb := l2.MacToBytes("ff:ff:ff:ff:ff:ff")
	if errb != nil {
		return c, errb
	}
	localAddr, errl := l2.MacToBytes(mac) // TODO: decide on mac passing before merging
	if errl != nil {
		return c, errl
	}
	var protocol uint16 = 31337 // TODO: add real protocol
	publicKeyHash := []byte(n.pk.Hash())
	initFrame := l2.NewEthFrame(broadcastAddr, localAddr, protocol, publicKeyHash)
	fmt.Println("Broadcasting packet.")
	var err error = frw.WriteFrame(initFrame) // TODO: check errors
	if err != nil {
		panic(err)
	}
	fmt.Println("Broadcasted packet.")
	// Process Loop
	go func() {
		for {
			fmt.Println("Receiving packet.")
			newInstanceFrame, _ := frw.ReadFrame()
			src := newInstanceFrame.Source()
			dest := newInstanceFrame.Destination()
			fmt.Printf("Received packet from %v.\n", src)
			fmt.Printf("Received packet to %v.\n", dest)
			if newInstanceFrame.Type() != protocol {
				continue // Throw away if protocols don't match
			}
			if bytes.Equal(src, localAddr) {
				continue // Throw away if from me
			}
			if !(bytes.Equal(dest, localAddr) || bytes.Equal(dest, broadcastAddr)) {
				continue // Throw away if it wasn't to me or the broadcast address
			}
			c <- string(newInstanceFrame.Data())
			if bytes.Equal(dest, broadcastAddr) { // Respond if to broadcast addr
				var p PublicKey = pktest("test2") // TODO: pass public key
				publicKeyHash := []byte(p.Hash())
				initFrame := l2.NewEthFrame(src, localAddr, 31337, publicKeyHash) // TODO: add real protocol
				fmt.Printf("Sending response packet %v.\n", src)
				var err error = frw.WriteFrame(initFrame) // TODO: check errors
				if err != nil {
					panic(err)
				}
				fmt.Println("Sent response packet.")
			}
		}
	}()
	return c, nil // TODO: return channel error?
}
<commit_msg>Pathced neighbor_finder.go from nf-testing. Removed some parts of test messages.<commit_after>package node

import (
	"bytes"
	"fmt"
	"github.com/AutoRoute/l2"
)

// The layer two protocol takes a layer two device and returns the hash of the
// Public Key of all neighbors it can find.
type NeighborFinder interface {
	Find(l2.FrameReadWriter) <-chan string
}

type NeighborData struct {
	pk PublicKey
}

func NewNeighborData(pk PublicKey) NeighborData {
	return NeighborData{pk}
}

func (n NeighborData) Find(mac string, frw l2.FrameReadWriter) (<-chan string, error) {
	c := make(chan string)
	// Broadcast Hash
	broadcastAddr, errb := l2.MacToBytes("ff:ff:ff:ff:ff:ff")
	if errb != nil {
		return c, errb
	}
	localAddr, errl := l2.MacToBytes(mac) // TODO: decide on mac passing before merging
	if errl != nil {
		return c, errl
	}
	var protocol uint16 = 31337 // TODO: add real protocol
	publicKeyHash := []byte(n.pk.Hash())
	initFrame := l2.NewEthFrame(broadcastAddr, localAddr, protocol, publicKeyHash)
	fmt.Println("Broadcasting packet.")
	var err error = frw.WriteFrame(initFrame) // TODO: check errors
	if err != nil {
		panic(err)
	}
	fmt.Println("Broadcasted packet.")
	// Process Loop
	go func() {
		for {
			fmt.Println("Receiving packet.")
			newInstanceFrame, _ := frw.ReadFrame()
			src := newInstanceFrame.Source()
			dest := newInstanceFrame.Destination()
			fmt.Printf("Received packet from %v.\n", src)
			fmt.Printf("Received packet to %v.\n", dest)
			if newInstanceFrame.Type() != protocol {
				continue // Throw away if protocols don't match
			}
			if bytes.Equal(src, localAddr) {
				continue // Throw away if from me
			}
			if !(bytes.Equal(dest, localAddr) || bytes.Equal(dest, broadcastAddr)) {
				continue // Throw away if it wasn't to me or the broadcast address
			}
			c <- string(newInstanceFrame.Data())
			if bytes.Equal(dest, broadcastAddr) { // Respond if to broadcast addr
				initFrame := l2.NewEthFrame(src, localAddr, 31337, publicKeyHash) // TODO: add real protocol
				fmt.Printf("Sending response packet %v.\n", src)
				var err error = frw.WriteFrame(initFrame) // TODO: check errors
				if err != nil {
					panic(err)
				}
				fmt.Println("Sent response packet.")
			}
		}
	}()
	return c, nil // TODO: return channel error?
}
<|endoftext|>


<commit_before>package container

import (
	"bufio"
	crand "crypto/rand"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"time"

	log "github.com/sirupsen/logrus"

	dockerclient "github.com/ivanilves/lstags/docker/client"
	dockerconfig "github.com/ivanilves/lstags/docker/config"
	"github.com/ivanilves/lstags/repository"
	"github.com/ivanilves/lstags/util/getenv"
	"github.com/ivanilves/lstags/util/wait"
)

const (
	imageRef   = "registry:2"
	baseName   = "registry"
	basePort   = 5000
	retryCount = 5
)

// Container is a Docker container running Docker registry inside
type Container struct {
	id           string
	hostname     string
	dockerClient *dockerclient.DockerClient
}

func getRandomPort() int {
	rand.Seed(time.Now().UnixNano())

	b := make([]byte, 1)

	crand.Read(b)

	return basePort + int(b[0]) + rand.Intn(400)
}

func getDockerClient() (*dockerclient.DockerClient, error) {
	dockerConfig, _ := dockerconfig.Load(dockerconfig.DefaultDockerJSON)

	return dockerclient.New(dockerConfig)
}

func getHostname(port int) string {
	return fmt.Sprintf("%s:%d", getenv.String("LOCAL_REGISTRY", "127.0.0.1"), port)
}

func run(dockerClient *dockerclient.DockerClient, hostPort int) (string, error) {
	portSpec := fmt.Sprintf("0.0.0.0:%d:%d", hostPort, basePort)

	name := fmt.Sprintf("%s-%d", baseName, hostPort)

	id, err := dockerClient.Run(imageRef, name, []string{portSpec})
	if err != nil {
		return "", err
	}

	return id, nil
}

func verify(hostname string) error {
	url := fmt.Sprintf("http://%s/v2/", hostname)

	var err error
	for retry := 0; retry < retryCount; retry++ {
		time.Sleep(1 * time.Second)

		_, err = http.Get(url)
		if err == nil {
			break
		}
	}

	return err
}

// Launch launches a Docker container with Docker registry inside
func Launch() (*Container, error) {
	dockerClient, _ := getDockerClient()

	hostPort := getRandomPort()

	id, _ := run(dockerClient, hostPort)

	hostname := getHostname(hostPort)

	return &Container{id: id, hostname: hostname, dockerClient: dockerClient}, verify(hostname)
}

// ID gets container ID
func (c *Container) ID() string {
	return c.id
}

// Hostname gets hostname to access Docker registry we run inside our container
func (c *Container) Hostname() string {
	return c.hostname
}

// Destroy force-stops and destroys Docker container with registry
func (c *Container) Destroy() error {
	return c.dockerClient.ForceRemove(c.id)
}

// SeedWithImages pulls specified images from whatever registry
// and then re-pushes em to our [local] container-based registry
func (c *Container) SeedWithImages(refs ...string) ([]string, error) {
	if len(refs) == 0 {
		return nil, fmt.Errorf("must pass one or more image references")
	}

	pushRefs := make([]string, len(refs))

	done := make(chan error, len(refs))

	for i, ref := range refs {
		go func(i int, ref string) {
			repo, err := repository.ParseRef(ref)
			if err != nil {
				done <- err
				return
			}
			if !repo.IsSingle() {
				done <- fmt.Errorf("invalid reference: %s (only REPOSITORY:TAG form is allowed)", ref)
				return
			}

			tag := repo.Tags()[0]

			pushRef := fmt.Sprintf("%s%s%s:%s", c.hostname, repo.PushPrefix(), repo.Path(), tag)

			pushRepo, _ := repository.ParseRef(pushRef)

			src := fmt.Sprintf("%s:%s", repo.Name(), tag)
			dst := fmt.Sprintf("%s:%s", pushRepo.Name(), tag)

			pushRefs[i] = pushRef

			pullResp, err := c.dockerClient.Pull(src)
			if err != nil {
				done <- err
				return
			}
			logDebugData(pullResp)

			c.dockerClient.Tag(src, dst)

			pushResp, err := c.dockerClient.Push(dst)
			if err != nil {
				done <- err
				return
			}
			logDebugData(pushResp)

			done <- err
		}(i, ref)
	}

	return pushRefs, wait.Until(done)
}

func logDebugData(data io.Reader) {
	scanner := bufio.NewScanner(data)
	for scanner.Scan() {
		log.Debug(scanner.Text())
	}
}
<commit_msg>Retry count increased<commit_after>package container

import (
	"bufio"
	crand "crypto/rand"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"time"

	log "github.com/sirupsen/logrus"

	dockerclient "github.com/ivanilves/lstags/docker/client"
	dockerconfig "github.com/ivanilves/lstags/docker/config"
	"github.com/ivanilves/lstags/repository"
	"github.com/ivanilves/lstags/util/getenv"
	"github.com/ivanilves/lstags/util/wait"
)

const (
	imageRef   = "registry:2"
	baseName   = "registry"
	basePort   = 5000
	retryCount = 10
)

// Container is a Docker container running Docker registry inside
type Container struct {
	id           string
	hostname     string
	dockerClient *dockerclient.DockerClient
}

func getRandomPort() int {
	rand.Seed(time.Now().UnixNano())

	b := make([]byte, 1)

	crand.Read(b)

	return basePort + int(b[0]) + rand.Intn(400)
}

func getDockerClient() (*dockerclient.DockerClient, error) {
	dockerConfig, _ := dockerconfig.Load(dockerconfig.DefaultDockerJSON)

	return dockerclient.New(dockerConfig)
}

func getHostname(port int) string {
	return fmt.Sprintf("%s:%d", getenv.String("LOCAL_REGISTRY", "127.0.0.1"), port)
}

func run(dockerClient *dockerclient.DockerClient, hostPort int) (string, error) {
	portSpec := fmt.Sprintf("0.0.0.0:%d:%d", hostPort, basePort)

	name := fmt.Sprintf("%s-%d", baseName, hostPort)

	id, err := dockerClient.Run(imageRef, name, []string{portSpec})
	if err != nil {
		return "", err
	}

	return id, nil
}

func verify(hostname string) error {
	url := fmt.Sprintf("http://%s/v2/", hostname)

	var err error
	for retry := 0; retry < retryCount; retry++ {
		time.Sleep(1 * time.Second)

		_, err = http.Get(url)
		if err == nil {
			break
		}
	}

	return err
}

// Launch launches a Docker container with Docker registry inside
func Launch() (*Container, error) {
	dockerClient, _ := getDockerClient()

	hostPort := getRandomPort()

	id, _ := run(dockerClient, hostPort)

	hostname := getHostname(hostPort)

	return &Container{id: id, hostname: hostname, dockerClient: dockerClient}, verify(hostname)
}

// ID gets container ID
func (c *Container) ID() string {
	return c.id
}

// Hostname gets hostname to access Docker registry we run inside our container
func (c *Container) Hostname() string {
	return c.hostname
}

// Destroy force-stops and destroys Docker container with registry
func (c *Container) Destroy() error {
	return c.dockerClient.ForceRemove(c.id)
}

// SeedWithImages pulls specified images from whatever registry
// and then re-pushes em to our [local] container-based registry
func (c *Container) SeedWithImages(refs ...string) ([]string, error) {
	if len(refs) == 0 {
		return nil, fmt.Errorf("must pass one or more image references")
	}

	pushRefs := make([]string, len(refs))

	done := make(chan error, len(refs))

	for i, ref := range refs {
		go func(i int, ref string) {
			repo, err := repository.ParseRef(ref)
			if err != nil {
				done <- err
				return
			}
			if !repo.IsSingle() {
				done <- fmt.Errorf("invalid reference: %s (only REPOSITORY:TAG form is allowed)", ref)
				return
			}

			tag := repo.Tags()[0]

			pushRef := fmt.Sprintf("%s%s%s:%s", c.hostname, repo.PushPrefix(), repo.Path(), tag)

			pushRepo, _ := repository.ParseRef(pushRef)

			src := fmt.Sprintf("%s:%s", repo.Name(), tag)
			dst := fmt.Sprintf("%s:%s", pushRepo.Name(), tag)

			pushRefs[i] = pushRef

			pullResp, err := c.dockerClient.Pull(src)
			if err != nil {
				done <- err
				return
			}
			logDebugData(pullResp)

			c.dockerClient.Tag(src, dst)

			pushResp, err := c.dockerClient.Push(dst)
			if err != nil {
				done <- err
				return
			}
			logDebugData(pushResp)

			done <- err
		}(i, ref)
	}

	return pushRefs, wait.Until(done)
}

func logDebugData(data io.Reader) {
	scanner := bufio.NewScanner(data)
	for scanner.Scan() {
		log.Debug(scanner.Text())
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package network

import (
	"fmt"
	"net"
	"sort"

	"github.com/juju/loggo"
)

var logger = loggo.GetLogger("juju.network")

// TODO(dimitern): Remove this once we use spaces as per the model.
const (
	// Id of the default public juju network
	DefaultPublic = "juju-public"

	// Id of the default private juju network
	DefaultPrivate = "juju-private"
)

// Id defines a provider-specific network id.
type Id string

// SubnetInfo describes the bare minimum information for a subnet,
// which the provider knows about but juju might not yet.
type SubnetInfo struct {
	// CIDR of the network, in 123.45.67.89/24 format. Can be empty if
	// unknown.
	CIDR string

	// ProviderId is a provider-specific network id. This the only
	// required field.
	ProviderId Id

	// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
	// normal networks. It's defined by IEEE 802.1Q standard, and used
	// to define a VLAN network. For more information, see:
	// http://en.wikipedia.org/wiki/IEEE_802.1Q.
	VLANTag int

	// AllocatableIPLow and AllocatableIPHigh describe the allocatable
	// portion of the subnet. The provider will only permit allocation
	// between these limits. If they are empty then none of the subnet is
	// allocatable.
	AllocatableIPLow  net.IP
	AllocatableIPHigh net.IP
}

// InterfaceConfigType defines valid network interface configuration
// types. See interfaces(5) for details
type InterfaceConfigType string

const (
	ConfigUnknown InterfaceConfigType = ""
	ConfigDHCP    InterfaceConfigType = "dhcp"
	ConfigStatic  InterfaceConfigType = "static"
	ConfigManual  InterfaceConfigType = "manual"
	// add others when needed
)

// InterfaceInfo describes a single network interface available on an
// instance. For providers that support networks, this will be
// available at StartInstance() time.
type InterfaceInfo struct {
	// DeviceIndex specifies the order in which the network interface
	// appears on the host. The primary interface has an index of 0.
	DeviceIndex int

	// MACAddress is the network interface's hardware MAC address
	// (e.g. "aa:bb:cc:dd:ee:ff").
	MACAddress string

	// CIDR of the network, in 123.45.67.89/24 format.
	CIDR string

	// NetworkName is juju-internal name of the network.
	NetworkName string

	// ProviderId is a provider-specific NIC id.
	ProviderId Id

	// ProviderSubnetId is the provider-specific id for the associated
	// subnet.
	ProviderSubnetId Id

	// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
	// normal networks. It's defined by IEEE 802.1Q standard.
	VLANTag int

	// InterfaceName is the raw OS-specific network device name (e.g.
	// "eth1", even for a VLAN eth1.42 virtual interface).
	InterfaceName string

	// Disabled is true when the interface needs to be disabled on the
	// machine, e.g. not to configure it.
	Disabled bool

	// NoAutoStart is true when the interface should not be configured
	// to start automatically on boot. By default and for
	// backwards-compatibility, interfaces are configured to
	// auto-start.
	NoAutoStart bool

	// ConfigType determines whether the interface should be
	// configured via DHCP, statically, manually, etc. See
	// interfaces(5) for more information.
	ConfigType InterfaceConfigType

	// Address contains an optional static IP address to configure for
	// this network interface. The subnet mask to set will be inferred
	// from the CIDR value.
	Address Address

	// DNSServers contains an optional list of IP addresses and/or
	// hostnames to configure as DNS servers for this network
	// interface.
	DNSServers []Address

	// Gateway address, if set, defines the default gateway to
	// configure for this network interface. For containers this
	// usually is (one of) the host address(es).
	GatewayAddress Address

	// ExtraConfig can contain any valid setting and its value allowed
	// inside an "iface" section of a interfaces(5) config file, e.g.
	// "up", "down", "mtu", etc.
	ExtraConfig map[string]string
}

type interfaceInfoSlice []InterfaceInfo

func (s interfaceInfoSlice) Len() int      { return len(s) }
func (s interfaceInfoSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s interfaceInfoSlice) Less(i, j int) bool {
	iface1 := s[i]
	iface2 := s[j]
	return iface1.DeviceIndex < iface2.DeviceIndex
}

// Sort a slice of InterfaceInfo on DeviceIndex in ascending order
func SortInterfaceInfo(interfaces []InterfaceInfo) {
	sort.Sort(interfaceInfoSlice(interfaces))
}

// ActualInterfaceName returns raw interface name for raw interface (e.g. "eth0") and
// virtual interface name for virtual interface (e.g. "eth0.42")
func (i *InterfaceInfo) ActualInterfaceName() string {
	if i.VLANTag > 0 {
		return fmt.Sprintf("%s.%d", i.InterfaceName, i.VLANTag)
	}
	return i.InterfaceName
}

// IsVirtual returns true when the interface is a virtual device, as
// opposed to a physical device (e.g. a VLAN or a network alias)
func (i *InterfaceInfo) IsVirtual() bool {
	return i.VLANTag > 0
}

// IsVLAN returns true when the interface is a VLAN interface.
func (i *InterfaceInfo) IsVLAN() bool {
	return i.VLANTag > 0
}

// PreferIPv6Getter will be implemented by both the environment and agent
// config.
type PreferIPv6Getter interface {
	PreferIPv6() bool
}

// InitializeFromConfig needs to be called once after the environment
// or agent configuration is available to configure networking
// settings.
func InitializeFromConfig(config PreferIPv6Getter) {
	globalPreferIPv6 = config.PreferIPv6()
	logger.Infof("setting prefer-ipv6 to %v", globalPreferIPv6)
}
<commit_msg>Fix the commment properly<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package network

import (
	"fmt"
	"net"
	"sort"

	"github.com/juju/loggo"
)

var logger = loggo.GetLogger("juju.network")

// TODO(dimitern): Remove this once we use spaces as per the model.
const (
	// Id of the default public juju network
	DefaultPublic = "juju-public"

	// Id of the default private juju network
	DefaultPrivate = "juju-private"
)

// Id defines a provider-specific network id.
type Id string

// SubnetInfo describes the bare minimum information for a subnet,
// which the provider knows about but juju might not yet.
type SubnetInfo struct {
	// CIDR of the network, in 123.45.67.89/24 format. Can be empty if
	// unknown.
	CIDR string

	// ProviderId is a provider-specific network id. This the only
	// required field.
	ProviderId Id

	// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
	// normal networks. It's defined by IEEE 802.1Q standard, and used
	// to define a VLAN network. For more information, see:
	// http://en.wikipedia.org/wiki/IEEE_802.1Q.
	VLANTag int

	// AllocatableIPLow and AllocatableIPHigh describe the allocatable
	// portion of the subnet. The provider will only permit allocation
	// between these limits. If they are empty then none of the subnet is
	// allocatable.
	AllocatableIPLow  net.IP
	AllocatableIPHigh net.IP
}

// InterfaceConfigType defines valid network interface configuration
// types. See interfaces(5) for details
type InterfaceConfigType string

const (
	ConfigUnknown InterfaceConfigType = ""
	ConfigDHCP    InterfaceConfigType = "dhcp"
	ConfigStatic  InterfaceConfigType = "static"
	ConfigManual  InterfaceConfigType = "manual"
	// add others when needed
)

// InterfaceInfo describes a single network interface available on an
// instance. For providers that support networks, this will be
// available at StartInstance() time.
type InterfaceInfo struct {
	// DeviceIndex specifies the order in which the network interface
	// appears on the host. The primary interface has an index of 0.
	DeviceIndex int

	// MACAddress is the network interface's hardware MAC address
	// (e.g. "aa:bb:cc:dd:ee:ff").
	MACAddress string

	// CIDR of the network, in 123.45.67.89/24 format.
	CIDR string

	// NetworkName is juju-internal name of the network.
	NetworkName string

	// ProviderId is a provider-specific NIC id.
	ProviderId Id

	// ProviderSubnetId is the provider-specific id for the associated
	// subnet.
	ProviderSubnetId Id

	// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
	// normal networks. It's defined by IEEE 802.1Q standard.
	VLANTag int

	// InterfaceName is the raw OS-specific network device name (e.g.
	// "eth1", even for a VLAN eth1.42 virtual interface).
	InterfaceName string

	// Disabled is true when the interface needs to be disabled on the
	// machine, e.g. not to configure it.
	Disabled bool

	// NoAutoStart is true when the interface should not be configured
	// to start automatically on boot. By default and for
	// backwards-compatibility, interfaces are configured to
	// auto-start.
	NoAutoStart bool

	// ConfigType determines whether the interface should be
	// configured via DHCP, statically, manually, etc. See
	// interfaces(5) for more information.
	ConfigType InterfaceConfigType

	// Address contains an optional static IP address to configure for
	// this network interface. The subnet mask to set will be inferred
	// from the CIDR value.
	Address Address

	// DNSServers contains an optional list of IP addresses and/or
	// hostnames to configure as DNS servers for this network
	// interface.
	DNSServers []Address

	// Gateway address, if set, defines the default gateway to
	// configure for this network interface. For containers this
	// usually is (one of) the host address(es).
	GatewayAddress Address

	// ExtraConfig can contain any valid setting and its value allowed
	// inside an "iface" section of a interfaces(5) config file, e.g.
	// "up", "down", "mtu", etc.
	ExtraConfig map[string]string
}

type interfaceInfoSlice []InterfaceInfo

func (s interfaceInfoSlice) Len() int      { return len(s) }
func (s interfaceInfoSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s interfaceInfoSlice) Less(i, j int) bool {
	iface1 := s[i]
	iface2 := s[j]
	return iface1.DeviceIndex < iface2.DeviceIndex
}

// SortInterfaceInfo sorts a slice of InterfaceInfo on DeviceIndex in ascending
// order.
func SortInterfaceInfo(interfaces []InterfaceInfo) {
	sort.Sort(interfaceInfoSlice(interfaces))
}

// ActualInterfaceName returns raw interface name for raw interface (e.g. "eth0") and
// virtual interface name for virtual interface (e.g. "eth0.42")
func (i *InterfaceInfo) ActualInterfaceName() string {
	if i.VLANTag > 0 {
		return fmt.Sprintf("%s.%d", i.InterfaceName, i.VLANTag)
	}
	return i.InterfaceName
}

// IsVirtual returns true when the interface is a virtual device, as
// opposed to a physical device (e.g. a VLAN or a network alias)
func (i *InterfaceInfo) IsVirtual() bool {
	return i.VLANTag > 0
}

// IsVLAN returns true when the interface is a VLAN interface.
func (i *InterfaceInfo) IsVLAN() bool {
	return i.VLANTag > 0
}

// PreferIPv6Getter will be implemented by both the environment and agent
// config.
type PreferIPv6Getter interface {
	PreferIPv6() bool
}

// InitializeFromConfig needs to be called once after the environment
// or agent configuration is available to configure networking
// settings.
func InitializeFromConfig(config PreferIPv6Getter) {
	globalPreferIPv6 = config.PreferIPv6()
	logger.Infof("setting prefer-ipv6 to %v", globalPreferIPv6)
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

package browser

import (
	"os/exec"
	"runtime"
)

// Start tries to open the URL in a browser
// and reports whether it succeeds.
// Note: copied startBrowser from
// https://github.com/golang/go/blob/master/src/cmd/cover/html.go
func Start(url string) bool {
	// try to start the browser
	var args []string
	switch runtime.GOOS {
	case "darwin":
		args = []string{"open"}
	case "windows":
		args = []string{"cmd", "/c", "start"}
	default:
		args = []string{"xdg-open"}
	}
	cmd := exec.Command(args[0], append(args[1:], url)...)
	return cmd.Start() == nil
}
<commit_msg>browser: improve documentation<commit_after>// Copyright 2013 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.

// Package browser defines web browser related helper functions.
package browser

import (
	"os/exec"
	"runtime"
)

// Start tries to open the URL in a browser
// and reports whether it succeeds.
//
// Note: startBrowser was copied from
// https://github.com/golang/go/blob/master/src/cmd/cover/html.go
func Start(url string) bool {
	// try to start the browser
	var args []string
	switch runtime.GOOS {
	case "darwin":
		args = []string{"open"}
	case "windows":
		args = []string{"cmd", "/c", "start"}
	default:
		args = []string{"xdg-open"}
	}
	cmd := exec.Command(args[0], append(args[1:], url)...)
	return cmd.Start() == nil
}
<|endoftext|>


<commit_before>package plex

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

	"net/http"

	"encoding/xml"
)

// Credentials stores MyPlex credentials
type Credentials struct {
	Username string
	Password string
}

var (
	tokenFile = path.Join(".cache", "token")
)

// Token requests a MyPlex authentication token from cache or from MyPlex.
func Token() string {
	token, err := ioutil.ReadFile(tokenFile)
	if err != nil {
		// File does not exist. Get credentials and write token to file.
		log.Println("Cached token does not exist, request a new token in the Web Interface (Settings -> Request New Token).")
		return ""
	}
	log.Println("Using cached token.")
	return string(token)
}

// TokenRequest requests a new token from MyPlex
func TokenRequest(cred Credentials) error {
	type xmlUser struct {
		Email               string `xml:"email"`
		Username            string `xml:"username"`
		AuthenticationToken string `xml:"authentication-token"`
	}

	// Create a new reqest object.
	req, err := http.NewRequest("POST", "https://plex.tv/users/sign_in.xml", nil)
	if err != nil {
		return fmt.Errorf("failed to create new request")
	}
	// Configure the authentication and headers of the request.
	req.SetBasicAuth(cred.Username, cred.Password)
	addHeaders(*req, "")

	// Create the HTTP Client
	client := &http.Client{}

	// Get the response from the MyPlex API.
	log.Println("Requesting token from MyPlex servers.")
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("failed request to MyPlex servers")
	}
	defer resp.Body.Close() // nolint: errcheck

	if resp.StatusCode == http.StatusUnauthorized {
		return fmt.Errorf(resp.Status)
	}

	var record xmlUser

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("error reading response: %v", err)
	}
	err = xml.Unmarshal(body, &record)
	if err != nil {
		return fmt.Errorf("error parsing xml response: %v", err)
	}
	log.Println("Token received.")

	err = cacheToken(record.AuthenticationToken)
	return err
}

func cacheToken(token string) error {
	// Write token to file.
	f, err := os.Create(tokenFile)
	if err != nil {
		return fmt.Errorf("unable to create token file")
	}
	_, err = f.WriteString(token)
	if err != nil {
		return fmt.Errorf("unable to write to token file")
	}
	err = f.Close()
	if err != nil {
		return fmt.Errorf("unable to close token file: %v", err)
	}
	return nil
}

// RemoveCachedToken removes the cached tokenfile
func RemoveCachedToken() error {
	return os.Remove(tokenFile)
}

func addHeaders(r http.Request, token string) {
	r.Header.Add("X-Plex-Client-Identifier", "0bc797da-2ddd-4ce5-946e-5b13e48f17bb")
	r.Header.Add("X-Plex-Product", "Plex-Sync")
	r.Header.Add("X-Plex-Device", "Plex-Sync")
	r.Header.Add("X-Plex-Version", ShortVersion)
	r.Header.Add("X-Plex-Provides", "controller")
	if token != "" {
		r.Header.Add("X-Plex-Token", token)
	}
}

// GetToken requests the AccessToken from MyPlex for the named server
func (h *Host) GetToken(t string) error {
	// Create a new reqest object.
	resp, err := apiRequest("GET", "https://plex.tv/pms/servers.xml", t, nil)
	if err != nil {
		return fmt.Errorf("error creating request: %v", err)
	}
	defer resp.Body.Close() // nolint: errcheck

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf(resp.Status)
	}

	var record myPlexServer

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("error reading response: %v", err)
	}
	err = xml.Unmarshal(body, &record)
	if err != nil {
		return fmt.Errorf("error parsing xml response: %v", err)
	}
	for _, x := range record.Server {
		if x.Name == h.Name {
			h.Token = x.AccessToken
			return nil
		}
	}
	return fmt.Errorf("no server found matching name %q", h.Name)
}

// codebeat:disable[TOO_MANY_IVARS]
type myPlexServer struct {
	Server []struct {
		AccessToken    string `xml:"accessToken,attr"`
		Name           string `xml:"name,attr"`
		Address        string `xml:"address,attr"`
		Port           string `xml:"port,attr"`
		Version        string `xml:"version,attr"`
		Scheme         string `xml:"scheme,attr"`
		Host           string `xml:"host,attr"`
		LocalAddresses string `xml:"localAddresses,attr"`
		Owned          string `xml:"owned,attr"`
		Synced         string `xml:"synced,attr"`
	}
}

// codebeat:enable[TOO_MANY_IVARS]

func apiRequest(method, url, token string, body io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, fmt.Errorf("failed to create new request, %v", err)
	}
	addHeaders(*req, token)

	// Create the HTTP Client
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed request to MyPlex servers, %v", err)
	}
	return resp, nil
}
<commit_msg>Hotfix/tokenfile generation (#90)<commit_after>package plex

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

	"net/http"

	"encoding/xml"
)

// Credentials stores MyPlex credentials
type Credentials struct {
	Username string
	Password string
}

var (
	tokenFile = path.Join(".cache", "token")
)

// Token requests a MyPlex authentication token from cache or from MyPlex.
func Token() string {
	token, err := ioutil.ReadFile(tokenFile)
	if err != nil {
		// File does not exist. Get credentials and write token to file.
		log.Println("Cached token does not exist, request a new token in the Web Interface (Settings -> Request New Token).")
		return ""
	}
	log.Println("Using cached token.")
	return string(token)
}

// TokenRequest requests a new token from MyPlex
func TokenRequest(cred Credentials) error {
	type xmlUser struct {
		Email               string `xml:"email"`
		Username            string `xml:"username"`
		AuthenticationToken string `xml:"authentication-token"`
	}

	// Create a new reqest object.
	req, err := http.NewRequest("POST", "https://plex.tv/users/sign_in.xml", nil)
	if err != nil {
		return fmt.Errorf("failed to create new request")
	}
	// Configure the authentication and headers of the request.
	req.SetBasicAuth(cred.Username, cred.Password)
	addHeaders(*req, "")

	// Create the HTTP Client
	client := &http.Client{}

	// Get the response from the MyPlex API.
	log.Println("Requesting token from MyPlex servers.")
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("failed request to MyPlex servers")
	}
	defer resp.Body.Close() // nolint: errcheck

	if resp.StatusCode == http.StatusUnauthorized {
		return fmt.Errorf(resp.Status)
	}

	var record xmlUser

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("error reading response: %v", err)
	}
	err = xml.Unmarshal(body, &record)
	if err != nil {
		return fmt.Errorf("error parsing xml response: %v", err)
	}
	log.Println("Token received.")

	err = cacheToken(record.AuthenticationToken)
	return err
}

func tokenDir(tokenPath string) string {
	dir, _ := path.Split(tokenPath)
	return dir
}

func cacheToken(token string) error {
	if tokenDir(tokenFile) != "" {
		if err := os.MkdirAll(tokenDir(tokenFile), 0755); err != nil {
			return fmt.Errorf("unable to create cache folder: %v", err)
		}
	}
	// Write token to file.
	f, err := os.Create(tokenFile)
	if err != nil {
		return fmt.Errorf("unable to create token file")
	}
	_, err = f.WriteString(token)
	if err != nil {
		return fmt.Errorf("unable to write to token file")
	}
	err = f.Close()
	if err != nil {
		return fmt.Errorf("unable to close token file: %v", err)
	}
	return nil
}

// RemoveCachedToken removes the cached tokenfile
func RemoveCachedToken() error {
	return os.Remove(tokenFile)
}

func addHeaders(r http.Request, token string) {
	r.Header.Add("X-Plex-Client-Identifier", "0bc797da-2ddd-4ce5-946e-5b13e48f17bb")
	r.Header.Add("X-Plex-Product", "Plex-Sync")
	r.Header.Add("X-Plex-Device", "Plex-Sync")
	r.Header.Add("X-Plex-Version", ShortVersion)
	r.Header.Add("X-Plex-Provides", "controller")
	if token != "" {
		r.Header.Add("X-Plex-Token", token)
	}
}

// GetToken requests the AccessToken from MyPlex for the named server
func (h *Host) GetToken(t string) error {
	// Create a new reqest object.
	resp, err := apiRequest("GET", "https://plex.tv/pms/servers.xml", t, nil)
	if err != nil {
		return fmt.Errorf("error creating request: %v", err)
	}
	defer resp.Body.Close() // nolint: errcheck

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf(resp.Status)
	}

	var record myPlexServer

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("error reading response: %v", err)
	}
	err = xml.Unmarshal(body, &record)
	if err != nil {
		return fmt.Errorf("error parsing xml response: %v", err)
	}
	for _, x := range record.Server {
		if x.Name == h.Name {
			h.Token = x.AccessToken
			return nil
		}
	}
	return fmt.Errorf("no server found matching name %q", h.Name)
}

// codebeat:disable[TOO_MANY_IVARS]
type myPlexServer struct {
	Server []struct {
		AccessToken    string `xml:"accessToken,attr"`
		Name           string `xml:"name,attr"`
		Address        string `xml:"address,attr"`
		Port           string `xml:"port,attr"`
		Version        string `xml:"version,attr"`
		Scheme         string `xml:"scheme,attr"`
		Host           string `xml:"host,attr"`
		LocalAddresses string `xml:"localAddresses,attr"`
		Owned          string `xml:"owned,attr"`
		Synced         string `xml:"synced,attr"`
	}
}

// codebeat:enable[TOO_MANY_IVARS]

func apiRequest(method, url, token string, body io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, fmt.Errorf("failed to create new request, %v", err)
	}
	addHeaders(*req, token)

	// Create the HTTP Client
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed request to MyPlex servers, %v", err)
	}
	return resp, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sync"
)

// routineX prints "x" 10 times
func routineX(wgX *sync.WaitGroup) {
	for i := 1; i <= 10; i++ {
		fmt.Printf("x")
	}
	wgX.Done() // flag the routine's end
}

// routineO prints "o" 10 times
func routineO(wgO *sync.WaitGroup) {
	for i := 1; i <= 10; i++ {
		fmt.Print("o")
	}
	wgO.Done() // flag the routine's end
}

func main() {

	// sync.WaitGroup will wait the routine's end to finish the program
	var wgX sync.WaitGroup
	var wgO sync.WaitGroup

	wgX.Add(1) // flag the routineX's start
	go routineX(&wgX)

	wgO.Add(1) // flag the routineO's start
	go routineO(&wgO)

	// you shall not pass until wg.Done
	wgX.Wait()
	wgO.Wait()

	fmt.Println()
}
<commit_msg>change printf<commit_after>package main

import (
	"fmt"
	"sync"
)

// routineX prints "x" 10 times
func routineX(wgX *sync.WaitGroup) {
	for i := 1; i <= 10; i++ {
		fmt.Print("x")
	}
	wgX.Done() // flag the routine's end
}

// routineO prints "o" 10 times
func routineO(wgO *sync.WaitGroup) {
	for i := 1; i <= 10; i++ {
		fmt.Print("o")
	}
	wgO.Done() // flag the routine's end
}

func main() {

	// sync.WaitGroup will wait the routine's end to finish the program
	var wgX sync.WaitGroup
	var wgO sync.WaitGroup

	wgX.Add(1) // flag the routineX's start
	go routineX(&wgX)

	wgO.Add(1) // flag the routineO's start
	go routineO(&wgO)

	// you shall not pass until wg.Done
	wgX.Wait()
	wgO.Wait()

	fmt.Println()
}
<|endoftext|>


<commit_before>package manager

import (
	"github.com/labstack/echo"
	"simpleGatewayExample/common"
	"simpleGatewayExample/sdk"
	"simpleGatewayExample/global/gdata"
	"net/http"
	"strings"
	"fmt"
	"go.uber.org/zap"
	"time"
)

type ExtApiRes struct {
	Suc  bool                   `json:"suc"`
	Data map[string]interface{} `json:"data"`
}

func apiCreate(c echo.Context) error {
	//设置跨域
	c.Response().Header().Set("Access-Control-Allow-Origin","*")
	//链路id号
	rid := common.RequestID()
	//时间 跟设置
	ts,debugOn := sdk.LogExtra(c)
	//拼接参数
	api := &gdata.API{}
	api.FullName = c.FormValue("api_name")
	if api.FullName == "" {
		return c.JSON(http.StatusOK,&ExtApiRes{
			Suc:false,
			Data:map[string]interface{}{
				"msg":"必填项不能为空",
			},
		})
	}

	api.Method = c.FormValue("method")
	api.UpstreamMode = c.FormValue("upstream_mode")
	api.UpstreamValue = c.FormValue("upstream_value")
	api.ProxyMode = "1"

	//检查 FullName 是否符合规范
	names := strings.Split(api.FullName,".")
	if len(names) != 5 {
		return c.JSON(http.StatusOK,&ExtApiRes{
			Suc:false,
			Data:map[string]interface{}{
				"msg":"Api名必须为(公司.产品.系统.接口.版本号)格式,例如cometstone.simpGatewayExample.manager.apiCreate.v1",
			},
		})
	}

	api.Company, api.Product, api.System, api.Interface, api.Version = names[0], names[1], names[2], names[3], names[4]
	query := fmt.Sprintf("INSERT INTO api (`full_name`,`company`,`product`,`system`,`interface`,`version`,`method`,`proxy_mode`,`upstream_mode`,`upstream_value`) VALUES ('%s', '%s','%s','%s','%s','%s','%s','%s','%s','%s')", api.FullName, api.Company, api.Product, api.System, api.Interface, api.Version, api.Method, api.ProxyMode,
		api.UpstreamMode, api.UpstreamValue)

	//用于调试查看sql
	sdk.DebugLog(rid,debugOn,"创建api sql",zap.String("sql",query))

	_, err := db.Exec(query)

	if err != nil {
		Logger.Info("api create, insert error", zap.String("rid", rid), zap.Error(err), zap.String("query", query))
		return c.String(http.StatusOK, "create api error")
	}

	Logger.Info("api创建成功", zap.String("rid", rid), zap.Int64("TimeDifference", time.Now().Sub(ts).Nanoseconds()/1000))

	updateApi(api.FullName, 1)

	return c.JSON(http.StatusOK, &ExtApiRes{
		Suc: true,
		Data: map[string]interface{}{
			"api": api,
		},
	})

}

func apiUpdate(c echo.Context) error {
	c.Response().Header().Set("Access-Control-allow-Origin","*")
	rid := common.RequestID()
	ts,debugOn := sdk.LogExtra(c)

	api := &gdata.API{}
	api.FullName = c.FormValue("api_name")
	api.Method = c.FormValue("method")
	api.ProxyMode = c.FormValue("proxy_mode")
	api.UpstreamMode = c.FormValue("upstream_mode")
	api.UpstreamValue = c.FormValue("upstream_value")

	if api.FullName == "" {
		Logger.Info("api_name不能为空", zap.String("rid", rid))
		return c.JSON(http.StatusOK, &ExtApiRes{
			Suc: false,
			Data: map[string]interface{}{
				"msg": "api名不能为空",
			},
		})
	}

	Logger.Info("api 更新",zap.String("rid",rid),zap.Any("api",*api))

	query := fmt.Sprintf("UPDATE api SET `method`='%s',`proxy_mode`='%s',`upstream_mode`='%s',`upstream_value`='%s' WHERE `full_name`='%s'",
		api.Method, api.ProxyMode, api.UpstreamMode, api.UpstreamValue, api.FullName)
	sdk.DebugLog(rid, debugOn, "更新api sql", zap.String("sql", query))

	_,err := db.Exec(query)
	if err != nil {
		Logger.Info("api update  error", zap.String("rid", rid), zap.Error(err), zap.String("query", query))
		return c.String(http.StatusOK, "update api error")
	}

	Logger.Info("api更新成功", zap.String("rid", rid), zap.Int64("TimeDifference", time.Now().Sub(ts).Nanoseconds()/1000))

	updateApi(api.FullName,2)

	return c.JSON(http.StatusOK,&ExtApiRes{
		Suc:true,
	})

}

func apiQuery(c echo.Context) error {

	return nil
}

func apiDelete(c echo.Context) error {

	return nil
}

func apiList(c echo.Context) error {

	return nil
}
<commit_msg>manager api<commit_after>package manager

import (
	"github.com/labstack/echo"
	"simpleGatewayExample/common"
	"simpleGatewayExample/sdk"
	"simpleGatewayExample/global/gdata"
	"net/http"
	"strings"
	"fmt"
	"go.uber.org/zap"
	"time"
)

type ExtApiRes struct {
	Suc  bool                   `json:"suc"`
	Data map[string]interface{} `json:"data"`
}

func apiCreate(c echo.Context) error {
	//设置跨域
	c.Response().Header().Set("Access-Control-Allow-Origin","*")
	//链路id号
	rid := common.RequestID()
	//时间 跟设置
	ts,debugOn := sdk.LogExtra(c)
	//拼接参数
	api := &gdata.API{}
	api.FullName = c.FormValue("api_name")
	if api.FullName == "" {
		return c.JSON(http.StatusOK,&ExtApiRes{
			Suc:false,
			Data:map[string]interface{}{
				"msg":"必填项不能为空",
			},
		})
	}

	api.Method = c.FormValue("method")
	api.UpstreamMode = c.FormValue("upstream_mode")
	api.UpstreamValue = c.FormValue("upstream_value")
	api.ProxyMode = "1"

	//检查 FullName 是否符合规范
	names := strings.Split(api.FullName,".")
	if len(names) != 5 {
		return c.JSON(http.StatusOK,&ExtApiRes{
			Suc:false,
			Data:map[string]interface{}{
				"msg":"Api名必须为(公司.产品.系统.接口.版本号)格式,例如cometstone.simpGatewayExample.manager.apiCreate.v1",
			},
		})
	}

	api.Company, api.Product, api.System, api.Interface, api.Version = names[0], names[1], names[2], names[3], names[4]
	query := fmt.Sprintf("INSERT INTO api (`full_name`,`company`,`product`,`system`,`interface`,`version`,`method`,`proxy_mode`,`upstream_mode`,`upstream_value`) VALUES ('%s', '%s','%s','%s','%s','%s','%s','%s','%s','%s')", api.FullName, api.Company, api.Product, api.System, api.Interface, api.Version, api.Method, api.ProxyMode,
		api.UpstreamMode, api.UpstreamValue)

	//用于调试查看sql
	sdk.DebugLog(rid,debugOn,"创建api sql",zap.String("sql",query))

	_, err := db.Exec(query)

	if err != nil {
		Logger.Info("api create, insert error", zap.String("rid", rid), zap.Error(err), zap.String("query", query))
		return c.String(http.StatusOK, "create api error")
	}

	Logger.Info("api创建成功", zap.String("rid", rid), zap.Int64("TimeDifference", time.Now().Sub(ts).Nanoseconds()/1000))

	updateApi(api.FullName, 1)

	return c.JSON(http.StatusOK, &ExtApiRes{
		Suc: true,
		Data: map[string]interface{}{
			"api": api,
		},
	})

}

func apiUpdate(c echo.Context) error {
	c.Response().Header().Set("Access-Control-allow-Origin","*")
	rid := common.RequestID()
	ts,debugOn := sdk.LogExtra(c)

	api := &gdata.API{}
	api.FullName = c.FormValue("api_name")
	api.Method = c.FormValue("method")
	api.ProxyMode = c.FormValue("proxy_mode")
	api.UpstreamMode = c.FormValue("upstream_mode")
	api.UpstreamValue = c.FormValue("upstream_value")

	if api.FullName == "" {
		Logger.Info("api_name不能为空", zap.String("rid", rid))
		return c.JSON(http.StatusOK, &ExtApiRes{
			Suc: false,
			Data: map[string]interface{}{
				"msg": "api名不能为空",
			},
		})
	}

	Logger.Info("api 更新",zap.String("rid",rid),zap.Any("api",*api))

	query := fmt.Sprintf("UPDATE api SET `method`='%s',`proxy_mode`='%s',`upstream_mode`='%s',`upstream_value`='%s' WHERE `full_name`='%s'",
		api.Method, api.ProxyMode, api.UpstreamMode, api.UpstreamValue, api.FullName)
	sdk.DebugLog(rid, debugOn, "更新api sql", zap.String("sql", query))

	_,err := db.Exec(query)
	if err != nil {
		Logger.Info("api update  error", zap.String("rid", rid), zap.Error(err), zap.String("query", query))
		return c.String(http.StatusOK, "update api error")
	}

	Logger.Info("api更新成功", zap.String("rid", rid), zap.Int64("TimeDifference", time.Now().Sub(ts).Nanoseconds()/1000))

	updateApi(api.FullName,2)

	return c.JSON(http.StatusOK,&ExtApiRes{
		Suc:true,
	})

}

func apiQuery(c echo.Context) error {
	c.Response().Header().Set("Access-Control-allow-Origin","*")
	rid := common.RequestID()
	ts,debugOn := sdk.LogExtra(c)

	apiName := c.FormValue("api_name")
	query := fmt.Sprintf("SELECT * FROM api WHERE `full_name`='%s'",apiName)

	Logger.Info("api查询",zap.String("rid",rid),zap.String("query",query))

	rows,err := db.Query(query)
	if err != nil {
		Logger.Info("api query error", zap.String("rid", rid), zap.Error(err), zap.String("query", query))
		return c.String(http.StatusOK,"api query error")
	}

	defer rows.Close()

	var api *gdata.API

	for rows.Next() {
		tempApi := &gdata.API{}
		err := rows.Scan(&tempApi.ID, &tempApi.FullName, &tempApi.Company, &tempApi.Product,
			&tempApi.System, &tempApi.Interface, &tempApi.Version, &tempApi.Method, &tempApi.ProxyMode, &tempApi.UpstreamMode, &tempApi.UpstreamValue)
		if err != nil {
			Logger.Info("api scan error", zap.String("rid", rid), zap.Error(err), zap.String("query", query))
			return c.String(http.StatusOK, "api scan error")
		}
		api = tempApi
	}

	if api == nil {
		return c.JSON(http.StatusOK,&ExtApiRes{
			Suc:false,
			Data:map[string]interface{}{
				"msg":"api 不存在",
			},
		})
	}

	Logger.Info("api查询成功", zap.String("rid", rid), zap.Int64("TimeDifference", time.Now().Sub(ts).Nanoseconds()/1000))
	sdk.DebugLog(rid, debugOn, "查询api", zap.Any("api", api))

	return c.JSON(http.StatusOK, &ExtApiRes{
		Suc: true,
		Data: map[string]interface{}{
			"api": api,
		},
	})

}

func apiDelete(c echo.Context) error {
	c.Response().Header().Set("Access-Control-Allow-Origin","*")
	rid := common.RequestID()
	ts,debugOn :=sdk.LogExtra(c)

	apiAll := c.FormValue("apis")

	Logger.Info("api删除",zap.String("rid",rid),zap.String("api_name",apiAll))

	apis := strings.Split(apiAll,",")
	for _, api := range apis {
		query := fmt.Sprintf("DELETE FROM api WHERE `full_name` ='%s' ",api)
		sdk.DebugLog(rid, debugOn, "删除api", zap.String("query", query))
		_,err := db.Exec(query)
		if err != nil {
			Logger.Info("api delete error ",zap.String("rid",rid),zap.Error(err),zap.String("query",query))
		}
	}

	Logger.Info("api删除成功", zap.String("rid", rid), zap.Int64("TimeDifference", time.Now().Sub(ts).Nanoseconds()/1000))
	updateApi(apiAll, 3)

	return c.String(http.StatusOK,"success")
}

func apiList(c echo.Context) error {

	return nil
}
<|endoftext|>


<commit_before>package image

import (
	"encoding/json"
	"fmt"
	img "image"
	"image/draw"
	"log"
	"math"
	"os"
	"os/exec"
	"time"

	"pixur.org/pixur/schema"

	"github.com/nfnt/resize"

	"image/gif"
	"image/jpeg"
	_ "image/png"
)

// TODO: maybe make this into it's own package
const (
	DefaultThumbnailWidth  = 160
	DefaultThumbnailHeight = 160

	maxWebmDuration = 60*2 + 1 // Two minutes, with 1 second of leeway
)

type BadWebmFormatErr struct {
	error
}

func FillImageConfig(f *os.File, p *schema.Pic) (img.Image, error) {
	if _, err := f.Seek(0, os.SEEK_SET); err != nil {
		return nil, err
	}
	defer f.Seek(0, os.SEEK_SET)

	im, imgType, err := img.Decode(f)
	if err == img.ErrFormat {
		// Try Webm
		im, err = fillImageConfigFromWebm(f, p)
		if err != nil {
			return nil, err
		}
	} else if err != nil {
		return nil, err
	} else {
		// TODO: handle this error
		p.Mime, _ = schema.FromImageFormat(imgType)
		p.Width = int64(im.Bounds().Dx())
		p.Height = int64(im.Bounds().Dy())
	}

	if p.Mime == schema.Pic_GIF {
		if _, err := f.Seek(0, os.SEEK_SET); err != nil {
			return nil, err
		}

		GIF, err := gif.DecodeAll(f)
		if err != nil {
			return nil, err
		}
		// Ignore gifs that either have only one frame
		if len(GIF.Delay) > 1 {
			hundredths := 0
			for frameDelay := range GIF.Delay {
				hundredths += frameDelay
			}
			p.AnimationInfo = &schema.AnimationInfo{
				Duration: &schema.Duration{
					Seconds: int64(hundredths / 100),
					Nanos:   int32((hundredths % 100) * 10 * 1000 * 1000),
				},
			}
		}
	}

	return im, nil
}

// TODO: interpret image rotation metadata
func MakeThumbnail(im img.Image) img.Image {
	bounds := findMaxSquare(im.Bounds())
	largeSquareImage := img.NewNRGBA(bounds)
	draw.Draw(largeSquareImage, bounds, im, bounds.Min, draw.Src)
	return resize.Resize(DefaultThumbnailWidth, DefaultThumbnailHeight, largeSquareImage,
		resize.NearestNeighbor)
}

func SaveThumbnail(im img.Image, p *schema.Pic, pixPath string) error {
	f, err := os.Create(p.ThumbnailPath(pixPath))
	if err != nil {
		return err
	}
	defer f.Close()
	return jpeg.Encode(f, im, nil)
}

func findMaxSquare(bounds img.Rectangle) img.Rectangle {
	width := bounds.Dx()
	height := bounds.Dy()
	if height < width {
		missingSpace := width - height
		return img.Rectangle{
			Min: img.Point{
				X: bounds.Min.X + missingSpace/2,
				Y: bounds.Min.Y,
			},
			Max: img.Point{
				X: bounds.Min.X + missingSpace/2 + height,
				Y: bounds.Max.Y,
			},
		}
	} else {
		missingSpace := height - width
		return img.Rectangle{
			Min: img.Point{
				X: bounds.Min.X,
				Y: bounds.Min.Y + missingSpace/2,
			},
			Max: img.Point{
				X: bounds.Max.X,
				Y: bounds.Min.Y + missingSpace/2 + width,
			},
		}
	}
}

type ffprobeConfig struct {
	Streams []ffprobeStream `json:"streams"`
	Format  ffprobeFormat   `json:"format"`
}

type ffprobeFormat struct {
	StreamCount int     `json:"nb_streams"`
	FormatName  string  `json:"format_name"`
	Duration    float64 `json:"duration,string"`
}

type ffprobeStream struct {
	CodecName string `json:"codec_name"`
	CodecType string `json:"codec_type"`
	Width     int64  `json:"width"`
	Height    int64  `json:"height"`
}

func fillImageConfigFromWebm(tempFile *os.File, p *schema.Pic) (img.Image, error) {
	config, err := getWebmConfig(tempFile.Name())
	if err != nil {
		return nil, err
	}
	p.Mime = schema.Pic_WEBM
	// Handle the 0 and 1 case
	for _, stream := range config.Streams {
		p.Width = stream.Width
		p.Height = stream.Height
		break
	}

	if dur, success := convertFloatDuration(config.Format.Duration); success {
		if dur >= time.Nanosecond {
			p.AnimationInfo = &schema.AnimationInfo{
				Duration: schema.FromDuration(dur),
			}
		}
	} else {
		log.Println("Invalid duration from ffmpeg: ", config.Format.Duration)
	}

	return getFirstWebmFrame(tempFile.Name())
}

func convertFloatDuration(seconds float64) (time.Duration, bool) {
	if math.IsNaN(seconds) || math.IsInf(seconds, 0) || seconds > math.MaxInt64 {
		return time.Duration(0), false
	}
	if seconds < 0 {
		return time.Duration(0), false
	}
	return time.Duration(seconds * 1e9), true
}

func getWebmConfig(filepath string) (*ffprobeConfig, error) {
	cmd := exec.Command("ffprobe",
		"-print_format", "json",
		"-v", "quiet", // disable version info
		"-show_format",
		"-show_streams",
		filepath)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	config := new(ffprobeConfig)
	if err := json.NewDecoder(stdout).Decode(config); err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	if config.Format.FormatName != "matroska,webm" {
		return nil, fmt.Errorf("Only webm supported: %v", config)
	}
	if config.Format.StreamCount == 0 {
		return nil, fmt.Errorf("No Streams found: %v", config)
	}
	if config.Format.Duration < 0 || config.Format.Duration > maxWebmDuration {
		return nil, fmt.Errorf("Invalid Duration %v", config)
	}

	var videoFound bool
	// Only check for a video stream, since we will just mute it on output.
	for _, stream := range config.Streams {
		if stream.CodecType == "video" {
			if stream.CodecName == "vp8" || stream.CodecName == "vp9" {
				videoFound = true
				break
			}
		}
	}

	if !videoFound {
		return nil, &BadWebmFormatErr{fmt.Errorf("Bad Video %v", config)}
	}

	return config, nil
}

func getFirstWebmFrame(filepath string) (img.Image, error) {
	cmd := exec.Command("ffmpeg",
		"-i", filepath,
		"-v", "quiet", // disable version info
		"-frames:v", "1",
		"-codec:v", "png",
		"-f", "image2pipe",
		"-")
	// PNG data comes across stdout
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}

	im, _, err := img.Decode(stdout)
	if err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	return im, nil
}
<commit_msg>Seek to the first second frame in WebM thumbnails<commit_after>package image

import (
	"encoding/json"
	"fmt"
	img "image"
	"image/draw"
	"log"
	"math"
	"os"
	"os/exec"
	"time"

	"pixur.org/pixur/schema"

	"github.com/nfnt/resize"

	"image/gif"
	"image/jpeg"
	_ "image/png"
)

// TODO: maybe make this into it's own package
const (
	DefaultThumbnailWidth  = 160
	DefaultThumbnailHeight = 160

	maxWebmDuration = 60*2 + 1 // Two minutes, with 1 second of leeway
)

type BadWebmFormatErr struct {
	error
}

func FillImageConfig(f *os.File, p *schema.Pic) (img.Image, error) {
	if _, err := f.Seek(0, os.SEEK_SET); err != nil {
		return nil, err
	}
	defer f.Seek(0, os.SEEK_SET)

	im, imgType, err := img.Decode(f)
	if err == img.ErrFormat {
		// Try Webm
		im, err = fillImageConfigFromWebm(f, p)
		if err != nil {
			return nil, err
		}
	} else if err != nil {
		return nil, err
	} else {
		// TODO: handle this error
		p.Mime, _ = schema.FromImageFormat(imgType)
		p.Width = int64(im.Bounds().Dx())
		p.Height = int64(im.Bounds().Dy())
	}

	if p.Mime == schema.Pic_GIF {
		if _, err := f.Seek(0, os.SEEK_SET); err != nil {
			return nil, err
		}

		GIF, err := gif.DecodeAll(f)
		if err != nil {
			return nil, err
		}
		// Ignore gifs that either have only one frame
		if len(GIF.Delay) > 1 {
			hundredths := 0
			for frameDelay := range GIF.Delay {
				hundredths += frameDelay
			}
			p.AnimationInfo = &schema.AnimationInfo{
				Duration: &schema.Duration{
					Seconds: int64(hundredths / 100),
					Nanos:   int32((hundredths % 100) * 10 * 1000 * 1000),
				},
			}
		}
	}

	return im, nil
}

// TODO: interpret image rotation metadata
func MakeThumbnail(im img.Image) img.Image {
	bounds := findMaxSquare(im.Bounds())
	largeSquareImage := img.NewNRGBA(bounds)
	draw.Draw(largeSquareImage, bounds, im, bounds.Min, draw.Src)
	return resize.Resize(DefaultThumbnailWidth, DefaultThumbnailHeight, largeSquareImage,
		resize.NearestNeighbor)
}

func SaveThumbnail(im img.Image, p *schema.Pic, pixPath string) error {
	f, err := os.Create(p.ThumbnailPath(pixPath))
	if err != nil {
		return err
	}
	defer f.Close()
	return jpeg.Encode(f, im, nil)
}

func findMaxSquare(bounds img.Rectangle) img.Rectangle {
	width := bounds.Dx()
	height := bounds.Dy()
	if height < width {
		missingSpace := width - height
		return img.Rectangle{
			Min: img.Point{
				X: bounds.Min.X + missingSpace/2,
				Y: bounds.Min.Y,
			},
			Max: img.Point{
				X: bounds.Min.X + missingSpace/2 + height,
				Y: bounds.Max.Y,
			},
		}
	} else {
		missingSpace := height - width
		return img.Rectangle{
			Min: img.Point{
				X: bounds.Min.X,
				Y: bounds.Min.Y + missingSpace/2,
			},
			Max: img.Point{
				X: bounds.Max.X,
				Y: bounds.Min.Y + missingSpace/2 + width,
			},
		}
	}
}

type ffprobeConfig struct {
	Streams []ffprobeStream `json:"streams"`
	Format  ffprobeFormat   `json:"format"`
}

type ffprobeFormat struct {
	StreamCount int     `json:"nb_streams"`
	FormatName  string  `json:"format_name"`
	Duration    float64 `json:"duration,string"`
}

type ffprobeStream struct {
	CodecName string `json:"codec_name"`
	CodecType string `json:"codec_type"`
	Width     int64  `json:"width"`
	Height    int64  `json:"height"`
}

func fillImageConfigFromWebm(tempFile *os.File, p *schema.Pic) (img.Image, error) {
	config, err := getWebmConfig(tempFile.Name())
	if err != nil {
		return nil, err
	}
	p.Mime = schema.Pic_WEBM
	// Handle the 0 and 1 case
	for _, stream := range config.Streams {
		p.Width = stream.Width
		p.Height = stream.Height
		break
	}

	if dur, success := convertFloatDuration(config.Format.Duration); success {
		if dur >= time.Nanosecond {
			p.AnimationInfo = &schema.AnimationInfo{
				Duration: schema.FromDuration(dur),
			}
		}
	} else {
		log.Println("Invalid duration from ffmpeg: ", config.Format.Duration)
	}

	return getFirstWebmFrame(tempFile.Name())
}

func convertFloatDuration(seconds float64) (time.Duration, bool) {
	if math.IsNaN(seconds) || math.IsInf(seconds, 0) || seconds > math.MaxInt64 {
		return time.Duration(0), false
	}
	if seconds < 0 {
		return time.Duration(0), false
	}
	return time.Duration(seconds * 1e9), true
}

func getWebmConfig(filepath string) (*ffprobeConfig, error) {
	cmd := exec.Command("ffprobe",
		"-print_format", "json",
		"-v", "quiet", // disable version info
		"-show_format",
		"-show_streams",
		filepath)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	config := new(ffprobeConfig)
	if err := json.NewDecoder(stdout).Decode(config); err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	if config.Format.FormatName != "matroska,webm" {
		return nil, fmt.Errorf("Only webm supported: %v", config)
	}
	if config.Format.StreamCount == 0 {
		return nil, fmt.Errorf("No Streams found: %v", config)
	}
	if config.Format.Duration < 0 || config.Format.Duration > maxWebmDuration {
		return nil, fmt.Errorf("Invalid Duration %v", config)
	}

	var videoFound bool
	// Only check for a video stream, since we will just mute it on output.
	for _, stream := range config.Streams {
		if stream.CodecType == "video" {
			if stream.CodecName == "vp8" || stream.CodecName == "vp9" {
				videoFound = true
				break
			}
		}
	}

	if !videoFound {
		return nil, &BadWebmFormatErr{fmt.Errorf("Bad Video %v", config)}
	}

	return config, nil
}

func getFirstWebmFrame(filepath string) (img.Image, error) {
	cmd := exec.Command("ffmpeg",
		"-i", filepath,
		"-v", "quiet", // disable version info
		"-frames:v", "1",
		"-ss", "1", // Grab the last frame before the first second
		"-codec:v", "png",
		"-f", "image2pipe",
		"-")
	// PNG data comes across stdout
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}

	im, _, err := img.Decode(stdout)
	if err != nil {
		return nil, err
	}
	if err := cmd.Wait(); err != nil {
		return nil, err
	}

	return im, nil
}
<|endoftext|>


<commit_before>package plugins

import (
	"crypto/tls"
	"io"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/belak/irc"
	"github.com/belak/seabird/bot"
	"github.com/belak/seabird/mux"
	"golang.org/x/net/html"
)

// NOTE: This isn't perfect in any sense of the word, but it's pretty close
// and I don't know if it's worth the time to make it better.
var urlRegex = regexp.MustCompile(`https?://[^ ]+`)
var titleRegex = regexp.MustCompile(`(?:\s*[\r\n]+\s*)+`)

// NOTE: This nasty work is done so we ignore invalid ssl certs
var client = &http.Client{
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	},
	Timeout: 5 * time.Second,
}

type URLPlugin struct {
	providers map[string][]LinkProvider
}

type LinkProvider func(c *irc.Client, e *irc.Event, url *url.URL) bool

func NewURLPlugin() bot.Plugin {
	return &URLPlugin{
		providers: make(map[string][]LinkProvider),
	}
}

func (p *URLPlugin) Register(b *bot.Bot) error {
	b.BasicMux.Event("PRIVMSG", p.URLTitle)

	b.CommandMux.Event("down", IsItDown, &mux.HelpInfo{
		"<website>",
		"Checks if given website is down",
	})

	return nil
}

func (p *URLPlugin) RegisterProvider(domain string, f LinkProvider) error {
	p.providers[domain] = append(p.providers[domain], f)

	return nil
}

func (p *URLPlugin) URLTitle(c *irc.Client, e *irc.Event) {
	for _, rawurl := range urlRegex.FindAllString(e.Trailing(), -1) {
		go func(raw string) {
			u, err := url.ParseRequestURI(rawurl)
			if err != nil {
				return
			}

			for _, provider := range p.providers[u.Host] {
				if provider(c, e, u) {
					return
				}
			}

			// If there was a www, we fall back to no www
			// This is not perfect, but it will fix a number of issues
			// Alternatively, we could require the linkifiers to
			// register multiple times
			if strings.HasPrefix(u.Host, "www.") {
				host := u.Host[4:]
				for _, provider := range p.providers[host] {
					if provider(c, e, u) {
						return
					}
				}
			}

			defaultLinkProvider(rawurl, c, e)
		}(rawurl)
	}
}

func defaultLinkProvider(url string, c *irc.Client, e *irc.Event) bool {
	var client = &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
		Timeout: 5 * time.Second,
	}

	r, err := client.Get(url)
	if err != nil {
		return false
	}
	defer r.Body.Close()

	if r.StatusCode != 200 {
		return false
	}

	// We search the first 1K and if a title isn't in there, we deal with it
	z, err := html.Parse(io.LimitReader(r.Body, 1024*1024))
	if err != nil {
		return false
	}

	var titleRegex = regexp.MustCompile(`(?:\s*[\r\n]+\s*)+`)

	// DFS that searches the tree for any node named title then
	// returns the data of that node's first child
	var f func(*html.Node) (string, bool)
	f = func(n *html.Node) (string, bool) {
		// If it's an element and it's a title node, look for a child
		if n.Type == html.ElementNode && n.Data == "title" {
			if n.FirstChild != nil {
				t := n.FirstChild.Data
				t = titleRegex.ReplaceAllString(t, " ")
				t = strings.TrimSpace(t)

				if t != "" {
					return t, true
				} else {
					return "", false
				}
			}
		}

		// Loop through all nodes and try recursing
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			if str, ok := f(c); ok {
				return str, true
			}
		}

		return "", false
	}

	if str, ok := f(z); ok {
		// Title: title title
		c.Reply(e, "Title: %s", str)
		return true
	} else {
		return false
	}
}

func IsItDown(c *irc.Client, e *irc.Event) {
	go func() {
		url, err := url.Parse(e.Trailing())
		if err != nil {
			c.Reply(e, "URL doesn't appear to be valid")
			return
		}

		if url.Scheme == "" {
			url.Scheme = "http"
		}

		r, err := client.Head(url.String())
		if err != nil || r.StatusCode != 200 {
			c.Reply(e, "It's not just you! %s looks down from here.", url)
			return
		}

		c.Reply(e, "It's just you! %s looks up from here!", url)
	}()
}
<commit_msg>Hopeful fix for URL issues<commit_after>package plugins

import (
	"crypto/tls"
	"io"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/belak/irc"
	"github.com/belak/seabird/bot"
	"github.com/belak/seabird/mux"
	"golang.org/x/net/html"
)

// NOTE: This isn't perfect in any sense of the word, but it's pretty close
// and I don't know if it's worth the time to make it better.
var urlRegex = regexp.MustCompile(`https?://[^ ]+`)
var titleRegex = regexp.MustCompile(`(?:\s*[\r\n]+\s*)+`)

// NOTE: This nasty work is done so we ignore invalid ssl certs
var client = &http.Client{
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	},
	Timeout: 5 * time.Second,
}

type URLPlugin struct {
	providers map[string][]LinkProvider
}

type LinkProvider func(c *irc.Client, e *irc.Event, url *url.URL) bool

func NewURLPlugin() bot.Plugin {
	return &URLPlugin{
		providers: make(map[string][]LinkProvider),
	}
}

func (p *URLPlugin) Register(b *bot.Bot) error {
	b.BasicMux.Event("PRIVMSG", p.URLTitle)

	b.CommandMux.Event("down", IsItDown, &mux.HelpInfo{
		"<website>",
		"Checks if given website is down",
	})

	return nil
}

func (p *URLPlugin) RegisterProvider(domain string, f LinkProvider) error {
	p.providers[domain] = append(p.providers[domain], f)

	return nil
}

func (p *URLPlugin) URLTitle(c *irc.Client, e *irc.Event) {
	for _, rawurl := range urlRegex.FindAllString(e.Trailing(), -1) {
		go func(raw string) {
			u, err := url.ParseRequestURI(raw)
			if err != nil {
				return
			}

			for _, provider := range p.providers[u.Host] {
				if provider(c, e, u) {
					return
				}
			}

			// If there was a www, we fall back to no www
			// This is not perfect, but it will fix a number of issues
			// Alternatively, we could require the linkifiers to
			// register multiple times
			if strings.HasPrefix(u.Host, "www.") {
				host := u.Host[4:]
				for _, provider := range p.providers[host] {
					if provider(c, e, u) {
						return
					}
				}
			}

			defaultLinkProvider(raw, c, e)
		}(rawurl)
	}
}

func defaultLinkProvider(url string, c *irc.Client, e *irc.Event) bool {
	var client = &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
		Timeout: 5 * time.Second,
	}

	r, err := client.Get(url)
	if err != nil {
		return false
	}
	defer r.Body.Close()

	if r.StatusCode != 200 {
		return false
	}

	// We search the first 1K and if a title isn't in there, we deal with it
	z, err := html.Parse(io.LimitReader(r.Body, 1024*1024))
	if err != nil {
		return false
	}

	var titleRegex = regexp.MustCompile(`(?:\s*[\r\n]+\s*)+`)

	// DFS that searches the tree for any node named title then
	// returns the data of that node's first child
	var f func(*html.Node) (string, bool)
	f = func(n *html.Node) (string, bool) {
		// If it's an element and it's a title node, look for a child
		if n.Type == html.ElementNode && n.Data == "title" {
			if n.FirstChild != nil {
				t := n.FirstChild.Data
				t = titleRegex.ReplaceAllString(t, " ")
				t = strings.TrimSpace(t)

				if t != "" {
					return t, true
				} else {
					return "", false
				}
			}
		}

		// Loop through all nodes and try recursing
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			if str, ok := f(c); ok {
				return str, true
			}
		}

		return "", false
	}

	if str, ok := f(z); ok {
		// Title: title title
		c.Reply(e, "Title: %s", str)
		return true
	} else {
		return false
	}
}

func IsItDown(c *irc.Client, e *irc.Event) {
	go func() {
		url, err := url.Parse(e.Trailing())
		if err != nil {
			c.Reply(e, "URL doesn't appear to be valid")
			return
		}

		if url.Scheme == "" {
			url.Scheme = "http"
		}

		r, err := client.Head(url.String())
		if err != nil || r.StatusCode != 200 {
			c.Reply(e, "It's not just you! %s looks down from here.", url)
			return
		}

		c.Reply(e, "It's just you! %s looks up from here!", url)
	}()
}
<|endoftext|>


<commit_before>package plural

import (
	"fmt"
	"testing"
)

func TestPluralFormatIntEnglish(t *testing.T) {
	p012 := Plurals{Case{0, "nothing"}, Case{1, "%v thing"}, Case{2, "%v things"}}

	cases := []struct {
		n      interface{}
		expect string
	}{
		{0, "nothing"},
		{1, "1 thing"},
		{2, "2 things"},
		{3, "3 things"},
		{400, "400 things"},
		{int8(0), "nothing"},
		{int16(1), "1 thing"},
		{int32(2), "2 things"},
		{int64(3), "3 things"},
		{uint8(0), "nothing"},
		{uint16(1), "1 thing"},
		{uint32(2), "2 things"},
		{uint64(3), "3 things"},
		{float32(2), "2 things"},
		{float32(0.1), "0.1 thing"},
		{float32(2.1), "2.1 things"},
		{float64(3), "3 things"},
		{float64(3.00001), "3.00001 things"},
		{fp32(3), "3 things"},
		{fp64(3), "3 things"},
		{ip(0), "nothing"},
		{ip(1), "1 thing"},
		{ip(2), "2 things"},
		{ip(3), "3 things"},
		{ip8(3), "3 things"},
		{ip16(3), "3 things"},
		{ip32(3), "3 things"},
		{ip64(3), "3 things"},
		{uip(3), "3 things"},
		{uip8(3), "3 things"},
		{uip16(3), "3 things"},
		{uip32(3), "3 things"},
		{uip64(3), "3 things"},
	}
	for _, c := range cases {
		s, err := p012.Format(c.n)
		if err != nil {
			t.Errorf("Format(%d) => %v, want %s", c.n, err, c.expect)
		} else if s != c.expect {
			t.Errorf("Format(%d) == %s, want %s", c.n, s, c.expect)
		}
	}
}

func TestSimplePlurals(t *testing.T) {
	p012 := Simple("nothing", "%v thing", "%v things")

	cases := []struct {
		n      interface{}
		expect string
	}{
		{0, "nothing"},
		{1, "1 thing"},
		{2, "2 things"},
		{3, "3 things"},
		{400, "400 things"},
	}
	for _, c := range cases {
		s, err := p012.Format(c.n)
		if err != nil {
			t.Errorf("Format(%d) => %v, want %s", c.n, err, c.expect)
		} else if s != c.expect {
			t.Errorf("Format(%d) == %s, want %s", c.n, s, c.expect)
		}
	}
}

func TestErrorCase(t *testing.T) {
	plurals := Plurals{Case{0, "nothing"}, Case{1, "%v thing"}, Case{2, "%v things"}}

	cases := []struct {
		n      interface{}
		expect string
	}{
		{"foo", "Unexpected type string for foo"},
	}
	for _, c := range cases {
		_, err := plurals.Format(c.n)
		if err == nil {
			t.Errorf("Format(%#v) no error, want %s", c.n, c.expect)
		} else if err.Error() != c.expect {
			t.Errorf("Format(%v) == %s, want %s", c.n, err.Error(), c.expect)
		}
	}
}

func ip(v int) *int {
	return &v
}

func ip8(v int8) *int8 {
	return &v
}

func ip16(v int16) *int16 {
	return &v
}

func ip32(v int32) *int32 {
	return &v
}

func ip64(v int64) *int64 {
	return &v
}

func uip(v uint) *uint {
	return &v
}

func uip8(v uint8) *uint8 {
	return &v
}

func uip16(v uint16) *uint16 {
	return &v
}

func uip32(v uint32) *uint32 {
	return &v
}

func uip64(v uint64) *uint64 {
	return &v
}

func fp32(v float32) *float32 {
	return &v
}

func fp64(v float64) *float64 {
	return &v
}

func ExamplePlurals() {
	// Plurals{} holds a sequence of cardinal cases where the first match is used, otherwise the last one is used.
	// The last case will typically include a "%v" placeholder for the number.
	// carPlurals and weightPlurals provide English formatted cases for some number of cars and their weight.
	var carPlurals = Plurals{
		Case{0, "no cars"},
		Case{1, "%v car"},
		Case{2, "%v cars"},
	}
	var weightPlurals = Plurals{
		Case{0, "weigh nothing"},
		Case{1, "weighs %g tonne"},
		Case{2, "weigh %1.1f tonnes"},
	}

	for d := 0; d < 4; d++ {
		s, _ := carPlurals.Format(d)
		w, _ := weightPlurals.Format(float32(d) * 0.6)
		fmt.Printf("%s %s\n", s, w)
	}

	// See also the 'Simple' example.
	// Output: no cars weigh nothing
	// 1 car weighs 0.6 tonne
	// 2 cars weigh 1.2 tonnes
	// 3 cars weigh 1.8 tonnes
}

func ExampleSimple() {
	// Simple(...) builds simple common kinds of plurals using small ordinals (0, 1, 2, 3 etc).
	// Notice that the counting starts from zero.
	var carPlurals = Simple("no cars", "%v car", "%v cars")
	var weightPlurals = Simple("weigh nothing", "weighs %g tonne", "weigh %1.1f tonnes")

	for d := 0; d < 5; d++ {
		s, _ := carPlurals.Format(d)
		w, _ := weightPlurals.Format(float32(d) * 0.6)
		fmt.Printf("%s %s\n", s, w)
	}

	// See also the other, more general, example.
	// Output: no cars weigh nothing
	// 1 car weighs 0.6 tonne
	// 2 cars weigh 1.2 tonnes
	// 3 cars weigh 1.8 tonnes
	// 4 cars weigh 2.4 tonnes
}
<commit_msg>Extra test case<commit_after>package plural

import (
	"fmt"
	"testing"
)

func TestPluralFormatIntEnglish(t *testing.T) {
	p012 := Plurals{Case{0, "nothing"}, Case{1, "%v thing"}, Case{2, "%v things"}}

	cases := []struct {
		n      interface{}
		expect string
	}{
		{0, "nothing"},
		{1, "1 thing"},
		{2, "2 things"},
		{3, "3 things"},
		{400, "400 things"},
		{int8(0), "nothing"},
		{int16(1), "1 thing"},
		{int32(2), "2 things"},
		{int64(3), "3 things"},
		{uint8(0), "nothing"},
		{uint16(1), "1 thing"},
		{uint32(2), "2 things"},
		{uint64(3), "3 things"},
		{float32(2), "2 things"},
		{float32(0), "nothing"},
		{float32(0.1), "0.1 thing"},
		{float32(2.1), "2.1 things"},
		{float64(3), "3 things"},
		{float64(3.00001), "3.00001 things"},
		{fp32(3), "3 things"},
		{fp64(3), "3 things"},
		{ip(0), "nothing"},
		{ip(1), "1 thing"},
		{ip(2), "2 things"},
		{ip(3), "3 things"},
		{ip8(3), "3 things"},
		{ip16(3), "3 things"},
		{ip32(3), "3 things"},
		{ip64(3), "3 things"},
		{uip(3), "3 things"},
		{uip8(3), "3 things"},
		{uip16(3), "3 things"},
		{uip32(3), "3 things"},
		{uip64(3), "3 things"},
	}
	for _, c := range cases {
		s, err := p012.Format(c.n)
		if err != nil {
			t.Errorf("Format(%d) => %v, want %s", c.n, err, c.expect)
		} else if s != c.expect {
			t.Errorf("Format(%d) == %s, want %s", c.n, s, c.expect)
		}
	}
}

func TestSimplePlurals(t *testing.T) {
	p012 := Simple("nothing", "%v thing", "%v things")

	cases := []struct {
		n      interface{}
		expect string
	}{
		{0, "nothing"},
		{1, "1 thing"},
		{2, "2 things"},
		{3, "3 things"},
		{400, "400 things"},
	}
	for _, c := range cases {
		s, err := p012.Format(c.n)
		if err != nil {
			t.Errorf("Format(%d) => %v, want %s", c.n, err, c.expect)
		} else if s != c.expect {
			t.Errorf("Format(%d) == %s, want %s", c.n, s, c.expect)
		}
	}
}

func TestErrorCase(t *testing.T) {
	plurals := Plurals{Case{0, "nothing"}, Case{1, "%v thing"}, Case{2, "%v things"}}

	cases := []struct {
		n      interface{}
		expect string
	}{
		{"foo", "Unexpected type string for foo"},
	}
	for _, c := range cases {
		_, err := plurals.Format(c.n)
		if err == nil {
			t.Errorf("Format(%#v) no error, want %s", c.n, c.expect)
		} else if err.Error() != c.expect {
			t.Errorf("Format(%v) == %s, want %s", c.n, err.Error(), c.expect)
		}
	}
}

func ip(v int) *int {
	return &v
}

func ip8(v int8) *int8 {
	return &v
}

func ip16(v int16) *int16 {
	return &v
}

func ip32(v int32) *int32 {
	return &v
}

func ip64(v int64) *int64 {
	return &v
}

func uip(v uint) *uint {
	return &v
}

func uip8(v uint8) *uint8 {
	return &v
}

func uip16(v uint16) *uint16 {
	return &v
}

func uip32(v uint32) *uint32 {
	return &v
}

func uip64(v uint64) *uint64 {
	return &v
}

func fp32(v float32) *float32 {
	return &v
}

func fp64(v float64) *float64 {
	return &v
}

func ExamplePlurals() {
	// Plurals{} holds a sequence of cardinal cases where the first match is used, otherwise the last one is used.
	// The last case will typically include a "%v" placeholder for the number.
	// carPlurals and weightPlurals provide English formatted cases for some number of cars and their weight.
	var carPlurals = Plurals{
		Case{0, "no cars"},
		Case{1, "%v car"},
		Case{2, "%v cars"},
	}
	var weightPlurals = Plurals{
		Case{0, "weigh nothing"},
		Case{1, "weighs %g tonne"},
		Case{2, "weigh %1.1f tonnes"},
	}

	for d := 0; d < 4; d++ {
		s, _ := carPlurals.Format(d)
		w, _ := weightPlurals.Format(float32(d) * 0.6)
		fmt.Printf("%s %s\n", s, w)
	}

	// See also the 'Simple' example.
	// Output: no cars weigh nothing
	// 1 car weighs 0.6 tonne
	// 2 cars weigh 1.2 tonnes
	// 3 cars weigh 1.8 tonnes
}

func ExampleSimple() {
	// Simple(...) builds simple common kinds of plurals using small ordinals (0, 1, 2, 3 etc).
	// Notice that the counting starts from zero.
	var carPlurals = Simple("no cars", "%v car", "%v cars")
	var weightPlurals = Simple("weigh nothing", "weighs %g tonne", "weigh %1.1f tonnes")

	for d := 0; d < 5; d++ {
		s, _ := carPlurals.Format(d)
		w, _ := weightPlurals.Format(float32(d) * 0.6)
		fmt.Printf("%s %s\n", s, w)
	}

	// See also the other, more general, example.
	// Output: no cars weigh nothing
	// 1 car weighs 0.6 tonne
	// 2 cars weigh 1.2 tonnes
	// 3 cars weigh 1.8 tonnes
	// 4 cars weigh 2.4 tonnes
}
<|endoftext|>


<commit_before>package controllers

import (
    "github.com/revel/revel"
    "encoding/json"
    "io/ioutil"
    "github.com/ip4368/go-password"
    "github.com/ip4368/go-userprofile"
)

type Users struct {
    *revel.Controller
}

type RegisterProfile struct {
    Username string `json:"username"`
    Password string `json:"password"`
    Email string `json:"email"`
}

func (c *Users) Register() revel.Result {
	// read request body to byte
	var r RegisterProfile
	var bodyBytes []byte
	if c.Request.Body != nil {
		bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
	}
	json.Unmarshal([]byte(bodyBytes), &r)

	password.ValidatePassword(r.Password)
	userprofile.ValidateEmail(r.Email)
	userprofile.ValidateUsername(r.Username)
	return c.Render()
}

func (c *Users) Login() revel.Result {
	// read request body to byte
	var r RegisterProfile
	var bodyBytes []byte
	if c.Request.Body != nil {
		bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
	}
	json.Unmarshal([]byte(bodyBytes), &r)

	userprofile.ValidateEmail(r.Email)
	return c.Render()
}
<commit_msg>start serving JSON as users api endpoint<commit_after>package controllers

import (
    "github.com/revel/revel"
    "encoding/json"
    "io/ioutil"
    "github.com/ip4368/go-password"
    "github.com/ip4368/go-userprofile"
)

type Users struct {
    *revel.Controller
}

type RegisterProfile struct {
    Username string `json:"username"`
    Password string `json:"password"`
    Email string `json:"email"`
}

func (c *Users) Register() revel.Result {
	// start with initialise response interface
	data := make(map[string]interface{})
	data["error"] = nil
	data["message"] = "Successfully Registered"

	// read request body to byte
	var r RegisterProfile
	var bodyBytes []byte
	if c.Request.Body != nil {
		bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
	}
	json.Unmarshal([]byte(bodyBytes), &r)

	if !userprofile.ValidateEmail(r.Email) {
		data["error"] = 1
		data["message"] = "Invalid Email"
		return c.RenderJson(data)
	}
	if !userprofile.ValidateUsername(r.Username) {
		data["error"] = 2
		data["message"] = "Invalid Username"
		return c.RenderJson(data)
	}
	if !password.ValidatePassword(r.Password) {
		data["error"] = 3
		data["message"] = "Invalid Password"
		return c.RenderJson(data)
	}

	//hashed, salt, _ := password.HashAutoSalt(r.Password)
	return c.RenderJson(data)
}

func (c *Users) Login() revel.Result {
	// start with initialise response interface
	data := make(map[string]interface{})
	data["error"] = nil
	data["message"] = "Successfully Logged In"

	// read request body to byte
	var r RegisterProfile
	var bodyBytes []byte
	if c.Request.Body != nil {
		bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
	}
	json.Unmarshal([]byte(bodyBytes), &r)

	userprofile.ValidateEmail(r.Email)
	return c.RenderJson(data)
}
<|endoftext|>