<commit_before>package api

import (
	"encoding/json"
	"errors"
	"github.com/gorilla/websocket"
	"github.com/vansante/go-event-emitter"
	"gitlab.transip.us/swiltink/go-MusicBot/player"
	"gitlab.transip.us/swiltink/go-MusicBot/songplayer"
	"io/ioutil"
	"sync"
	"time"
)

const (
	writeWait      = 10 * time.Second
	pongWait       = 60 * time.Second
	pingPeriod     = (pongWait * 9) / 10
	maxMessageSize = 128
)

type ControlWebsocket struct {
	ws        *websocket.Conn
	readOnly  bool
	hasClosed bool
	player    player.MusicPlayer
	capturer  *eventemitter.Capturer
	writeLock sync.Mutex
}

func NewControlWebsocket(ws *websocket.Conn, readOnly bool, player player.MusicPlayer) (cws *ControlWebsocket) {
	cws = &ControlWebsocket{
		ws:       ws,
		readOnly: readOnly,
		player:   player,
	}
	return
}

func (cws *ControlWebsocket) Start() {
	cws.player.AddCapturer(cws.onEvent)

	go cws.socketWriter()
	cws.socketReader()
}

func (cws *ControlWebsocket) onEvent(event string, args ...interface{}) {
	for i := range args {
		songs, ok := args[i].([]songplayer.Playable)
		if ok {
			args[i] = getAPISongs(songs)
			continue
		}
		song, ok := args[i].(songplayer.Playable)
		if ok {
			args[i] = getAPISong(song, song.GetDuration())
			continue
		}
		dur, ok := args[i].(time.Duration)
		if ok {
			args[i] = int(dur.Seconds())
			continue
		}
	}
	evt := Event{
		Event:     event,
		Arguments: args,
	}
	cws.write(evt)
}

func (cws *ControlWebsocket) socketWriter() {
	ticker := time.NewTicker(pingPeriod)

	// Make sure we stop the ticker when done
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			cws.writeLock.Lock()
			cws.ws.SetWriteDeadline(time.Now().Add(writeWait))
			err := cws.ws.WriteMessage(websocket.PingMessage, []byte{})
			cws.writeLock.Unlock()
			if err != nil {
				break
			}
		}
	}
	cws.closeConnection()
}

func (cws *ControlWebsocket) socketReader() {
	cws.ws.SetReadLimit(maxMessageSize)
	cws.ws.SetReadDeadline(time.Now().Add(pongWait))
	cws.ws.SetPongHandler(func(string) error {
		cws.ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// If the application is not otherwise interested in messages from the peer,
	// then the application should start a goroutine to read and discard messages from the peer
	// (From the documentation)
	for {
		if tp, reader, err := cws.ws.NextReader(); err != nil {
			if cws.readOnly {
				// Ignore everything :)
				break
			}
			if tp != websocket.TextMessage {
				break
			}

			buf, err := ioutil.ReadAll(reader)
			if err != nil {
				cws.write("Error: " + err.Error())
				return
			}
			cmd := &Command{}
			err = json.Unmarshal(buf, cmd)
			if err != nil {
				cws.write("Error: " + err.Error())
				return
			}
			cws.executeCommand(cmd)
			break
		}
	}
	cws.closeConnection()
}

func (cws *ControlWebsocket) executeCommand(cmd *Command) {
	switch cmd.Command {
	case "add":
		if len(cmd.Arguments) < 1 {
			err := errors.New("Missing URL argument")
			cws.write(getCommandResponse(cmd, err))
			return
		}
		_, err := cws.player.AddSongs(cmd.Arguments[0])
		cws.write(getCommandResponse(cmd, err))
	case "open":
		if len(cmd.Arguments) < 1 {
			err := errors.New("Missing URL argument")
			cws.write(getCommandResponse(cmd, err))
			return
		}
		_, err := cws.player.InsertSongs(cmd.Arguments[0], 0)
		cws.write(getCommandResponse(cmd, err))
	case "play":
		_, err := cws.player.Play()
		cws.write(getCommandResponse(cmd, err))
	case "pause":
		err := cws.player.Pause()
		cws.write(getCommandResponse(cmd, err))
	case "status":
		song, remaining := cws.player.GetCurrentSong()
		resp := getCommandResponse(cmd, nil)
		resp.Status = &Status{
			Status:  cws.player.GetStatus(),
			Current: getAPISong(song, remaining),
			List:    getAPISongs(cws.player.GetQueuedSongs()),
		}
		cws.write(resp)
	case "next":
		_, err := cws.player.Next()
		cws.write(getCommandResponse(cmd, err))
	case "stop":
		err := cws.player.Stop()
		cws.write(getCommandResponse(cmd, err))
	case "empty-list":
		cws.player.EmptyQueue()
		cws.write(getCommandResponse(cmd, nil))
	case "shuffle-list":
		cws.player.ShuffleQueue()
		cws.write(getCommandResponse(cmd, nil))
	}
}

func (cws *ControlWebsocket) closeConnection() {
	// Use the write lock to ensure this function is _really_ called once.
	cws.writeLock.Lock()
	defer cws.writeLock.Unlock()

	if cws.hasClosed {
		return
	}
	cws.hasClosed = true

	// Close websocket
	cws.ws.Close()

	// Remove our event capturer
	cws.player.RemoveCapturer(cws.capturer)
}

func (cws *ControlWebsocket) write(data interface{}) {
	// Applications are responsible for ensuring that no more than one goroutine calls the write methods
	// (From the docs)
	cws.writeLock.Lock()
	cws.ws.SetWriteDeadline(time.Now().Add(writeWait))
	cws.ws.WriteJSON(data)
	cws.writeLock.Unlock()
}
<commit_msg>Actually set the capturer so we can actually close the event listener<commit_after>package api

import (
	"encoding/json"
	"errors"
	"github.com/gorilla/websocket"
	"github.com/vansante/go-event-emitter"
	"gitlab.transip.us/swiltink/go-MusicBot/player"
	"gitlab.transip.us/swiltink/go-MusicBot/songplayer"
	"io/ioutil"
	"sync"
	"time"
)

const (
	writeWait      = 10 * time.Second
	pongWait       = 60 * time.Second
	pingPeriod     = (pongWait * 9) / 10
	maxMessageSize = 128
)

type ControlWebsocket struct {
	ws        *websocket.Conn
	readOnly  bool
	hasClosed bool
	player    player.MusicPlayer
	capturer  *eventemitter.Capturer
	writeLock sync.Mutex
}

func NewControlWebsocket(ws *websocket.Conn, readOnly bool, player player.MusicPlayer) (cws *ControlWebsocket) {
	cws = &ControlWebsocket{
		ws:       ws,
		readOnly: readOnly,
		player:   player,
	}
	return
}

func (cws *ControlWebsocket) Start() {
	cws.capturer = cws.player.AddCapturer(cws.onEvent)

	go cws.socketWriter()
	cws.socketReader()
}

func (cws *ControlWebsocket) onEvent(event string, args ...interface{}) {
	for i := range args {
		songs, ok := args[i].([]songplayer.Playable)
		if ok {
			args[i] = getAPISongs(songs)
			continue
		}
		song, ok := args[i].(songplayer.Playable)
		if ok {
			args[i] = getAPISong(song, song.GetDuration())
			continue
		}
		dur, ok := args[i].(time.Duration)
		if ok {
			args[i] = int(dur.Seconds())
			continue
		}
	}
	evt := Event{
		Event:     event,
		Arguments: args,
	}
	cws.write(evt)
}

func (cws *ControlWebsocket) socketWriter() {
	ticker := time.NewTicker(pingPeriod)

	// Make sure we stop the ticker when done
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			cws.writeLock.Lock()
			cws.ws.SetWriteDeadline(time.Now().Add(writeWait))
			err := cws.ws.WriteMessage(websocket.PingMessage, []byte{})
			cws.writeLock.Unlock()
			if err != nil {
				break
			}
		}
	}
	cws.closeConnection()
}

func (cws *ControlWebsocket) socketReader() {
	cws.ws.SetReadLimit(maxMessageSize)
	cws.ws.SetReadDeadline(time.Now().Add(pongWait))
	cws.ws.SetPongHandler(func(string) error {
		cws.ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// If the application is not otherwise interested in messages from the peer,
	// then the application should start a goroutine to read and discard messages from the peer
	// (From the documentation)
	for {
		if tp, reader, err := cws.ws.NextReader(); err != nil {
			if cws.readOnly {
				// Ignore everything :)
				break
			}
			if tp != websocket.TextMessage {
				break
			}

			buf, err := ioutil.ReadAll(reader)
			if err != nil {
				cws.write("Error: " + err.Error())
				return
			}
			cmd := &Command{}
			err = json.Unmarshal(buf, cmd)
			if err != nil {
				cws.write("Error: " + err.Error())
				return
			}
			cws.executeCommand(cmd)
			break
		}
	}
	cws.closeConnection()
}

func (cws *ControlWebsocket) executeCommand(cmd *Command) {
	switch cmd.Command {
	case "add":
		if len(cmd.Arguments) < 1 {
			err := errors.New("Missing URL argument")
			cws.write(getCommandResponse(cmd, err))
			return
		}
		_, err := cws.player.AddSongs(cmd.Arguments[0])
		cws.write(getCommandResponse(cmd, err))
	case "open":
		if len(cmd.Arguments) < 1 {
			err := errors.New("Missing URL argument")
			cws.write(getCommandResponse(cmd, err))
			return
		}
		_, err := cws.player.InsertSongs(cmd.Arguments[0], 0)
		cws.write(getCommandResponse(cmd, err))
	case "play":
		_, err := cws.player.Play()
		cws.write(getCommandResponse(cmd, err))
	case "pause":
		err := cws.player.Pause()
		cws.write(getCommandResponse(cmd, err))
	case "status":
		song, remaining := cws.player.GetCurrentSong()
		resp := getCommandResponse(cmd, nil)
		resp.Status = &Status{
			Status:  cws.player.GetStatus(),
			Current: getAPISong(song, remaining),
			List:    getAPISongs(cws.player.GetQueuedSongs()),
		}
		cws.write(resp)
	case "next":
		_, err := cws.player.Next()
		cws.write(getCommandResponse(cmd, err))
	case "stop":
		err := cws.player.Stop()
		cws.write(getCommandResponse(cmd, err))
	case "empty-list":
		cws.player.EmptyQueue()
		cws.write(getCommandResponse(cmd, nil))
	case "shuffle-list":
		cws.player.ShuffleQueue()
		cws.write(getCommandResponse(cmd, nil))
	}
}

func (cws *ControlWebsocket) closeConnection() {
	// Use the write lock to ensure this function is _really_ called once.
	cws.writeLock.Lock()
	defer cws.writeLock.Unlock()

	if cws.hasClosed {
		return
	}
	cws.hasClosed = true

	// Close websocket
	cws.ws.Close()

	// Remove our event capturer
	cws.player.RemoveCapturer(cws.capturer)
}

func (cws *ControlWebsocket) write(data interface{}) {
	// Applications are responsible for ensuring that no more than one goroutine calls the write methods
	// (From the docs)
	cws.writeLock.Lock()
	cws.ws.SetWriteDeadline(time.Now().Add(writeWait))
	cws.ws.WriteJSON(data)
	cws.writeLock.Unlock()
}
<|endoftext|>


<commit_before>package gaurun

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/alexjlockwood/gcm"
	"github.com/cubicdaiya/apns"
	"io/ioutil"
	"net/http"
	"sync/atomic"
	"time"
)

type RequestGaurun struct {
	Notifications []RequestGaurunNotification `json:"notifications"`
}

type RequestGaurunNotification struct {
	// Common
	Tokens   []string `json:"token"`
	Platform int      `json:"platform"`
	Message  string   `json:"message"`
	// Android
	CollapseKey    string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool   `json:"delay_while_idle,omitempty"`
	TimeToLive     int    `json:"time_to_live,omitempty"`
	// iOS
	Badge  int          `json:"badge,omitempty"`
	Sound  string       `json:"sound,omitempty"`
	Expiry int          `json:"expiry,omitempty"`
	Retry  int          `json:"retry,omitempty"`
	Extend []ExtendJSON `json:"extend,omitempty"`
	// meta
	IDs []uint64 `json:"seq_id,omitempty"`
}

type ExtendJSON struct {
	Key   string `json:"key"`
	Value string `json:"val"`
}

type ResponseGaurun struct {
	Message string `json:"message"`
}

type CertificatePem struct {
	Cert []byte
	Key  []byte
}

func InitGCMClient() {
	TransportGaurun = &http.Transport{MaxIdleConnsPerHost: ConfGaurun.Core.WorkerNum}
	GCMClient = &gcm.Sender{ApiKey: ConfGaurun.Android.ApiKey}
	GCMClient.Http = &http.Client{Transport: TransportGaurun}
	GCMClient.Http.Timeout = time.Duration(ConfGaurun.Android.Timeout) * time.Second
}

func StartPushWorkers(workerNum, queueNum int) {
	QueueNotification = make(chan RequestGaurunNotification, queueNum)
	for i := 0; i < workerNum; i++ {
		go pushNotificationWorker()
	}
}

func enqueueNotifications(notifications []RequestGaurunNotification) {
	for _, notification := range notifications {
		err := validateNotification(&notification)
		if err != nil {
			LogError.Error(err)
			continue
		}
		var enabledPush bool
		switch notification.Platform {
		case PlatFormIos:
			enabledPush = ConfGaurun.Ios.Enabled
		case PlatFormAndroid:
			enabledPush = ConfGaurun.Android.Enabled
		}
		if enabledPush {
			notification.IDs = make([]uint64, len(notification.Tokens))
			for i := 0; i < len(notification.IDs); i++ {
				notification.IDs[i] = numberingPush()
				LogPush(notification.IDs[i], StatusAcceptedPush, notification.Tokens[i], 0, notification, nil)
			}
			QueueNotification <- notification
		}
	}
}

func classifyByDevice(reqGaurun *RequestGaurun) ([]RequestGaurunNotification, []RequestGaurunNotification) {
	var (
		reqGaurunNotificationIos     []RequestGaurunNotification
		reqGaurunNotificationAndroid []RequestGaurunNotification
	)
	for _, notification := range reqGaurun.Notifications {
		switch notification.Platform {
		case PlatFormIos:
			reqGaurunNotificationIos = append(reqGaurunNotificationIos, notification)
		case PlatFormAndroid:
			reqGaurunNotificationAndroid = append(reqGaurunNotificationAndroid, notification)
		}
	}
	return reqGaurunNotificationIos, reqGaurunNotificationAndroid
}

func pushNotificationIos(req RequestGaurunNotification, client *apns.Client) bool {
	LogError.Debug("START push notification for iOS")

	for i, token := range req.Tokens {
		id := req.IDs[i]
		payload := apns.NewPayload()
		payload.Alert = req.Message
		payload.Badge = req.Badge
		payload.Sound = req.Sound

		pn := apns.NewPushNotification()
		pn.DeviceToken = token
		pn.Expiry = uint32(req.Expiry)
		pn.AddPayload(payload)

		if len(req.Extend) > 0 {
			for _, extend := range req.Extend {
				pn.Set(extend.Key, extend.Value)
			}
		}

		stime := time.Now()
		resp := client.Send(pn)
		etime := time.Now()
		ptime := etime.Sub(stime).Seconds()

		if resp.Error != nil {
			atomic.AddInt64(&StatGaurun.Ios.PushError, 1)
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, resp.Error)
			client.Conn.Close()
			client.ConnTls.Close()
			return false
		} else {
			LogPush(id, StatusSucceededPush, token, ptime, req, nil)
			atomic.AddInt64(&StatGaurun.Ios.PushSuccess, 1)
		}
	}

	client = nil
	LogError.Debug("END push notification for iOS")
	return true
}

func pushNotificationAndroid(req RequestGaurunNotification) bool {
	LogError.Debug("START push notification for Android")

	data := map[string]interface{}{"message": req.Message}
	if len(req.Extend) > 0 {
		for _, extend := range req.Extend {
			data[extend.Key] = extend.Value
		}
	}

	msg := gcm.NewMessage(data, req.Tokens...)
	msg.CollapseKey = req.CollapseKey
	msg.DelayWhileIdle = req.DelayWhileIdle
	msg.TimeToLive = req.TimeToLive

	stime := time.Now()
	resp, err := GCMClient.SendNoRetry(msg)
	etime := time.Now()
	ptime := etime.Sub(stime).Seconds()
	if err != nil {
		atomic.AddInt64(&StatGaurun.Android.PushError, 1)
		for i, token := range req.Tokens {
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, err)
		}
		return false
	}

	if resp.Failure > 0 {
		atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(resp.Success))
		atomic.AddInt64(&StatGaurun.Android.PushError, int64(resp.Failure))
		if len(resp.Results) == len(req.Tokens) {
			for i, token := range req.Tokens {
				if resp.Results[i].Error != "" {
					LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, errors.New(resp.Results[i].Error))
				}
			}
		}
		return true
	}

	for i, token := range req.Tokens {
		LogPush(req.IDs[i], StatusSucceededPush, token, ptime, req, nil)
	}
	StatGaurun.Android.PushSuccess += int64(len(req.Tokens))
	LogError.Debug("END push notification for Android")
	return true
}

func pushNotificationWorker() {
	var (
		success    bool
		retryMax   int
		ep         string
		apnsClient *apns.Client
		loop       int
		err        error
	)
	if ConfGaurun.Ios.Sandbox {
		ep = EpApnsSandbox
	} else {
		ep = EpApnsProd
	}

	apnsClient = nil
	loop = 0
	for {
		stime := time.Now()

		notification := <-QueueNotification

		etime := time.Now()
		itime := etime.Sub(stime).Seconds()

		if notification.Platform == PlatFormIos {
			if apnsClient != nil && int(itime) > ConfGaurun.Ios.KeepAliveIdleTimeout {
				apnsClient.Conn.Close()
				apnsClient.ConnTls.Close()
				apnsClient = nil
			}

			if apnsClient != nil && ConfGaurun.Ios.KeepAliveMax > 0 && loop > ConfGaurun.Ios.KeepAliveMax {
				apnsClient.Conn.Close()
				apnsClient.ConnTls.Close()
				apnsClient = nil
				loop = 0
			}

			loop++

			if apnsClient == nil {
				apnsClient, err = apns.NewClient(
					ep,
					ConfGaurun.Ios.PemCertPath,
					ConfGaurun.Ios.PemKeyPath,
					0,
				)
				if err != nil {
					LogError.Errorf("failed to connect to APNS: %s", err.Error())
					apnsClient = nil
					loop = 0
					QueueNotification <- notification
					continue
				}
				apnsClient.TimeoutWaitError = time.Duration(ConfGaurun.Ios.TimeoutError) * time.Millisecond
			}
		}

		switch notification.Platform {
		case PlatFormIos:
			success = pushNotificationIos(notification, apnsClient)
			if !success {
				apnsClient = nil
			}
			retryMax = ConfGaurun.Ios.RetryMax
		case PlatFormAndroid:
			success = pushNotificationAndroid(notification)
			retryMax = ConfGaurun.Android.RetryMax
		}
		if !success && notification.Retry < retryMax {
			if len(QueueNotification) < cap(QueueNotification) {
				notification.Retry++
				QueueNotification <- notification
			}
		}
	}
}

func validateNotification(notification *RequestGaurunNotification) error {

	for _, token := range notification.Tokens {
		if len(token) == 0 {
			return errors.New("empty token")
		}
	}

	if notification.Platform < 1 || notification.Platform > 2 {
		return errors.New("invalid platform")
	}

	if len(notification.Message) == 0 {
		return errors.New("empty message")
	}

	return nil
}

func sendResponse(w http.ResponseWriter, msg string, code int) {
	var (
		respGaurun ResponseGaurun
	)

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Server", serverHeader())

	w.WriteHeader(code)
	respGaurun.Message = msg
	err := json.NewEncoder(w).Encode(&respGaurun)
	if err != nil {
		// Internal Server Error(500) should be returned by right.
		// But 'code' is returned because of the limitation of json.NewEncoder and WriteHeader.
		msg := "Response-body could not be created"
		fmt.Fprintf(w, msg)
		LogError.Error(msg)
		return
	}
}

func PushNotificationHandler(w http.ResponseWriter, r *http.Request) {
	LogAcceptedRequest(ConfGaurun.Api.PushUri, r.Method, r.Proto, r.ContentLength)
	LogError.Debug("push-request is Accepted")

	LogError.Debug("method check")
	if r.Method != "POST" {
		sendResponse(w, "method must be POST", http.StatusBadRequest)
		return
	}

	var (
		reqGaurun RequestGaurun
		err       error
	)

	if ConfGaurun.Log.Level == "debug" {
		reqBody, err := ioutil.ReadAll(r.Body)
		if err != nil {
			sendResponse(w, "failed to read request-body", http.StatusInternalServerError)
			return
		}
		LogError.Debugf("parse request body: %s", reqBody)
		err = json.Unmarshal(reqBody, &reqGaurun)
	} else {
		LogError.Debug("parse request body")
		err = json.NewDecoder(r.Body).Decode(&reqGaurun)
	}

	if err != nil {
		sendResponse(w, "Request-body is malformed", http.StatusBadRequest)
		return
	}

	if len(reqGaurun.Notifications) == 0 {
		sendResponse(w, "empty notification", http.StatusBadRequest)
		return
	} else if len(reqGaurun.Notifications) > ConfGaurun.Core.NotificationMax {
		msg := fmt.Sprintf("number of notifications(%d) over limit(%d)", len(reqGaurun.Notifications), ConfGaurun.Core.NotificationMax)
		sendResponse(w, msg, http.StatusBadRequest)
		return
	}

	LogError.Debug("enqueue notification")
	go enqueueNotifications(reqGaurun.Notifications)

	LogError.Debug("response to client")
	sendResponse(w, "ok", http.StatusOK)
}
<commit_msg>[FIX] test for accept silent push<commit_after>package gaurun

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/alexjlockwood/gcm"
	"github.com/cubicdaiya/apns"
	"io/ioutil"
	"net/http"
	"sync/atomic"
	"time"
)

type RequestGaurun struct {
	Notifications []RequestGaurunNotification `json:"notifications"`
}

type RequestGaurunNotification struct {
	// Common
	Tokens   []string `json:"token"`
	Platform int      `json:"platform"`
	Message  string   `json:"message,omitempty"`
	// Android
	CollapseKey    string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool   `json:"delay_while_idle,omitempty"`
	TimeToLive     int    `json:"time_to_live,omitempty"`
	// iOS
	Badge  int          `json:"badge,omitempty"`
	Sound  string       `json:"sound,omitempty"`
	Expiry int          `json:"expiry,omitempty"`
	Retry  int          `json:"retry,omitempty"`
	ContentAvailable int `json:"content-available,omitempty"`
	Extend []ExtendJSON `json:"extend,omitempty"`
	// meta
	IDs []uint64 `json:"seq_id,omitempty"`
}

type ExtendJSON struct {
	Key   string `json:"key"`
	Value string `json:"val"`
}

type ResponseGaurun struct {
	Message string `json:"message"`
}

type CertificatePem struct {
	Cert []byte
	Key  []byte
}

func InitGCMClient() {
	TransportGaurun = &http.Transport{MaxIdleConnsPerHost: ConfGaurun.Core.WorkerNum}
	GCMClient = &gcm.Sender{ApiKey: ConfGaurun.Android.ApiKey}
	GCMClient.Http = &http.Client{Transport: TransportGaurun}
	GCMClient.Http.Timeout = time.Duration(ConfGaurun.Android.Timeout) * time.Second
}

func StartPushWorkers(workerNum, queueNum int) {
	QueueNotification = make(chan RequestGaurunNotification, queueNum)
	for i := 0; i < workerNum; i++ {
		go pushNotificationWorker()
	}
}

func enqueueNotifications(notifications []RequestGaurunNotification) {
	for _, notification := range notifications {
		err := validateNotification(&notification)
		if err != nil {
			LogError.Error(err)
			continue
		}
		var enabledPush bool
		switch notification.Platform {
		case PlatFormIos:
			enabledPush = ConfGaurun.Ios.Enabled
		case PlatFormAndroid:
			enabledPush = ConfGaurun.Android.Enabled
		}
		if enabledPush {
			notification.IDs = make([]uint64, len(notification.Tokens))
			for i := 0; i < len(notification.IDs); i++ {
				notification.IDs[i] = numberingPush()
				LogPush(notification.IDs[i], StatusAcceptedPush, notification.Tokens[i], 0, notification, nil)
			}
			QueueNotification <- notification
		}
	}
}

func classifyByDevice(reqGaurun *RequestGaurun) ([]RequestGaurunNotification, []RequestGaurunNotification) {
	var (
		reqGaurunNotificationIos     []RequestGaurunNotification
		reqGaurunNotificationAndroid []RequestGaurunNotification
	)
	for _, notification := range reqGaurun.Notifications {
		switch notification.Platform {
		case PlatFormIos:
			reqGaurunNotificationIos = append(reqGaurunNotificationIos, notification)
		case PlatFormAndroid:
			reqGaurunNotificationAndroid = append(reqGaurunNotificationAndroid, notification)
		}
	}
	return reqGaurunNotificationIos, reqGaurunNotificationAndroid
}

func pushNotificationIos(req RequestGaurunNotification, client *apns.Client) bool {
	LogError.Debug("START push notification for iOS")

	for i, token := range req.Tokens {
		id := req.IDs[i]
		payload := apns.NewPayload()
		payload.Alert = req.Message
		payload.Badge = req.Badge
		payload.Sound = req.Sound
		payload.ContentAvailable = req.ContentAvailable

		pn := apns.NewPushNotification()
		pn.DeviceToken = token
		pn.Expiry = uint32(req.Expiry)
		pn.AddPayload(payload)

		if len(req.Extend) > 0 {
			for _, extend := range req.Extend {
				pn.Set(extend.Key, extend.Value)
			}
		}

		stime := time.Now()
		resp := client.Send(pn)
		etime := time.Now()
		ptime := etime.Sub(stime).Seconds()

		if resp.Error != nil {
			atomic.AddInt64(&StatGaurun.Ios.PushError, 1)
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, resp.Error)
			client.Conn.Close()
			client.ConnTls.Close()
			return false
		} else {
			LogPush(id, StatusSucceededPush, token, ptime, req, nil)
			atomic.AddInt64(&StatGaurun.Ios.PushSuccess, 1)
		}
	}

	client = nil
	LogError.Debug("END push notification for iOS")
	return true
}

func pushNotificationAndroid(req RequestGaurunNotification) bool {
	LogError.Debug("START push notification for Android")

	data := map[string]interface{}{"message": req.Message}
	if len(req.Extend) > 0 {
		for _, extend := range req.Extend {
			data[extend.Key] = extend.Value
		}
	}

	msg := gcm.NewMessage(data, req.Tokens...)
	msg.CollapseKey = req.CollapseKey
	msg.DelayWhileIdle = req.DelayWhileIdle
	msg.TimeToLive = req.TimeToLive

	stime := time.Now()
	resp, err := GCMClient.SendNoRetry(msg)
	etime := time.Now()
	ptime := etime.Sub(stime).Seconds()
	if err != nil {
		atomic.AddInt64(&StatGaurun.Android.PushError, 1)
		for i, token := range req.Tokens {
			LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, err)
		}
		return false
	}

	if resp.Failure > 0 {
		atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(resp.Success))
		atomic.AddInt64(&StatGaurun.Android.PushError, int64(resp.Failure))
		if len(resp.Results) == len(req.Tokens) {
			for i, token := range req.Tokens {
				if resp.Results[i].Error != "" {
					LogPush(req.IDs[i], StatusFailedPush, token, ptime, req, errors.New(resp.Results[i].Error))
				}
			}
		}
		return true
	}

	for i, token := range req.Tokens {
		LogPush(req.IDs[i], StatusSucceededPush, token, ptime, req, nil)
	}
	StatGaurun.Android.PushSuccess += int64(len(req.Tokens))
	LogError.Debug("END push notification for Android")
	return true
}

func pushNotificationWorker() {
	var (
		success    bool
		retryMax   int
		ep         string
		apnsClient *apns.Client
		loop       int
		err        error
	)
	if ConfGaurun.Ios.Sandbox {
		ep = EpApnsSandbox
	} else {
		ep = EpApnsProd
	}

	apnsClient = nil
	loop = 0
	for {
		stime := time.Now()

		notification := <-QueueNotification

		etime := time.Now()
		itime := etime.Sub(stime).Seconds()

		if notification.Platform == PlatFormIos {
			if apnsClient != nil && int(itime) > ConfGaurun.Ios.KeepAliveIdleTimeout {
				apnsClient.Conn.Close()
				apnsClient.ConnTls.Close()
				apnsClient = nil
			}

			if apnsClient != nil && ConfGaurun.Ios.KeepAliveMax > 0 && loop > ConfGaurun.Ios.KeepAliveMax {
				apnsClient.Conn.Close()
				apnsClient.ConnTls.Close()
				apnsClient = nil
				loop = 0
			}

			loop++

			if apnsClient == nil {
				apnsClient, err = apns.NewClient(
					ep,
					ConfGaurun.Ios.PemCertPath,
					ConfGaurun.Ios.PemKeyPath,
					0,
				)
				if err != nil {
					LogError.Errorf("failed to connect to APNS: %s", err.Error())
					apnsClient = nil
					loop = 0
					QueueNotification <- notification
					continue
				}
				apnsClient.TimeoutWaitError = time.Duration(ConfGaurun.Ios.TimeoutError) * time.Millisecond
			}
		}

		switch notification.Platform {
		case PlatFormIos:
			success = pushNotificationIos(notification, apnsClient)
			if !success {
				apnsClient = nil
			}
			retryMax = ConfGaurun.Ios.RetryMax
		case PlatFormAndroid:
			success = pushNotificationAndroid(notification)
			retryMax = ConfGaurun.Android.RetryMax
		}
		if !success && notification.Retry < retryMax {
			if len(QueueNotification) < cap(QueueNotification) {
				notification.Retry++
				QueueNotification <- notification
			}
		}
	}
}

func validateNotification(notification *RequestGaurunNotification) error {

	for _, token := range notification.Tokens {
		if len(token) == 0 {
			return errors.New("empty token")
		}
	}

	if notification.Platform < 1 || notification.Platform > 2 {
		return errors.New("invalid platform")
	}

	if len(notification.Message) == 0 && notification.ContentAvailable != 1 {
		return errors.New("empty message")
	}

	return nil
}

func sendResponse(w http.ResponseWriter, msg string, code int) {
	var (
		respGaurun ResponseGaurun
	)

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Server", serverHeader())

	w.WriteHeader(code)
	respGaurun.Message = msg
	err := json.NewEncoder(w).Encode(&respGaurun)
	if err != nil {
		// Internal Server Error(500) should be returned by right.
		// But 'code' is returned because of the limitation of json.NewEncoder and WriteHeader.
		msg := "Response-body could not be created"
		fmt.Fprintf(w, msg)
		LogError.Error(msg)
		return
	}
}

func PushNotificationHandler(w http.ResponseWriter, r *http.Request) {
	LogAcceptedRequest(ConfGaurun.Api.PushUri, r.Method, r.Proto, r.ContentLength)
	LogError.Debug("push-request is Accepted")

	LogError.Debug("method check")
	if r.Method != "POST" {
		sendResponse(w, "method must be POST", http.StatusBadRequest)
		return
	}

	var (
		reqGaurun RequestGaurun
		err       error
	)

	if ConfGaurun.Log.Level == "debug" {
		reqBody, err := ioutil.ReadAll(r.Body)
		if err != nil {
			sendResponse(w, "failed to read request-body", http.StatusInternalServerError)
			return
		}
		LogError.Debugf("parse request body: %s", reqBody)
		err = json.Unmarshal(reqBody, &reqGaurun)
	} else {
		LogError.Debug("parse request body")
		err = json.NewDecoder(r.Body).Decode(&reqGaurun)
	}

	if err != nil {
		sendResponse(w, "Request-body is malformed", http.StatusBadRequest)
		return
	}

	if len(reqGaurun.Notifications) == 0 {
		sendResponse(w, "empty notification", http.StatusBadRequest)
		return
	} else if len(reqGaurun.Notifications) > ConfGaurun.Core.NotificationMax {
		msg := fmt.Sprintf("number of notifications(%d) over limit(%d)", len(reqGaurun.Notifications), ConfGaurun.Core.NotificationMax)
		sendResponse(w, msg, http.StatusBadRequest)
		return
	}

	LogError.Debug("enqueue notification")
	go enqueueNotifications(reqGaurun.Notifications)

	LogError.Debug("response to client")
	sendResponse(w, "ok", http.StatusOK)
}
<|endoftext|>


<commit_before>// Copyright 2020 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 or at
// https://developers.google.com/open-source/licenses/bsd.

package main

import (
	"net/http"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	"golang.org/x/net/context/ctxhttp"
)

type pkggodevEvent struct {
	Host    string
	Path    string
	Status  int
	URL     string
	Latency time.Duration
	Error   error
}

func teeRequestToPkgGoDev(godocReq *http.Request, latency time.Duration, isRobot bool, status int) (gddoEvent *gddoEvent, pkgEvent *pkggodevEvent) {
	gddoEvent = newGDDOEvent(godocReq, latency, isRobot, status)
	u := pkgGoDevURL(godocReq.URL)

	// Strip the utm_source from the URL.
	vals := u.Query()
	vals.Del("utm_source")
	u.RawQuery = vals.Encode()

	pkgEvent = &pkggodevEvent{
		Host: u.Host,
		Path: u.Path,
		URL:  u.String(),
	}
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		pkgEvent.Status = http.StatusInternalServerError
		pkgEvent.Error = err
		return gddoEvent, pkgEvent
	}
	start := time.Now()
	xfwd := req.Header.Get("X-Forwarded-for")
	req.Header.Set("X-Godoc-Forwarded-for", xfwd)
	resp, err := ctxhttp.Do(godocReq.Context(), http.DefaultClient, req)
	if err != nil {
		// Use StatusBadGateway to indicate the upstream error.
		pkgEvent.Status = http.StatusBadGateway
		pkgEvent.Error = err
		return gddoEvent, pkgEvent
	}

	pkgEvent.Status = resp.StatusCode
	pkgEvent.Latency = time.Since(start)
	return gddoEvent, pkgEvent

}

// doNotTeeURLsToPkgGoDev are paths that should not be teed to pkg.go.dev.
var doNotTeeURLsToPkgGoDev = map[string]bool{
	"/-/bot":     true,
	"/-/refresh": true,
}

// doNotTeeExtsToPkgGoDev are URL extensions that should not be teed to
// pkg.go.dev.
var doNotTeeExtsToPkgGoDev = map[string]bool{
	".css":  true,
	".html": true,
	".js":   true,
	".txt":  true,
	".xml":  true,
	".ico":  true,
}

// shouldTeeRequest reports whether a request should be teed to pkg.go.dev.
func shouldTeeRequest(u string) bool {
	// Don't tee App Engine requests to pkg.go.dev.
	if strings.HasPrefix(u, "/_ah/") {
		return false
	}
	ext := filepath.Ext(u)
	if doNotTeeExtsToPkgGoDev[ext] {
		return false
	}
	if doNotTeeURLsToPkgGoDev[u] {
		return false
	}
	return true
}

type gddoEvent struct {
	Host        string
	Path        string
	Status      int
	URL         string
	Header      http.Header
	Latency     time.Duration
	IsRobot     bool
	UsePkgGoDev bool
	Error       error
}

func newGDDOEvent(r *http.Request, latency time.Duration, isRobot bool, status int) *gddoEvent {
	targetURL := url.URL{
		Scheme:   "https",
		Host:     r.URL.Host,
		Path:     r.URL.Path,
		RawQuery: r.URL.RawQuery,
	}
	if targetURL.Host == "" && r.Host != "" {
		targetURL.Host = r.Host
	}
	return &gddoEvent{
		Host:        targetURL.Host,
		Path:        r.URL.Path,
		Status:      status,
		URL:         targetURL.String(),
		Header:      r.Header,
		Latency:     latency,
		IsRobot:     isRobot,
		UsePkgGoDev: shouldRedirectToPkgGoDev(r),
	}
}

func userReturningFromPkgGoDev(req *http.Request) bool {
	return req.FormValue("utm_source") == "backtogodoc"
}

const (
	pkgGoDevRedirectCookie = "pkggodev-redirect"
	pkgGoDevRedirectParam  = "redirect"
	pkgGoDevRedirectOn     = "on"
	pkgGoDevRedirectOff    = "off"
	pkgGoDevHost           = "pkg.go.dev"
)

func shouldRedirectToPkgGoDev(req *http.Request) bool {
	// API requests are not redirected.
	if strings.HasPrefix(req.URL.Host, "api") {
		return false
	}
	redirectParam := req.FormValue(pkgGoDevRedirectParam)
	if redirectParam == pkgGoDevRedirectOn || redirectParam == pkgGoDevRedirectOff {
		return redirectParam == pkgGoDevRedirectOn
	}
	cookie, err := req.Cookie(pkgGoDevRedirectCookie)
	return (err == nil && cookie.Value == pkgGoDevRedirectOn)
}

// pkgGoDevRedirectHandler redirects requests from godoc.org to pkg.go.dev,
// based on whether a cookie is set for pkggodev-redirect. The cookie
// can be turned on/off using a query param.
func pkgGoDevRedirectHandler(f func(http.ResponseWriter, *http.Request) error) func(http.ResponseWriter, *http.Request) error {
	return func(w http.ResponseWriter, r *http.Request) error {
		if userReturningFromPkgGoDev(r) {
			return f(w, r)
		}

		redirectParam := r.FormValue(pkgGoDevRedirectParam)

		if redirectParam == pkgGoDevRedirectOn {
			cookie := &http.Cookie{Name: pkgGoDevRedirectCookie, Value: redirectParam, Path: "/"}
			http.SetCookie(w, cookie)
		}
		if redirectParam == pkgGoDevRedirectOff {
			cookie := &http.Cookie{Name: pkgGoDevRedirectCookie, Value: "", MaxAge: -1, Path: "/"}
			http.SetCookie(w, cookie)
		}

		if !shouldRedirectToPkgGoDev(r) {
			return f(w, r)
		}

		http.Redirect(w, r, pkgGoDevURL(r.URL).String(), http.StatusFound)
		return nil
	}
}

func pkgGoDevURL(godocURL *url.URL) *url.URL {
	u := &url.URL{Scheme: "https", Host: pkgGoDevHost}
	q := url.Values{"utm_source": []string{"godoc"}}

	if strings.Contains(godocURL.Path, "/vendor/") || strings.HasSuffix(godocURL.Path, "/vendor") {
		u.Path = "/"
		u.RawQuery = q.Encode()
		return u
	}

	switch godocURL.Path {
	case "/-/go":
		u.Path = "/std"
	case "/-/about":
		u.Path = "/about"
	case "/C":
		u.Path = "/C"
	case "/":
		if qparam := godocURL.Query().Get("q"); qparam != "" {
			u.Path = "/search"
			q.Set("q", qparam)
		} else {
			u.Path = "/"
		}
	case "/-/subrepo":
		u.Path = "/search"
		q.Set("q", "golang.org/x")
	default:
		{
			_, isSVG := godocURL.Query()["status.svg"]
			_, isPNG := godocURL.Query()["status.png"]
			if isSVG || isPNG {
				u.Path = "/badge" + godocURL.Path
				break
			}

			u.Path = godocURL.Path
			if _, ok := godocURL.Query()["imports"]; ok {
				q.Set("tab", "imports")
			} else if _, ok := godocURL.Query()["importers"]; ok {
				q.Set("tab", "importedby")
			}
		}
	}

	u.RawQuery = q.Encode()
	return u
}
<commit_msg>gddo-server: fetch path on pkg.go.dev 404<commit_after>// Copyright 2020 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 or at
// https://developers.google.com/open-source/licenses/bsd.

package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	"golang.org/x/net/context/ctxhttp"
)

type pkggodevEvent struct {
	Host    string
	Path    string
	Status  int
	URL     string
	Latency time.Duration
	Error   string
	// If a request 404s, make a request to fetch it and store the response.
	FetchStatus   int
	FetchResponse string
}

func teeRequestToPkgGoDev(godocReq *http.Request, latency time.Duration, isRobot bool, status int) (gddoEvent *gddoEvent, pkgEvent *pkggodevEvent) {
	gddoEvent = newGDDOEvent(godocReq, latency, isRobot, status)
	u := pkgGoDevURL(godocReq.URL)

	// Strip the utm_source from the URL.
	vals := u.Query()
	vals.Del("utm_source")
	u.RawQuery = vals.Encode()

	pkgEvent = &pkggodevEvent{
		Host: u.Host,
		Path: u.Path,
		URL:  u.String(),
	}
	start := time.Now()
	status, errResp := makeRequest(godocReq.Context(), u.String())
	pkgEvent.Status = status
	pkgEvent.Latency = time.Since(start)
	// The response will always be an error here if not empty.
	pkgEvent.Error = errResp

	if pkgEvent.Status == http.StatusNotFound && gddoEvent.Status == http.StatusOK {
		// If the request was successful on godoc.org but returned a 404 on
		// pkg.go.dev make a fetch request.
		status, body := makeRequest(godocReq.Context(), "/fetch"+u.String())
		pkgEvent.FetchStatus = status
		pkgEvent.FetchResponse = body
	}
	return gddoEvent, pkgEvent
}

func makeRequest(ctx context.Context, url string) (int, string) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return http.StatusInternalServerError, fmt.Sprintf("http.NewRequest: %v", err)
	}
	xfwd := req.Header.Get("X-Forwarded-for")
	req.Header.Set("X-Godoc-Forwarded-for", xfwd)
	resp, err := ctxhttp.Do(ctx, http.DefaultClient, req)
	if err != nil {
		// Use StatusBadGateway to indicate the upstream error.
		return http.StatusBadGateway, err.Error()
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return resp.StatusCode, fmt.Sprintf("can't read body: %v", err)
	}
	return resp.StatusCode, string(body)
}

// doNotTeeURLsToPkgGoDev are paths that should not be teed to pkg.go.dev.
var doNotTeeURLsToPkgGoDev = map[string]bool{
	"/-/bot":     true,
	"/-/refresh": true,
}

// doNotTeeExtsToPkgGoDev are URL extensions that should not be teed to
// pkg.go.dev.
var doNotTeeExtsToPkgGoDev = map[string]bool{
	".css":  true,
	".html": true,
	".js":   true,
	".txt":  true,
	".xml":  true,
	".ico":  true,
}

// shouldTeeRequest reports whether a request should be teed to pkg.go.dev.
func shouldTeeRequest(u string) bool {
	// Don't tee App Engine requests to pkg.go.dev.
	if strings.HasPrefix(u, "/_ah/") {
		return false
	}
	ext := filepath.Ext(u)
	if doNotTeeExtsToPkgGoDev[ext] {
		return false
	}
	if doNotTeeURLsToPkgGoDev[u] {
		return false
	}
	return true
}

type gddoEvent struct {
	Host        string
	Path        string
	Status      int
	URL         string
	Header      http.Header
	Latency     time.Duration
	IsRobot     bool
	UsePkgGoDev bool
	Error       error
}

func newGDDOEvent(r *http.Request, latency time.Duration, isRobot bool, status int) *gddoEvent {
	targetURL := url.URL{
		Scheme:   "https",
		Host:     r.URL.Host,
		Path:     r.URL.Path,
		RawQuery: r.URL.RawQuery,
	}
	if targetURL.Host == "" && r.Host != "" {
		targetURL.Host = r.Host
	}
	return &gddoEvent{
		Host:        targetURL.Host,
		Path:        r.URL.Path,
		Status:      status,
		URL:         targetURL.String(),
		Header:      r.Header,
		Latency:     latency,
		IsRobot:     isRobot,
		UsePkgGoDev: shouldRedirectToPkgGoDev(r),
	}
}

func userReturningFromPkgGoDev(req *http.Request) bool {
	return req.FormValue("utm_source") == "backtogodoc"
}

const (
	pkgGoDevRedirectCookie = "pkggodev-redirect"
	pkgGoDevRedirectParam  = "redirect"
	pkgGoDevRedirectOn     = "on"
	pkgGoDevRedirectOff    = "off"
	pkgGoDevHost           = "pkg.go.dev"
)

func shouldRedirectToPkgGoDev(req *http.Request) bool {
	// API requests are not redirected.
	if strings.HasPrefix(req.URL.Host, "api") {
		return false
	}
	redirectParam := req.FormValue(pkgGoDevRedirectParam)
	if redirectParam == pkgGoDevRedirectOn || redirectParam == pkgGoDevRedirectOff {
		return redirectParam == pkgGoDevRedirectOn
	}
	cookie, err := req.Cookie(pkgGoDevRedirectCookie)
	return (err == nil && cookie.Value == pkgGoDevRedirectOn)
}

// pkgGoDevRedirectHandler redirects requests from godoc.org to pkg.go.dev,
// based on whether a cookie is set for pkggodev-redirect. The cookie
// can be turned on/off using a query param.
func pkgGoDevRedirectHandler(f func(http.ResponseWriter, *http.Request) error) func(http.ResponseWriter, *http.Request) error {
	return func(w http.ResponseWriter, r *http.Request) error {
		if userReturningFromPkgGoDev(r) {
			return f(w, r)
		}

		redirectParam := r.FormValue(pkgGoDevRedirectParam)

		if redirectParam == pkgGoDevRedirectOn {
			cookie := &http.Cookie{Name: pkgGoDevRedirectCookie, Value: redirectParam, Path: "/"}
			http.SetCookie(w, cookie)
		}
		if redirectParam == pkgGoDevRedirectOff {
			cookie := &http.Cookie{Name: pkgGoDevRedirectCookie, Value: "", MaxAge: -1, Path: "/"}
			http.SetCookie(w, cookie)
		}

		if !shouldRedirectToPkgGoDev(r) {
			return f(w, r)
		}

		http.Redirect(w, r, pkgGoDevURL(r.URL).String(), http.StatusFound)
		return nil
	}
}

func pkgGoDevURL(godocURL *url.URL) *url.URL {
	u := &url.URL{Scheme: "https", Host: pkgGoDevHost}
	q := url.Values{"utm_source": []string{"godoc"}}

	if strings.Contains(godocURL.Path, "/vendor/") || strings.HasSuffix(godocURL.Path, "/vendor") {
		u.Path = "/"
		u.RawQuery = q.Encode()
		return u
	}

	switch godocURL.Path {
	case "/-/go":
		u.Path = "/std"
	case "/-/about":
		u.Path = "/about"
	case "/C":
		u.Path = "/C"
	case "/":
		if qparam := godocURL.Query().Get("q"); qparam != "" {
			u.Path = "/search"
			q.Set("q", qparam)
		} else {
			u.Path = "/"
		}
	case "/-/subrepo":
		u.Path = "/search"
		q.Set("q", "golang.org/x")
	default:
		{
			_, isSVG := godocURL.Query()["status.svg"]
			_, isPNG := godocURL.Query()["status.png"]
			if isSVG || isPNG {
				u.Path = "/badge" + godocURL.Path
				break
			}

			u.Path = godocURL.Path
			if _, ok := godocURL.Query()["imports"]; ok {
				q.Set("tab", "imports")
			} else if _, ok := godocURL.Query()["importers"]; ok {
				q.Set("tab", "importedby")
			}
		}
	}

	u.RawQuery = q.Encode()
	return u
}
<|endoftext|>


<commit_before>// Package insecure provides an insecure, unencrypted implementation of the the SecureConn and SecureTransport interfaces.
//
// Recommended only for testing and other non-production usage.
package insecure

import (
	"context"
	"fmt"
	"io"
	"net"

	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/sec"
	"github.com/libp2p/go-msgio"

	ci "github.com/libp2p/go-libp2p-core/crypto"
	pb "github.com/libp2p/go-libp2p-core/sec/insecure/pb"
)

// ID is the multistream-select protocol ID that should be used when identifying
// this security transport.
const ID = "/plaintext/2.0.0"

// Transport is a no-op stream security transport. It provides no
// security and simply mocks the security methods. Identity methods
// return the local peer's ID and private key, and whatever the remote
// peer presents as their ID and public key.
// No authentication of the remote identity is performed.
type Transport struct {
	id  peer.ID
	key ci.PrivKey
}

// New constructs a new insecure transport.
// Deprecated: use NewWithIdentity instead.
func New(id peer.ID) *Transport {
	return &Transport{
		id: id,
	}
}

// New constructs a new insecure transport. The provided private key
// is stored and returned from LocalPrivateKey to satisfy the
// SecureTransport interface, and the public key is sent to
// remote peers. No security is provided.
func NewWithIdentity(id peer.ID, key ci.PrivKey) *Transport {
	return &Transport{
		id:  id,
		key: key,
	}
}

// LocalPeer returns the transport's local peer ID.
func (t *Transport) LocalPeer() peer.ID {
	return t.id
}

// LocalPrivateKey returns the local private key.
// This key is used only for identity generation and provides no security.
func (t *Transport) LocalPrivateKey() ci.PrivKey {
	return t.key
}

// SecureInbound *pretends to secure* an inbound connection to the given peer.
// It sends the local peer's ID and public key, and receives the same from the remote peer.
// No validation is performed as to the authenticity or ownership of the provided public key,
// and the key exchange provides no security.
//
// SecureInbound may fail if the remote peer sends an ID and public key that are inconsistent
// with each other, or if a network error occurs during the ID exchange.
func (t *Transport) SecureInbound(ctx context.Context, insecure net.Conn) (sec.SecureConn, error) {
	conn := &Conn{
		Conn:         insecure,
		local:        t.id,
		localPrivKey: t.key,
	}

	err := conn.runHandshakeSync()
	if err != nil {
		return nil, err
	}

	return conn, nil
}

// SecureOutbound *pretends to secure* an outbound connection to the given peer.
// It sends the local peer's ID and public key, and receives the same from the remote peer.
// No validation is performed as to the authenticity or ownership of the provided public key,
// and the key exchange provides no security.
//
// SecureOutbound may fail if the remote peer sends an ID and public key that are inconsistent
// with each other, or if the ID sent by the remote peer does not match the one dialed. It may
// also fail if a network error occurs during the ID exchange.
func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
	conn := &Conn{
		Conn:         insecure,
		local:        t.id,
		localPrivKey: t.key,
	}

	err := conn.runHandshakeSync()
	if err != nil {
		return nil, err
	}

	if t.key != nil && p != conn.remote {
		return nil, fmt.Errorf("remote peer sent unexpected peer ID. expected=%s received=%s",
			p, conn.remote)
	}

	return conn, nil
}

// Conn is the connection type returned by the insecure transport.
type Conn struct {
	net.Conn

	local  peer.ID
	remote peer.ID

	localPrivKey ci.PrivKey
	remotePubKey ci.PubKey
}

func makeExchangeMessage(pubkey ci.PubKey) (*pb.Exchange, error) {
	keyMsg, err := ci.PublicKeyToProto(pubkey)
	if err != nil {
		return nil, err
	}
	id, err := peer.IDFromPublicKey(pubkey)
	if err != nil {
		return nil, err
	}

	return &pb.Exchange{
		Id:     []byte(id),
		Pubkey: keyMsg,
	}, nil
}

func (ic *Conn) runHandshakeSync() error {
	// If we were initialized without keys, behave as in plaintext/1.0.0 (do nothing)
	if ic.localPrivKey == nil {
		return nil
	}

	// Generate an Exchange message
	msg, err := makeExchangeMessage(ic.localPrivKey.GetPublic())
	if err != nil {
		return err
	}

	// Send our Exchange and read theirs
	remoteMsg, err := readWriteMsg(ic.Conn, msg)
	if err != nil {
		return err
	}

	// Pull remote ID and public key from message
	remotePubkey, err := ci.PublicKeyFromProto(remoteMsg.Pubkey)
	if err != nil {
		return err
	}

	remoteID, err := peer.IDFromBytes(remoteMsg.Id)
	if err != nil {
		return err
	}

	// Validate that ID matches public key
	if !remoteID.MatchesPublicKey(remotePubkey) {
		calculatedID, _ := peer.IDFromPublicKey(remotePubkey)
		return fmt.Errorf("remote peer id does not match public key. id=%s calculated_id=%s",
			remoteID, calculatedID)
	}

	// Add remote ID and key to conn state
	ic.remotePubKey = remotePubkey
	ic.remote = remoteID
	return nil
}

// read and write a message at the same time.
func readWriteMsg(rw io.ReadWriter, out *pb.Exchange) (*pb.Exchange, error) {
	const maxMessageSize = 1 << 16

	outBytes, err := out.Marshal()
	if err != nil {
		return nil, err
	}
	wresult := make(chan error)
	go func() {
		w := msgio.NewVarintWriter(rw)
		wresult <- w.WriteMsg(outBytes)
	}()

	r := msgio.NewVarintReaderSize(rw, maxMessageSize)
	msg, err1 := r.ReadMsg()

	// Always wait for the read to finish.
	err2 := <-wresult

	if err1 != nil {
		return nil, err1
	}
	if err2 != nil {
		r.ReleaseMsg(msg)
		return nil, err2
	}
	inMsg := new(pb.Exchange)
	err = inMsg.Unmarshal(msg)
	return inMsg, err
}

// LocalPeer returns the local peer ID.
func (ic *Conn) LocalPeer() peer.ID {
	return ic.local
}

// RemotePeer returns the remote peer ID if we initiated the dial. Otherwise, it
// returns "" (because this connection isn't actually secure).
func (ic *Conn) RemotePeer() peer.ID {
	return ic.remote
}

// RemotePublicKey returns whatever public key was given by the remote peer.
// Note that no verification of ownership is done, as this connection is not secure.
func (ic *Conn) RemotePublicKey() ci.PubKey {
	return ic.remotePubKey
}

// LocalPrivateKey returns the private key for the local peer.
func (ic *Conn) LocalPrivateKey() ci.PrivKey {
	return ic.localPrivKey
}

var _ sec.SecureTransport = (*Transport)(nil)
var _ sec.SecureConn = (*Conn)(nil)
<commit_msg>remove deprecated constructor for the insecure transport (#206)<commit_after>// Package insecure provides an insecure, unencrypted implementation of the the SecureConn and SecureTransport interfaces.
//
// Recommended only for testing and other non-production usage.
package insecure

import (
	"context"
	"fmt"
	"io"
	"net"

	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/sec"
	"github.com/libp2p/go-msgio"

	ci "github.com/libp2p/go-libp2p-core/crypto"
	pb "github.com/libp2p/go-libp2p-core/sec/insecure/pb"
)

// ID is the multistream-select protocol ID that should be used when identifying
// this security transport.
const ID = "/plaintext/2.0.0"

// Transport is a no-op stream security transport. It provides no
// security and simply mocks the security methods. Identity methods
// return the local peer's ID and private key, and whatever the remote
// peer presents as their ID and public key.
// No authentication of the remote identity is performed.
type Transport struct {
	id  peer.ID
	key ci.PrivKey
}

// NewWithIdentity constructs a new insecure transport. The provided private key
// is stored and returned from LocalPrivateKey to satisfy the
// SecureTransport interface, and the public key is sent to
// remote peers. No security is provided.
func NewWithIdentity(id peer.ID, key ci.PrivKey) *Transport {
	return &Transport{
		id:  id,
		key: key,
	}
}

// LocalPeer returns the transport's local peer ID.
func (t *Transport) LocalPeer() peer.ID {
	return t.id
}

// LocalPrivateKey returns the local private key.
// This key is used only for identity generation and provides no security.
func (t *Transport) LocalPrivateKey() ci.PrivKey {
	return t.key
}

// SecureInbound *pretends to secure* an inbound connection to the given peer.
// It sends the local peer's ID and public key, and receives the same from the remote peer.
// No validation is performed as to the authenticity or ownership of the provided public key,
// and the key exchange provides no security.
//
// SecureInbound may fail if the remote peer sends an ID and public key that are inconsistent
// with each other, or if a network error occurs during the ID exchange.
func (t *Transport) SecureInbound(ctx context.Context, insecure net.Conn) (sec.SecureConn, error) {
	conn := &Conn{
		Conn:         insecure,
		local:        t.id,
		localPrivKey: t.key,
	}

	err := conn.runHandshakeSync()
	if err != nil {
		return nil, err
	}

	return conn, nil
}

// SecureOutbound *pretends to secure* an outbound connection to the given peer.
// It sends the local peer's ID and public key, and receives the same from the remote peer.
// No validation is performed as to the authenticity or ownership of the provided public key,
// and the key exchange provides no security.
//
// SecureOutbound may fail if the remote peer sends an ID and public key that are inconsistent
// with each other, or if the ID sent by the remote peer does not match the one dialed. It may
// also fail if a network error occurs during the ID exchange.
func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
	conn := &Conn{
		Conn:         insecure,
		local:        t.id,
		localPrivKey: t.key,
	}

	err := conn.runHandshakeSync()
	if err != nil {
		return nil, err
	}

	if t.key != nil && p != conn.remote {
		return nil, fmt.Errorf("remote peer sent unexpected peer ID. expected=%s received=%s",
			p, conn.remote)
	}

	return conn, nil
}

// Conn is the connection type returned by the insecure transport.
type Conn struct {
	net.Conn

	local  peer.ID
	remote peer.ID

	localPrivKey ci.PrivKey
	remotePubKey ci.PubKey
}

func makeExchangeMessage(pubkey ci.PubKey) (*pb.Exchange, error) {
	keyMsg, err := ci.PublicKeyToProto(pubkey)
	if err != nil {
		return nil, err
	}
	id, err := peer.IDFromPublicKey(pubkey)
	if err != nil {
		return nil, err
	}

	return &pb.Exchange{
		Id:     []byte(id),
		Pubkey: keyMsg,
	}, nil
}

func (ic *Conn) runHandshakeSync() error {
	// If we were initialized without keys, behave as in plaintext/1.0.0 (do nothing)
	if ic.localPrivKey == nil {
		return nil
	}

	// Generate an Exchange message
	msg, err := makeExchangeMessage(ic.localPrivKey.GetPublic())
	if err != nil {
		return err
	}

	// Send our Exchange and read theirs
	remoteMsg, err := readWriteMsg(ic.Conn, msg)
	if err != nil {
		return err
	}

	// Pull remote ID and public key from message
	remotePubkey, err := ci.PublicKeyFromProto(remoteMsg.Pubkey)
	if err != nil {
		return err
	}

	remoteID, err := peer.IDFromBytes(remoteMsg.Id)
	if err != nil {
		return err
	}

	// Validate that ID matches public key
	if !remoteID.MatchesPublicKey(remotePubkey) {
		calculatedID, _ := peer.IDFromPublicKey(remotePubkey)
		return fmt.Errorf("remote peer id does not match public key. id=%s calculated_id=%s",
			remoteID, calculatedID)
	}

	// Add remote ID and key to conn state
	ic.remotePubKey = remotePubkey
	ic.remote = remoteID
	return nil
}

// read and write a message at the same time.
func readWriteMsg(rw io.ReadWriter, out *pb.Exchange) (*pb.Exchange, error) {
	const maxMessageSize = 1 << 16

	outBytes, err := out.Marshal()
	if err != nil {
		return nil, err
	}
	wresult := make(chan error)
	go func() {
		w := msgio.NewVarintWriter(rw)
		wresult <- w.WriteMsg(outBytes)
	}()

	r := msgio.NewVarintReaderSize(rw, maxMessageSize)
	msg, err1 := r.ReadMsg()

	// Always wait for the read to finish.
	err2 := <-wresult

	if err1 != nil {
		return nil, err1
	}
	if err2 != nil {
		r.ReleaseMsg(msg)
		return nil, err2
	}
	inMsg := new(pb.Exchange)
	err = inMsg.Unmarshal(msg)
	return inMsg, err
}

// LocalPeer returns the local peer ID.
func (ic *Conn) LocalPeer() peer.ID {
	return ic.local
}

// RemotePeer returns the remote peer ID if we initiated the dial. Otherwise, it
// returns "" (because this connection isn't actually secure).
func (ic *Conn) RemotePeer() peer.ID {
	return ic.remote
}

// RemotePublicKey returns whatever public key was given by the remote peer.
// Note that no verification of ownership is done, as this connection is not secure.
func (ic *Conn) RemotePublicKey() ci.PubKey {
	return ic.remotePubKey
}

// LocalPrivateKey returns the private key for the local peer.
func (ic *Conn) LocalPrivateKey() ci.PrivKey {
	return ic.localPrivKey
}

var _ sec.SecureTransport = (*Transport)(nil)
var _ sec.SecureConn = (*Conn)(nil)
<|endoftext|>


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

package signatures

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"crypto/x509"
	"encoding/asn1"
	"encoding/hex"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"log"
	"math/big"

	"github.com/benlaurie/objecthash/go/objecthash"

	"github.com/google/key-transparency/core/proto/ctmap"
)

var (
	// ErrWrongKeyType occurs when a key is not an ECDSA key.
	ErrWrongKeyType = errors.New("not an ECDSA key")
	// ErrPointNotOnCurve occurs when a public key is not on the curve.
	ErrPointNotOnCurve = errors.New("point is not on the P256 curve")
	// ErrWrongHashAlgo occurs when a hash algorithm other than SHA256 was specified.
	ErrWrongHashAlgo = errors.New("not the SHA256 hash algorithm")
	// ErrWrongSignatureAlgo occurs when a signature algorithm other than ECDSA was specified.
	ErrWrongSignatureAlgo = errors.New("not the ECDSA signature algorithm")
	// ErrExtraDataAfterSig occurs when extra data was found after the signature.
	ErrExtraDataAfterSig = errors.New("extra data found after signature")
	// ErrVerificaionFailed occurs when the signature verification failed.
	ErrVerificaionFailed = errors.New("failed to verify ECDSA signature")
)

// Signer generates signatures with a single key.
type Signer struct {
	privKey crypto.PrivateKey
	KeyName string
	rand    io.Reader
}

// GenerateKeyPair creates a new random keypair and returns the wrapped signer and verifier.
func GenerateKeyPair() (*Signer, *Verifier, error) {
	pubkeyCurve := elliptic.P256()
	privatekey := new(ecdsa.PrivateKey)
	privatekey, err := ecdsa.GenerateKey(pubkeyCurve, rand.Reader)
	if err != nil {
		return nil, nil, err
	}
	sig, err := NewSigner(rand.Reader, privatekey)
	if err != nil {
		return nil, nil, err
	}
	ver, err := NewVerifier(privatekey.Public())
	if err != nil {
		return nil, nil, err
	}
	return sig, ver, nil
}

// PrivateKeyFromPEM parses a PEM formatted block and returns the private key
// contained within and any remaining unread bytes, or an error.
func PrivateKeyFromPEM(b []byte) (crypto.Signer, []byte, error) {
	p, rest := pem.Decode(b)
	if p == nil {
		return nil, rest, fmt.Errorf("no PEM block found in %s", string(b))
	}
	k, err := x509.ParseECPrivateKey(p.Bytes)
	if err != nil {
		return nil, nil, err
	}
	return k, rest, nil
}

// KeyName is the first 8 hex digits of the SHA256 of the public pem.
func KeyName(k crypto.PublicKey) (string, error) {
	pubBytes, err := x509.MarshalPKIXPublicKey(k)
	if err != nil {
		return "", err
	}
	id := sha256.Sum256(pubBytes)
	return hex.EncodeToString(id[:])[:8], nil
}

// NewSigner creates a signer object from a private key.
func NewSigner(rand io.Reader, pk crypto.Signer) (*Signer, error) {
	switch pkType := pk.(type) {
	case *ecdsa.PrivateKey:
		params := *(pkType.Params())
		if params != *elliptic.P256().Params() {
			return nil, ErrPointNotOnCurve
		}
		if !elliptic.P256().IsOnCurve(pkType.X, pkType.Y) {
			return nil, ErrPointNotOnCurve
		}
	default:
		return nil, ErrWrongKeyType
	}

	id, err := KeyName(pk.Public())
	if err != nil {
		return nil, err
	}

	return &Signer{
		privKey: pk,
		KeyName: id,
		rand:    rand,
	}, nil
}

// Sign generates a digital signature object.
func (s Signer) Sign(data interface{}) (*ctmap.DigitallySigned, error) {
	j, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	hash := objecthash.CommonJSONHash(string(j))

	ecdsaKey, ok := s.privKey.(*ecdsa.PrivateKey)
	if !ok {
		return nil, ErrWrongKeyType
	}
	var ecSig struct {
		R, S *big.Int
	}
	ecSig.R, ecSig.S, err = ecdsa.Sign(s.rand, ecdsaKey, hash[:])
	if err != nil {
		return nil, err
	}
	sig, err := asn1.Marshal(ecSig)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal ECDSA signature: %v", err)
	}
	return &ctmap.DigitallySigned{
		HashAlgorithm: ctmap.DigitallySigned_SHA256,
		SigAlgorithm:  ctmap.DigitallySigned_ECDSA,
		Signature:     sig,
	}, nil
}

// Verifier can verify signatures on SCTs and STHs
type Verifier struct {
	pubKey  crypto.PublicKey
	KeyName string
}

// PublicKeyFromPEM parses a PEM formatted block and returns the public key
// contained within and any remaining unread bytes, or an error.
func PublicKeyFromPEM(b []byte) (crypto.PublicKey, []byte, error) {
	p, rest := pem.Decode(b)
	if p == nil {
		return nil, rest, fmt.Errorf("no PEM block found in %s", string(b))
	}
	k, err := x509.ParsePKIXPublicKey(p.Bytes)
	if err != nil {
		return nil, nil, err
	}
	return k, rest, nil
}

// NewVerifier creates a verifier from a ECDSA public key.
func NewVerifier(pk crypto.PublicKey) (*Verifier, error) {
	switch pkType := pk.(type) {
	case *ecdsa.PublicKey:
		params := *(pkType.Params())
		if params != *elliptic.P256().Params() {
			return nil, ErrPointNotOnCurve
		}
		if !elliptic.P256().IsOnCurve(pkType.X, pkType.Y) {
			return nil, ErrPointNotOnCurve
		}
	default:
		return nil, ErrWrongKeyType
	}

	id, err := KeyName(pk)
	if err != nil {
		return nil, err
	}

	return &Verifier{
		pubKey:  pk,
		KeyName: id,
	}, nil
}

// Verify checks the digital signature associated applied to data.
func (s Verifier) Verify(data interface{}, sig *ctmap.DigitallySigned) error {
	if sig.HashAlgorithm != ctmap.DigitallySigned_SHA256 {
		return ErrWrongHashAlgo
	}
	if sig.SigAlgorithm != ctmap.DigitallySigned_ECDSA {
		return ErrWrongSignatureAlgo
	}

	j, err := json.Marshal(data)
	if err != nil {
		return err
	}
	hash := objecthash.CommonJSONHash(string(j))

	ecdsaKey, ok := s.pubKey.(*ecdsa.PublicKey)
	if !ok {
		return ErrWrongKeyType
	}
	var ecdsaSig struct {
		R, S *big.Int
	}
	rest, err := asn1.Unmarshal(sig.Signature, &ecdsaSig)
	if err != nil {
		return fmt.Errorf("failed to unmarshal ECDSA signature: %v", err)
	}
	if len(rest) != 0 {
		return ErrExtraDataAfterSig
	}

	if !ecdsa.Verify(ecdsaKey, hash[:], ecdsaSig.R, ecdsaSig.S) {
		return ErrVerificaionFailed
	}
	return nil
}
<commit_msg>Fix master<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package signatures

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"crypto/x509"
	"encoding/asn1"
	"encoding/hex"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"math/big"

	"github.com/benlaurie/objecthash/go/objecthash"

	"github.com/google/key-transparency/core/proto/ctmap"
)

var (
	// ErrWrongKeyType occurs when a key is not an ECDSA key.
	ErrWrongKeyType = errors.New("not an ECDSA key")
	// ErrPointNotOnCurve occurs when a public key is not on the curve.
	ErrPointNotOnCurve = errors.New("point is not on the P256 curve")
	// ErrWrongHashAlgo occurs when a hash algorithm other than SHA256 was specified.
	ErrWrongHashAlgo = errors.New("not the SHA256 hash algorithm")
	// ErrWrongSignatureAlgo occurs when a signature algorithm other than ECDSA was specified.
	ErrWrongSignatureAlgo = errors.New("not the ECDSA signature algorithm")
	// ErrExtraDataAfterSig occurs when extra data was found after the signature.
	ErrExtraDataAfterSig = errors.New("extra data found after signature")
	// ErrVerificaionFailed occurs when the signature verification failed.
	ErrVerificaionFailed = errors.New("failed to verify ECDSA signature")
)

// Signer generates signatures with a single key.
type Signer struct {
	privKey crypto.PrivateKey
	KeyName string
	rand    io.Reader
}

// GenerateKeyPair creates a new random keypair and returns the wrapped signer and verifier.
func GenerateKeyPair() (*Signer, *Verifier, error) {
	pubkeyCurve := elliptic.P256()
	privatekey := new(ecdsa.PrivateKey)
	privatekey, err := ecdsa.GenerateKey(pubkeyCurve, rand.Reader)
	if err != nil {
		return nil, nil, err
	}
	sig, err := NewSigner(rand.Reader, privatekey)
	if err != nil {
		return nil, nil, err
	}
	ver, err := NewVerifier(privatekey.Public())
	if err != nil {
		return nil, nil, err
	}
	return sig, ver, nil
}

// PrivateKeyFromPEM parses a PEM formatted block and returns the private key
// contained within and any remaining unread bytes, or an error.
func PrivateKeyFromPEM(b []byte) (crypto.Signer, []byte, error) {
	p, rest := pem.Decode(b)
	if p == nil {
		return nil, rest, fmt.Errorf("no PEM block found in %s", string(b))
	}
	k, err := x509.ParseECPrivateKey(p.Bytes)
	if err != nil {
		return nil, nil, err
	}
	return k, rest, nil
}

// KeyName is the first 8 hex digits of the SHA256 of the public pem.
func KeyName(k crypto.PublicKey) (string, error) {
	pubBytes, err := x509.MarshalPKIXPublicKey(k)
	if err != nil {
		return "", err
	}
	id := sha256.Sum256(pubBytes)
	return hex.EncodeToString(id[:])[:8], nil
}

// NewSigner creates a signer object from a private key.
func NewSigner(rand io.Reader, pk crypto.Signer) (*Signer, error) {
	switch pkType := pk.(type) {
	case *ecdsa.PrivateKey:
		params := *(pkType.Params())
		if params != *elliptic.P256().Params() {
			return nil, ErrPointNotOnCurve
		}
		if !elliptic.P256().IsOnCurve(pkType.X, pkType.Y) {
			return nil, ErrPointNotOnCurve
		}
	default:
		return nil, ErrWrongKeyType
	}

	id, err := KeyName(pk.Public())
	if err != nil {
		return nil, err
	}

	return &Signer{
		privKey: pk,
		KeyName: id,
		rand:    rand,
	}, nil
}

// Sign generates a digital signature object.
func (s Signer) Sign(data interface{}) (*ctmap.DigitallySigned, error) {
	j, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	hash := objecthash.CommonJSONHash(string(j))

	ecdsaKey, ok := s.privKey.(*ecdsa.PrivateKey)
	if !ok {
		return nil, ErrWrongKeyType
	}
	var ecSig struct {
		R, S *big.Int
	}
	ecSig.R, ecSig.S, err = ecdsa.Sign(s.rand, ecdsaKey, hash[:])
	if err != nil {
		return nil, err
	}
	sig, err := asn1.Marshal(ecSig)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal ECDSA signature: %v", err)
	}
	return &ctmap.DigitallySigned{
		HashAlgorithm: ctmap.DigitallySigned_SHA256,
		SigAlgorithm:  ctmap.DigitallySigned_ECDSA,
		Signature:     sig,
	}, nil
}

// Verifier can verify signatures on SCTs and STHs
type Verifier struct {
	pubKey  crypto.PublicKey
	KeyName string
}

// PublicKeyFromPEM parses a PEM formatted block and returns the public key
// contained within and any remaining unread bytes, or an error.
func PublicKeyFromPEM(b []byte) (crypto.PublicKey, []byte, error) {
	p, rest := pem.Decode(b)
	if p == nil {
		return nil, rest, fmt.Errorf("no PEM block found in %s", string(b))
	}
	k, err := x509.ParsePKIXPublicKey(p.Bytes)
	if err != nil {
		return nil, nil, err
	}
	return k, rest, nil
}

// NewVerifier creates a verifier from a ECDSA public key.
func NewVerifier(pk crypto.PublicKey) (*Verifier, error) {
	switch pkType := pk.(type) {
	case *ecdsa.PublicKey:
		params := *(pkType.Params())
		if params != *elliptic.P256().Params() {
			return nil, ErrPointNotOnCurve
		}
		if !elliptic.P256().IsOnCurve(pkType.X, pkType.Y) {
			return nil, ErrPointNotOnCurve
		}
	default:
		return nil, ErrWrongKeyType
	}

	id, err := KeyName(pk)
	if err != nil {
		return nil, err
	}

	return &Verifier{
		pubKey:  pk,
		KeyName: id,
	}, nil
}

// Verify checks the digital signature associated applied to data.
func (s Verifier) Verify(data interface{}, sig *ctmap.DigitallySigned) error {
	if sig.HashAlgorithm != ctmap.DigitallySigned_SHA256 {
		return ErrWrongHashAlgo
	}
	if sig.SigAlgorithm != ctmap.DigitallySigned_ECDSA {
		return ErrWrongSignatureAlgo
	}

	j, err := json.Marshal(data)
	if err != nil {
		return err
	}
	hash := objecthash.CommonJSONHash(string(j))

	ecdsaKey, ok := s.pubKey.(*ecdsa.PublicKey)
	if !ok {
		return ErrWrongKeyType
	}
	var ecdsaSig struct {
		R, S *big.Int
	}
	rest, err := asn1.Unmarshal(sig.Signature, &ecdsaSig)
	if err != nil {
		return fmt.Errorf("failed to unmarshal ECDSA signature: %v", err)
	}
	if len(rest) != 0 {
		return ErrExtraDataAfterSig
	}

	if !ecdsa.Verify(ecdsaKey, hash[:], ecdsaSig.R, ecdsaSig.S) {
		return ErrVerificaionFailed
	}
	return nil
}
<|endoftext|>


<commit_before>package routers

import (
	"github.com/gorilla/mux"
	"github.com/unrolled/secure"
)

var secureMiddleware = secure.New(secure.Options{
	// SSLRedirect:        true,
	FrameDeny:          true,
	ContentTypeNosniff: true,
	BrowserXssFilter:   true,
	IsDevelopment:      false,
})

func InitRoutes() *mux.Router {
	router := mux.NewRouter()
	SetTeamRoutes(router)
	SetGameRoutes(router)
	SetPlayerRoutes(router)
	SetShotRoutes(router)

	return router
}
<commit_msg>Set development true temporarily to allow cors<commit_after>package routers

import (
	"github.com/gorilla/mux"
	"github.com/unrolled/secure"
)

var secureMiddleware = secure.New(secure.Options{
	// SSLRedirect:        true,
	FrameDeny:          true,
	ContentTypeNosniff: true,
	BrowserXssFilter:   true,
	IsDevelopment:      true,
})

func InitRoutes() *mux.Router {
	router := mux.NewRouter()
	SetTeamRoutes(router)
	SetGameRoutes(router)
	SetPlayerRoutes(router)
	SetShotRoutes(router)

	return router
}
<|endoftext|>


<commit_before>package coco

import (
	"crypto/cipher"
	"fmt"
	"sync"

	"github.com/dedis/crypto/abstract"
)

// Peer is an abstract peer which we can Put to and Get from.
type Peer interface {
	Put(data interface{})
	Get() interface{}
}

// A TreeNode has the ability to send messages up the tree (parents),
// get from the parents, send messages to the children, and get messages from
// the children.
type TreeNode interface {
	PutUp(interface{})
	GetUp() interface{}
	PutDown(interface{})
	GetDown() []interface{}
}

// host embodies the local state of a single host in the network
// it satisfies the tree node interface.
type Host struct {
	hostname string
	prikey   abstract.Secret
	pubkey   abstract.Point
	parent   Peer
	children []Peer
}

func NewHost(suite abstract.Suite, rand cipher.Stream, hostname string) *Host {
	h := &Host{hostname: hostname,
		prikey:   suite.Secret().Pick(rand),
		children: make([]Peer, 0)}
	h.pubkey = suite.Point().Mul(nil, h.prikey)
	return h
}

func (h Host) AddParent(p Peer) {
	h.parent = p
}

// TODO: only add new children
func (h Host) AddChildren(ps ...Peer) {
	h.children = append(h.children, ps...)
}

func (h Host) PutUp(data interface{}) {
	h.parent.Put(data)
}
func (h Host) GetUp() interface{} {
	return h.parent.Get()
}

func (h Host) PutDown(data interface{}) {
	for _, c := range h.children {
		c.Put(data)
	}
}
func (h Host) GetDown() []interface{} {
	data := make([]interface{}, len(h.children))
	var wg sync.WaitGroup
	for i, c := range h.children {
		wg.Add(1)
		go func(i int) {
			data[i] = c.Get()
		}(i)
	}
	wg.Wait()
	return data
}

// goRouter is a testing structure for the GoPeer. It allows us to simulate
// tcp network connections locally (and is easily adaptable for network
// connections).
type goRouter struct {
	sync.Mutex
	channel map[string]chan interface{}
}

func newGoRouter() *goRouter {
	return &goRouter{channel: make(map[string]chan interface{})}
}

type GoPeer struct {
	router   *goRouter
	pubkey   abstract.Point
	hostname string
}

func NewGoPeer(pubkey abstract.Point, hostname string) *GoPeer {
	gp := &GoPeer{newGoRouter(), pubkey, hostname}
	gp.router.Lock()

	defer gp.router.Unlock()
	if _, ok := gp.router.channel[hostname]; ok {
		// return the already existant peer
		fmt.Println("Peer Already Exists")
		return nil
	}
	gp.router.channel[hostname] = make(chan interface{})
	return gp
}

func (p GoPeer) Put(data interface{}) {
	p.router.Lock()
	defer p.router.Unlock()
	if _, ok := p.router.channel[p.hostname]; !ok {
		p.router.channel[p.hostname] = make(chan interface{})
	}
	ch := p.router.channel[p.hostname]
	ch <- data
}

func (p GoPeer) Get() interface{} {
	p.router.Lock()
	defer p.router.Unlock()
	if _, ok := p.router.channel[p.hostname]; !ok {
		p.router.channel[p.hostname] = make(chan interface{})
	}
	ch := p.router.channel[p.hostname]
	data := <-ch
	return data
}
<commit_msg>adding support for goroutine pseudo-network<commit_after>package coco

import (
	"errors"
	"fmt"
	"sync"
)

// Peer is an abstract peer which we can Put to and Get from.
type Peer interface {
	Name() string // to keep track of peers in hosts
	Put(data interface{})
	Get() interface{}
}

// A TreeNode has the ability to send messages up the tree (parents),
// get from the parents, send messages to the children, and get messages from
// the children.
type TreeNode interface {
	PutUp(interface{})
	GetUp() interface{}
	PutDown(interface{})
	GetDown() []interface{}
}

// host embodies the local state of a single host in the network
// it satisfies the tree node interface.
type Host struct {
	hostname string
	parent   Peer
	children map[string]Peer // map so we only add unique peers
}

func NewHost(hostname string) *Host {
	h := &Host{hostname: hostname,
		children: make(map[string]Peer)}
	return h
}

func (h Host) AddParent(p Peer) {
	h.parent = p
}

// TODO: only add new children
func (h Host) AddChildren(ps ...Peer) {
	for _, p := range ps {
		h.children[p.Name()] = p
	}
}

func (h Host) PutUp(data interface{}) {
	h.parent.Put(data)
}
func (h Host) GetUp() interface{} {
	return h.parent.Get()
}

func (h Host) PutDown(data interface{}) {
	for _, c := range h.children {
		c.Put(data)
	}
}
func (h Host) GetDown() []interface{} {
	var mu sync.Mutex
	data := make([]interface{}, len(h.children))
	var wg sync.WaitGroup
	for _, c := range h.children {
		wg.Add(1)
		go func() {
			d := c.Get()
			mu.Lock()
			data = append(data, d)
			mu.Unlock()
		}()
	}
	wg.Wait()
	return data
}

// directory is a testing structure for the GoPeer. It allows us to simulate
// tcp network connections locally (and is easily adaptable for network
// connections).
type directory struct {
	sync.Mutex
	channel map[string]chan interface{}
}

func newDirectory() *directory {
	return &directory{channel: make(map[string]chan interface{})}
}

type goPeer struct {
	dir      *directory
	hostname string
}

var PeerExists error = errors.New("peer already exists in given directory")

func NewGoPeer(dir *directory, hostname string) (*goPeer, error) {
	gp := &goPeer{dir, hostname}
	gp.dir.Lock()

	defer gp.dir.Unlock()
	if _, ok := gp.dir.channel[hostname]; ok {
		// return the already existant peer
		fmt.Println("Peer Already Exists")
		return nil, PeerExists
	}
	gp.dir.channel[hostname] = make(chan interface{})
	return gp, nil
}

func (p goPeer) Put(data interface{}) {
	p.dir.Lock()
	if _, ok := p.dir.channel[p.hostname]; !ok {
		p.dir.channel[p.hostname] = make(chan interface{})
	}
	ch := p.dir.channel[p.hostname]
	p.dir.Unlock()
	ch <- data
}

func (p goPeer) Get() interface{} {
	p.dir.Lock()
	defer p.dir.Unlock()
	if _, ok := p.dir.channel[p.hostname]; !ok {
		p.dir.channel[p.hostname] = make(chan interface{})
	}
	ch := p.dir.channel[p.hostname]
	p.dir.Unlock()
	data := <-ch
	return data
}
<|endoftext|>


<commit_before>package gyazo

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

	"github.com/google/go-querystring/query"
)

const (
	version         = "0.1"
	defaultEndpoint = "https://api.gyazo.com"
	uploadEndpoint  = "https://upload.gyazo.com"
)

// Client manages communication with the Gyazo API
type Client struct {
	// Gyazo API access token.
	token string

	// client provides request to API endpoints.
	client *http.Client

	// DefaultEndpint is Gyazo API endpoint.
	DefaultEndpoint string

	// UploadEndpint is Gyazo upload API endpoint.
	UploadEndpoint string
}

// Image represents a uploaded image.
//
// Gyazo API docs: https://gyazo.com/api/docs/image
type Image struct {
	ID           string `json:"image_id"`
	PermalinkURL string `json:"permalink_url"`
	ThumbURL     string `json:"thumb_url"`
	URL          string `json:"url"`
	Type         string `json:"type"`
}

// ListOptions  specifies the optional parameters to the List
type ListOptions struct {
	Page    int `url:"page,omitempty"`
	PerPage int `url:"per_page,omitempty"`
}

// NewClient returns a new Gyazo API client.
func NewClient(token string) (*Client, error) {
	if token == "" {
		return nil, errors.New("access token is empty")
	}

	c := &Client{token, http.DefaultClient, defaultEndpoint, uploadEndpoint}
	return c, nil
}

// List returns user images
func (c *Client) List(opts *ListOptions) (*[]Image, error) {
	url := c.DefaultEndpoint + "/api/images"
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.token))

	// Build and set query parameters
	if opts != nil {
		params, err := query.Values(opts)
		if err != nil {
			return nil, err
		}
		req.URL.RawQuery = params.Encode()
	}

	res, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		return nil, errors.New(res.Status)
	}

	list := new([]Image)
	if err = json.NewDecoder(res.Body).Decode(&list); err != nil {
		return nil, err
	}
	return list, nil
}
<commit_msg>Format comment<commit_after>package gyazo

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

	"github.com/google/go-querystring/query"
)

const (
	version         = "0.1"
	defaultEndpoint = "https://api.gyazo.com"
	uploadEndpoint  = "https://upload.gyazo.com"
)

// Client manages communication with the Gyazo API
type Client struct {
	// Gyazo API access token.
	token string

	// client provides request to API endpoints.
	client *http.Client

	// DefaultEndpint is Gyazo API endpoint.
	DefaultEndpoint string

	// UploadEndpint is Gyazo upload API endpoint.
	UploadEndpoint string
}

// Image represents a uploaded image.
//
// Gyazo API docs: https://gyazo.com/api/docs/image
type Image struct {
	ID           string `json:"image_id"`
	PermalinkURL string `json:"permalink_url"`
	ThumbURL     string `json:"thumb_url"`
	URL          string `json:"url"`
	Type         string `json:"type"`
}

// ListOptions specifies the optional parameters to the List.
type ListOptions struct {
	Page    int `url:"page,omitempty"`
	PerPage int `url:"per_page,omitempty"`
}

// NewClient returns a new Gyazo API client.
func NewClient(token string) (*Client, error) {
	if token == "" {
		return nil, errors.New("access token is empty")
	}

	c := &Client{token, http.DefaultClient, defaultEndpoint, uploadEndpoint}
	return c, nil
}

// List returns user images
func (c *Client) List(opts *ListOptions) (*[]Image, error) {
	url := c.DefaultEndpoint + "/api/images"
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.token))

	// Build and set query parameters
	if opts != nil {
		params, err := query.Values(opts)
		if err != nil {
			return nil, err
		}
		req.URL.RawQuery = params.Encode()
	}

	res, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		return nil, errors.New(res.Status)
	}

	list := new([]Image)
	if err = json.NewDecoder(res.Body).Decode(&list); err != nil {
		return nil, err
	}
	return list, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.

package command

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/vmware/photon-controller-cli/photon/client"
	"github.com/vmware/photon-controller-cli/photon/utils"
	"github.com/vmware/photon-controller-go-sdk/photon"
	"io"
	"os"
	"text/tabwriter"
)

const (
	ROUTED   = "ROUTED"
	ISOLATED = "ISOLATED"
)

func createVirtualNetwork(c *cli.Context, w io.Writer) error {
	err := checkArgNum(c.Args(), 0, "network create [<options>]")
	if err != nil {
		return err
	}

	name := c.String("name")
	description := c.String("description")
	routingType := c.String("routingType")
	size := c.Int("size")
	staticIpSize := c.Int("staticIpSize")
	projectId := c.String("projectId")

	if !utils.IsNonInteractive(c) {
		name, err = askForInput("Network name: ", name)
		if err != nil {
			return err
		}
		description, err = askForInput("Description of network: ", description)
		if err != nil {
			return err
		}
		routingType, err = askForInput("Routing type of network: ", routingType)
		if err != nil {
			return err
		}
		projectId, err = askForInput("Project ID that network belongs to: ", projectId)
		if err != nil {
			return err
		}
	}

	if len(name) == 0 {
		return fmt.Errorf("Please provide network name")
	}
	if routingType != ROUTED && routingType != ISOLATED {
		return fmt.Errorf("Please choose the correct routing type for network (ROUTED or ISOLATED)")
	}
	if size < 8 {
		return fmt.Errorf("Network size must be at least 8")
	}

	createSpec := &photon.VirtualSubnetCreateSpec{
		Name:                 name,
		Description:          description,
		RoutingType:          routingType,
		Size:                 size,
		ReservedStaticIpSize: staticIpSize,
	}

	client.Esxclient, err = client.GetClient(utils.IsNonInteractive(c))
	if err != nil {
		return err
	}

	task, err := client.Esxclient.VirtualSubnets.Create(projectId, createSpec)
	if err != nil {
		return err
	}

	id, err := waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	if utils.NeedsFormatting(c) {
		network, err := client.Esxclient.Subnets.Get(id)
		if err != nil {
			return err
		}
		utils.FormatObject(network, w, c)
	}

	return nil
}

func listVirtualNetworks(c *cli.Context, w io.Writer) error {
	err := checkArgNum(c.Args(), 0, "network list [<options>]")
	if err != nil {
		return err
	}
	client.Esxclient, err = client.GetClient(utils.IsNonInteractive(c))
	if err != nil {
		return err
	}

	name := c.String("name")
	options := &photon.VirtualSubnetGetOptions{
		Name: name,
	}

	projectId := c.String("projectId")
	if len(projectId) == 0 {
		return fmt.Errorf("Please provide project ID")
	}

	networks, err := client.Esxclient.VirtualSubnets.GetAll(projectId, options)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		for _, network := range networks.Items {
			fmt.Printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
				network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
				network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
		}
	} else if utils.NeedsFormatting(c) {
		utils.FormatObjects(networks.Items, w, c)
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 4, 4, 2, ' ', 0)
		fmt.Fprintf(w, "ID\tName\tState\tDescriptions\tRoutingType\tIsDefault\tCIDR\tLowDynamicIP\tHighDynamicIP"+
			"\tLowStaticIP\tHighStaticIP\tReservedIpList\n")
		for _, network := range networks.Items {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
				network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
				network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
		}
		err = w.Flush()
		if err != nil {
			return err
		}
		fmt.Printf("Total: %d\n", len(networks.Items))
	}

	return nil
}

func showVirtualNetwork(c *cli.Context, w io.Writer) error {
	err := checkArgNum(c.Args(), 1, "network show <id>")
	if err != nil {
		return err
	}
	id := c.Args().First()

	client.Esxclient, err = client.GetClient(utils.IsNonInteractive(c))
	if err != nil {
		return err
	}

	network, err := client.Esxclient.VirtualSubnets.Get(id)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		fmt.Printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
			network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
			network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
	} else if utils.NeedsFormatting(c) {
		utils.FormatObject(network, w, c)
	} else {
		fmt.Printf("Network ID: %s\n", network.ID)
		fmt.Printf("  Name:             %s\n", network.Name)
		fmt.Printf("  State:            %s\n", network.State)
		fmt.Printf("  Description:      %s\n", network.Description)
		fmt.Printf("  Routing Type:     %s\n", network.RoutingType)
		fmt.Printf("  Is Default:       %s\n", network.IsDefault)
		fmt.Printf("  CIDR:             %s\n", network.Cidr)
		fmt.Printf("  Start Dynamic IP: %s\n", network.LowIpDynamic)
		fmt.Printf("  End Dynamic IP:   %s\n", network.HighIpDynamic)
		fmt.Printf("  Start Static IP:  %s\n", network.LowIpStatic)
		fmt.Printf("  End Static IP:    %s\n", network.HighIpStatic)
		fmt.Printf("  Reserved IP List: %s\n", network.ReservedIpList)
	}

	return nil
}
<commit_msg>add mandatory parameters to interactive mode for create virtual network command<commit_after>// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.

package command

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/vmware/photon-controller-cli/photon/client"
	"github.com/vmware/photon-controller-cli/photon/utils"
	"github.com/vmware/photon-controller-go-sdk/photon"
	"io"
	"os"
	"strconv"
	"text/tabwriter"
)

const (
	ROUTED   = "ROUTED"
	ISOLATED = "ISOLATED"
)

func createVirtualNetwork(c *cli.Context, w io.Writer) error {
	err := checkArgNum(c.Args(), 0, "network create [<options>]")
	if err != nil {
		return err
	}

	name := c.String("name")
	description := c.String("description")
	routingType := c.String("routingType")
	size := c.Int("size")
	staticIpSize := c.Int("staticIpSize")
	projectId := c.String("projectId")

	if !utils.IsNonInteractive(c) {
		name, err = askForInput("Network name: ", name)
		if err != nil {
			return err
		}
		description, err = askForInput("Description of network: ", description)
		if err != nil {
			return err
		}
		routingType, err = askForInput("Routing type of network: ", routingType)
		if err != nil {
			return err
		}
		projectId, err = askForInput("Project ID that network belongs to: ", projectId)
		if err != nil {
			return err
		}
		sizeStr := ""
		sizeStr, err = askForInput("Size of IP pool of the network (must be power of 2, at least 8): ", sizeStr)
		if err != nil {
			return err
		}
		size, err = strconv.Atoi(sizeStr)
		if err != nil {
			return err
		}
		staticIpSizeStr := ""
		staticIpSizeStr, err = askForInput("Size of the static IP pool (must be less than size of IP pool): ",
			staticIpSizeStr)
		if err != nil {
			return err
		}
		staticIpSize, err = strconv.Atoi(staticIpSizeStr)
		if err != nil {
			return err
		}
	}

	if len(name) == 0 {
		return fmt.Errorf("Please provide network name")
	}
	if routingType != ROUTED && routingType != ISOLATED {
		return fmt.Errorf("Please choose the correct routing type for network (ROUTED or ISOLATED)")
	}
	if size < 8 {
		return fmt.Errorf("Network size must be at least 8")
	}

	createSpec := &photon.VirtualSubnetCreateSpec{
		Name:                 name,
		Description:          description,
		RoutingType:          routingType,
		Size:                 size,
		ReservedStaticIpSize: staticIpSize,
	}

	client.Esxclient, err = client.GetClient(utils.IsNonInteractive(c))
	if err != nil {
		return err
	}

	task, err := client.Esxclient.VirtualSubnets.Create(projectId, createSpec)
	if err != nil {
		return err
	}

	id, err := waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	if utils.NeedsFormatting(c) {
		network, err := client.Esxclient.Subnets.Get(id)
		if err != nil {
			return err
		}
		utils.FormatObject(network, w, c)
	}

	return nil
}

func listVirtualNetworks(c *cli.Context, w io.Writer) error {
	err := checkArgNum(c.Args(), 0, "network list [<options>]")
	if err != nil {
		return err
	}
	client.Esxclient, err = client.GetClient(utils.IsNonInteractive(c))
	if err != nil {
		return err
	}

	name := c.String("name")
	options := &photon.VirtualSubnetGetOptions{
		Name: name,
	}

	projectId := c.String("projectId")
	if len(projectId) == 0 {
		return fmt.Errorf("Please provide project ID")
	}

	networks, err := client.Esxclient.VirtualSubnets.GetAll(projectId, options)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		for _, network := range networks.Items {
			fmt.Printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
				network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
				network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
		}
	} else if utils.NeedsFormatting(c) {
		utils.FormatObjects(networks.Items, w, c)
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 4, 4, 2, ' ', 0)
		fmt.Fprintf(w, "ID\tName\tState\tDescriptions\tRoutingType\tIsDefault\tCIDR\tLowDynamicIP\tHighDynamicIP"+
			"\tLowStaticIP\tHighStaticIP\tReservedIpList\n")
		for _, network := range networks.Items {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
				network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
				network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
		}
		err = w.Flush()
		if err != nil {
			return err
		}
		fmt.Printf("Total: %d\n", len(networks.Items))
	}

	return nil
}

func showVirtualNetwork(c *cli.Context, w io.Writer) error {
	err := checkArgNum(c.Args(), 1, "network show <id>")
	if err != nil {
		return err
	}
	id := c.Args().First()

	client.Esxclient, err = client.GetClient(utils.IsNonInteractive(c))
	if err != nil {
		return err
	}

	network, err := client.Esxclient.VirtualSubnets.Get(id)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		fmt.Printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", network.ID, network.Name, network.State,
			network.Description, network.RoutingType, network.IsDefault, network.Cidr, network.LowIpDynamic,
			network.HighIpDynamic, network.LowIpStatic, network.HighIpStatic, network.ReservedIpList)
	} else if utils.NeedsFormatting(c) {
		utils.FormatObject(network, w, c)
	} else {
		fmt.Printf("Network ID: %s\n", network.ID)
		fmt.Printf("  Name:             %s\n", network.Name)
		fmt.Printf("  State:            %s\n", network.State)
		fmt.Printf("  Description:      %s\n", network.Description)
		fmt.Printf("  Routing Type:     %s\n", network.RoutingType)
		fmt.Printf("  Is Default:       %s\n", network.IsDefault)
		fmt.Printf("  CIDR:             %s\n", network.Cidr)
		fmt.Printf("  Start Dynamic IP: %s\n", network.LowIpDynamic)
		fmt.Printf("  End Dynamic IP:   %s\n", network.HighIpDynamic)
		fmt.Printf("  Start Static IP:  %s\n", network.LowIpStatic)
		fmt.Printf("  End Static IP:    %s\n", network.HighIpStatic)
		fmt.Printf("  Reserved IP List: %s\n", network.ReservedIpList)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    //"html"
    "log"
    "net/http"
	"io/ioutil"
	"strings"
)

var (
	head string = `<!DOCTYPE html>

<title>Page Title</title>

This is a Heading
`

	tail string =	`</body></html>`

	script string = ``
)

func main() {
	http.HandleFunc("/List", List)
	http.HandleFunc("/Watch/", Watch)
	http.Handle("/", http.FileServer(http.Dir("./Vids/")))
	log.Fatal(http.ListenAndServe(":8080", nil))

}

func List(w http.ResponseWriter, r *http.Request){
	fmt.Fprintf(w, head + getVideos() + tail)
}

func Watch(w http.ResponseWriter, r *http.Request){
	fileToWatch := r.URL.Path[len("/Watch"):]
	fmt.Printf("%q", r.URL.Path)
	fileText := ("<video width=\"400\" preload=\"none\" controls><source src=\""+fileToWatch+"\" type=\"video/mp4\">Your browser does not support HTML5 video.</video>")
	fmt.Fprintf(w, head + fileText + script + tail)
}

func getVideos() string{
	fileText:=""
	files, err := ioutil.ReadDir("./Vids")
	if(err != nil){
		fileText = `<p>No Files Found</p>`
	}
	for _, file := range files {
		if strings.Contains(strings.ToLower(file.Name()), "mp4"){
			fileText += (""+file.Name()+"\n
\n")
		}else{
			fileText += ("
" + file.Name() + "
\n
\n")
		}

	}
	return fileText;
}
<commit_msg>Changes to support vid queueing<commit_after>package main

import (
    "fmt"
    //"html"
    "log"
    "net/http"
	"io/ioutil"
	"strings"
)

var (
	head string = `<!DOCTYPE html>

<title>Page Title</title>

This is a Heading
`

	tail string =	`</body></html>`

	script string = ``
)

func main() {
	http.HandleFunc("/List", List)
	http.HandleFunc("/Watch/", Watch)
	http.Handle("/", http.FileServer(http.Dir("./Vids/")))
	log.Fatal(http.ListenAndServe(":8080", nil))

}

func List(w http.ResponseWriter, r *http.Request){
	fmt.Fprintf(w, head + getVideos() + tail)
}

func Watch(w http.ResponseWriter, r *http.Request){
	fileToWatch := r.URL.Path[len("/Watch"):]
	fmt.Printf("%q", r.URL.Path)
	fileText := ("<video width=\"400\" preload=\"none\" controls><source src=\""+fileToWatch+"\" type=\"video/mp4\">Your browser does not support HTML5 video.</video>")
	fmt.Fprintf(w, head + fileText + script + tail)
}

func getVideos() string{
	fileText:=""
	files, err := ioutil.ReadDir("./Vids")
	if(err != nil){
		fileText = `<p>No Files Found</p>`
	}
	for _, file := range files {
		if strings.Contains(strings.ToLower(file.Name()), "mp4"){
			fileText += (""+file.Name()+"\n
\n")
		}else{
			fileText += ("
" + file.Name() + "
\n
\n")
		}

	}
	return fileText;
}
<|endoftext|>