<commit_before>package scene

import (
	"image"

	"github.com/pankona/gomo-simra/simra"
	"github.com/pankona/phantomize/scene/config"
)

const (
	ctrlPanelHeight = 220
	fps             = 60
)

// game represents a scene object for game
type game struct {
	currentStage     int
	nextScene        simra.Driver
	field            simra.Sprite
	ctrlPanel        simra.Sprite
	ctrlButton       []simra.Sprite
	currentFrame     int64
	players          map[string]uniter
	uniters          map[string]uniter
	unitPopTimeTable unitPopTimeTable
	pubsub           *simra.PubSub
	gameState        gameState
	currentRunLoop   func()
	eventqueue       chan *command
	selection        *selection
}

type gameState int

const (
	gameStateInitial gameState = iota
	gameStateRunning
)

// Initialize initializes game scene
// This is called from simra.
// simra.GetInstance().SetDesiredScreenSize should be called to determine
// screen size of this scene.
func (g *game) Initialize() {
	simra.LogDebug("[IN]")

	simra.GetInstance().SetDesiredScreenSize(config.ScreenWidth, config.ScreenHeight)

	// initialize sprites
	g.initialize()

	simra.LogDebug("[OUT]")
}

func (g *game) updateGameState(newState gameState) {
	g.gameState = newState
	switch newState {
	case gameStateInitial:
		g.currentRunLoop = g.initialRunLoop
	case gameStateRunning:
		g.currentRunLoop = g.runningRunLoop
	default:
		//nop
	}

	// reset frame
	g.currentFrame = 0
}

type selection struct {
	selecting *simra.Sprite
	class     string
}

func (s *selection) OnEvent(i interface{}) {
	c, ok := i.(*command)
	if !ok {
		panic("unexpected command received. fatal.")
	}

	switch c.commandtype {
	case commandUpdateSelection:
		s.selecting = c.data.(*simra.Sprite)
		simra.LogDebug("selection updated: %v", s.selecting)
	case commandUnsetSelection:
		s.selecting = nil
		simra.LogDebug("selection updated: nil")
	default:
		// nop
	}
}

type ctrlButtonTouchListener struct {
	id   int
	game *game
}

func (c *ctrlButtonTouchListener) OnTouchBegin(x, y float32) {
	// nop
}

func (c *ctrlButtonTouchListener) OnTouchMove(x, y float32) {
	// nop
}

func (c *ctrlButtonTouchListener) OnTouchEnd(x, y float32) {
	c.game.eventqueue <- newCommand(commandUpdateSelection, &c.game.ctrlButton[c.id])
}

func (g *game) initCtrlPanel() {
	g.ctrlPanel.W = config.ScreenWidth
	g.ctrlPanel.H = ctrlPanelHeight
	g.ctrlPanel.X = config.ScreenWidth / 2
	g.ctrlPanel.Y = g.ctrlPanel.H / 2
	simra.GetInstance().AddSprite("ctrl_panel.png",
		image.Rect(0, 0, 1280, 240),
		&g.ctrlPanel)

	g.ctrlButton = make([]simra.Sprite, 9)
	for i := range g.ctrlButton {
		g.ctrlButton[i].W = 64
		g.ctrlButton[i].H = 64
		g.ctrlButton[i].X = (float32)(1010 + (64+45)*(i%3))
		g.ctrlButton[i].Y = (float32)(44 + (64+5)*(i/3))
		simra.GetInstance().AddSprite("player.png",
			image.Rect(0, 0, 384, 384),
			&g.ctrlButton[i])

		g.ctrlButton[i].AddTouchListener(&ctrlButtonTouchListener{id: i, game: g})
	}
}

func (g *game) initField() {
	g.field.W = config.ScreenWidth
	g.field.H = config.ScreenHeight
	g.field.X = config.ScreenWidth / 2
	g.field.Y = config.ScreenHeight / 2
	simra.GetInstance().AddSprite("field1.png",
		image.Rect(0, 0, 1280, 720),
		&g.field)
	g.field.AddTouchListener(&fieldTouchListener{game: g})
}

type fieldTouchListener struct {
	game *game
}

// OnTouchBegin is called when game scene is Touched.
func (f *fieldTouchListener) OnTouchBegin(x, y float32) {
	// nop
}

// OnTouchMove is called when game scene is Touched and moved.
func (f *fieldTouchListener) OnTouchMove(x, y float32) {
	// nop
}

// OnTouchEnd is called when game scene is Touched and it is released.
func (f *fieldTouchListener) OnTouchEnd(x, y float32) {
	if y <= ctrlPanelHeight {
		return
	}

	if f.game.selection.selecting == nil {
		return
	}

	s := f.game.selection.selecting
	var id string
	if s == &f.game.ctrlButton[6] {
		id = "player1"
	} else if s == &f.game.ctrlButton[7] {
		id = "player2"
	} else if s == &f.game.ctrlButton[8] {
		id = "player3"
	} else if s == &f.game.ctrlButton[3] {
		id = "player4"
	} else if s == &f.game.ctrlButton[4] {
		id = "player5"
	} else if s == &f.game.ctrlButton[5] {
		id = "player6"
	} else if s == &f.game.ctrlButton[0] {
		id = "player7"
	} else if s == &f.game.ctrlButton[1] {
		id = "player8"
	} else if s == &f.game.ctrlButton[2] {
		id = "player9"
	}

	if id != "" {
		simra.LogDebug("@@@@@@@ new unit summoning!!!")
		p := newUnit(id, "player", f.game)
		p.SetPosition(x, y)
		f.game.players[id] = p
		f.game.pubsub.Subscribe(p.GetID(), p)
		f.game.eventqueue <- newCommand(commandSpawn, p)
	}
}

func (g *game) initPlayer() {
	g.players = make(map[string]uniter)
	p := newUnit("player", "player", g)
	g.players["player"] = p
	g.pubsub.Subscribe(p.GetID(), p)
}

type unitPopTime struct {
	unitID          string
	popTime         int64
	initialPosition position
}

type unitPopTimeTable []*unitPopTime

func (g *game) initUnits(json string) {
	// TODO: load from json file
	units := make(map[string]uniter)
	units["unit1"] = newUnit("unit1", "", g)
	units["unit2"] = newUnit("unit2", "", g)
	units["unit3"] = newUnit("unit3", "", g)

	// TODO: unitpopTimeTable should be sorted by popTime
	g.unitPopTimeTable = append(g.unitPopTimeTable,
		&unitPopTime{
			unitID:          "unit1",
			popTime:         3 * fps,
			initialPosition: position{config.ScreenWidth - 32, config.ScreenHeight / 6 * 5},
		})
	g.unitPopTimeTable = append(g.unitPopTimeTable,
		&unitPopTime{
			unitID:          "unit2",
			popTime:         4 * fps,
			initialPosition: position{config.ScreenWidth - 32, config.ScreenHeight / 6 * 4},
		})
	g.unitPopTimeTable = append(g.unitPopTimeTable,
		&unitPopTime{
			unitID:          "unit3",
			popTime:         5 * fps,
			initialPosition: position{config.ScreenWidth - 32, config.ScreenHeight / 6 * 3},
		})

	g.uniters = units
}

func (g *game) popUnits() []uniter {
	poppedUnits := make([]uniter, 0)
	for _, v := range g.unitPopTimeTable {
		if v.popTime <= g.currentFrame {
			// pop unit
			u := g.uniters[v.unitID]
			u.SetPosition(v.initialPosition.x, v.initialPosition.y)
			poppedUnits = append(poppedUnits, u)
			continue
		}
	}

	if len(poppedUnits) != 0 {
		// remove popped units from unitPopTimeTable
		g.unitPopTimeTable = g.unitPopTimeTable[len(poppedUnits):]
	}

	return poppedUnits
}

func (g *game) initialize() {
	g.pubsub = simra.NewPubSub()
	g.eventqueue = make(chan *command, 256)
	g.initField()
	g.initCtrlPanel()
	g.initPlayer()
	g.initUnits("") // TODO: input JSON string
	g.selection = &selection{}
	simra.GetInstance().AddTouchListener(g)
	g.pubsub.Subscribe("god", g)
	g.pubsub.Subscribe("selection", g.selection)
	g.updateGameState(gameStateInitial)
}

func (g *game) eventFetch() []*command {
	// note:
	// if new events are pushed while fetching,
	// they should be fetched next run loop to
	// avoid inifinite event fetching.

	qlen := len(g.eventqueue)
	if qlen == 0 {
		return nil
	}

	c := make([]*command, qlen)
	for i := 0; i < qlen; i++ {
		c[i] = <-g.eventqueue
	}
	return c
}

func (g *game) OnEvent(i interface{}) {
	c, ok := i.(*command)
	if !ok {
		// should be a command. ignore.
		return
	}

	_, ok = c.data.(*game)
	if !ok {
		// this is not for me
		return
	}

	switch c.commandtype {
	case commandGoToInitialState:
		g.updateGameState(gameStateInitial)
	case commandGoToRunningState:
		g.updateGameState(gameStateRunning)
	}
}

func (g *game) initialRunLoop() {
	commands := g.eventFetch()
	for _, v := range commands {
		g.pubsub.Publish(v)
	}
	g.players["player"].DoAction()
}

func (g *game) runningRunLoop() {
	poppedUnits := g.popUnits()
	for _, v := range poppedUnits {
		err := g.pubsub.Subscribe(v.GetID(), v)
		if err != nil {
			panic("failed to subscribe. fatal.")
		}

		// generate spawn command
		g.eventqueue <- newCommand(commandSpawn, v)
	}

	// event fetch and publish to all subscribers
	commands := g.eventFetch()
	for _, v := range commands {
		g.pubsub.Publish(v)
	}

	// invoke action for all units
	for _, v := range g.players {
		v.DoAction()
	}
	for _, v := range g.uniters {
		v.DoAction()
	}
}

// Drive is called from simra.
// This is used to update sprites position.
// This will be called 60 times per sec.
func (g *game) Drive() {
	defer func() {
		g.currentFrame++
	}()

	if g.nextScene != nil {
		simra.GetInstance().SetScene(g.nextScene)
	}
	g.currentRunLoop()
}

// OnTouchBegin is called when game scene is Touched.
func (g *game) OnTouchBegin(x, y float32) {
	// nop
}

// OnTouchMove is called when game scene is Touched and moved.
func (g *game) OnTouchMove(x, y float32) {
	// nop
}

// OnTouchEnd is called when game scene is Touched and it is released.
func (g *game) OnTouchEnd(x, y float32) {
	if g.gameState == gameStateInitial {
		if y > ctrlPanelHeight {
			g.players["player"].SetPosition(x, y)
			g.eventqueue <- newCommand(commandSpawn, g.players["player"])
			g.eventqueue <- newCommand(commandGoToRunningState, g)
		}
	}
	//g.nextScene = &result{currentStage: g.currentStage}
}
<commit_msg>add TODO comment<commit_after>package scene

import (
	"image"

	"github.com/pankona/gomo-simra/simra"
	"github.com/pankona/phantomize/scene/config"
)

const (
	ctrlPanelHeight = 220
	fps             = 60
)

// game represents a scene object for game
type game struct {
	currentStage     int
	nextScene        simra.Driver
	field            simra.Sprite
	ctrlPanel        simra.Sprite
	ctrlButton       []simra.Sprite
	currentFrame     int64
	players          map[string]uniter
	uniters          map[string]uniter
	unitPopTimeTable unitPopTimeTable
	pubsub           *simra.PubSub
	gameState        gameState
	currentRunLoop   func()
	eventqueue       chan *command
	selection        *selection
}

type gameState int

const (
	gameStateInitial gameState = iota
	gameStateRunning
)

// Initialize initializes game scene
// This is called from simra.
// simra.GetInstance().SetDesiredScreenSize should be called to determine
// screen size of this scene.
func (g *game) Initialize() {
	simra.LogDebug("[IN]")

	simra.GetInstance().SetDesiredScreenSize(config.ScreenWidth, config.ScreenHeight)

	// initialize sprites
	g.initialize()

	simra.LogDebug("[OUT]")
}

func (g *game) updateGameState(newState gameState) {
	g.gameState = newState
	switch newState {
	case gameStateInitial:
		g.currentRunLoop = g.initialRunLoop
	case gameStateRunning:
		g.currentRunLoop = g.runningRunLoop
	default:
		//nop
	}

	// reset frame
	g.currentFrame = 0
}

type selection struct {
	selecting *simra.Sprite
	class     string
}

func (s *selection) OnEvent(i interface{}) {
	c, ok := i.(*command)
	if !ok {
		panic("unexpected command received. fatal.")
	}

	switch c.commandtype {
	case commandUpdateSelection:
		s.selecting = c.data.(*simra.Sprite)
		simra.LogDebug("selection updated: %v", s.selecting)
	case commandUnsetSelection:
		s.selecting = nil
		simra.LogDebug("selection updated: nil")
	default:
		// nop
	}
}

type ctrlButtonTouchListener struct {
	id   int
	game *game
}

func (c *ctrlButtonTouchListener) OnTouchBegin(x, y float32) {
	// nop
}

func (c *ctrlButtonTouchListener) OnTouchMove(x, y float32) {
	// nop
}

func (c *ctrlButtonTouchListener) OnTouchEnd(x, y float32) {
	c.game.eventqueue <- newCommand(commandUpdateSelection, &c.game.ctrlButton[c.id])
}

func (g *game) initCtrlPanel() {
	g.ctrlPanel.W = config.ScreenWidth
	g.ctrlPanel.H = ctrlPanelHeight
	g.ctrlPanel.X = config.ScreenWidth / 2
	g.ctrlPanel.Y = g.ctrlPanel.H / 2
	simra.GetInstance().AddSprite("ctrl_panel.png",
		image.Rect(0, 0, 1280, 240),
		&g.ctrlPanel)

	g.ctrlButton = make([]simra.Sprite, 9)
	for i := range g.ctrlButton {
		g.ctrlButton[i].W = 64
		g.ctrlButton[i].H = 64
		g.ctrlButton[i].X = (float32)(1010 + (64+45)*(i%3))
		g.ctrlButton[i].Y = (float32)(44 + (64+5)*(i/3))
		simra.GetInstance().AddSprite("player.png",
			image.Rect(0, 0, 384, 384),
			&g.ctrlButton[i])

		g.ctrlButton[i].AddTouchListener(&ctrlButtonTouchListener{id: i, game: g})
	}
}

func (g *game) initField() {
	g.field.W = config.ScreenWidth
	g.field.H = config.ScreenHeight
	g.field.X = config.ScreenWidth / 2
	g.field.Y = config.ScreenHeight / 2
	simra.GetInstance().AddSprite("field1.png",
		image.Rect(0, 0, 1280, 720),
		&g.field)
	g.field.AddTouchListener(&fieldTouchListener{game: g})
}

type fieldTouchListener struct {
	game *game
}

// OnTouchBegin is called when game scene is Touched.
func (f *fieldTouchListener) OnTouchBegin(x, y float32) {
	// nop
}

// OnTouchMove is called when game scene is Touched and moved.
func (f *fieldTouchListener) OnTouchMove(x, y float32) {
	// nop
}

// OnTouchEnd is called when game scene is Touched and it is released.
func (f *fieldTouchListener) OnTouchEnd(x, y float32) {
	if y <= ctrlPanelHeight {
		return
	}

	if f.game.selection.selecting == nil {
		return
	}

	s := f.game.selection.selecting
	var id string
	if s == &f.game.ctrlButton[6] {
		id = "player1"
	} else if s == &f.game.ctrlButton[7] {
		id = "player2"
	} else if s == &f.game.ctrlButton[8] {
		id = "player3"
	} else if s == &f.game.ctrlButton[3] {
		id = "player4"
	} else if s == &f.game.ctrlButton[4] {
		id = "player5"
	} else if s == &f.game.ctrlButton[5] {
		id = "player6"
	} else if s == &f.game.ctrlButton[0] {
		id = "player7"
	} else if s == &f.game.ctrlButton[1] {
		id = "player8"
	} else if s == &f.game.ctrlButton[2] {
		id = "player9"
	}

	if id != "" {
		// TODO: every alley spawn occurs file I/O. lol
		// loading texture in advance is needed.
		p := newUnit(id, "player", f.game)
		p.SetPosition(x, y)
		f.game.players[id] = p
		f.game.pubsub.Subscribe(p.GetID(), p)
		f.game.eventqueue <- newCommand(commandSpawn, p)
	}
}

func (g *game) initPlayer() {
	g.players = make(map[string]uniter)
	p := newUnit("player", "player", g)
	g.players["player"] = p
	g.pubsub.Subscribe(p.GetID(), p)
}

type unitPopTime struct {
	unitID          string
	popTime         int64
	initialPosition position
}

type unitPopTimeTable []*unitPopTime

func (g *game) initUnits(json string) {
	// TODO: load from json file
	units := make(map[string]uniter)
	units["unit1"] = newUnit("unit1", "", g)
	units["unit2"] = newUnit("unit2", "", g)
	units["unit3"] = newUnit("unit3", "", g)

	// TODO: unitpopTimeTable should be sorted by popTime
	g.unitPopTimeTable = append(g.unitPopTimeTable,
		&unitPopTime{
			unitID:          "unit1",
			popTime:         3 * fps,
			initialPosition: position{config.ScreenWidth - 32, config.ScreenHeight / 6 * 5},
		})
	g.unitPopTimeTable = append(g.unitPopTimeTable,
		&unitPopTime{
			unitID:          "unit2",
			popTime:         4 * fps,
			initialPosition: position{config.ScreenWidth - 32, config.ScreenHeight / 6 * 4},
		})
	g.unitPopTimeTable = append(g.unitPopTimeTable,
		&unitPopTime{
			unitID:          "unit3",
			popTime:         5 * fps,
			initialPosition: position{config.ScreenWidth - 32, config.ScreenHeight / 6 * 3},
		})

	g.uniters = units
}

func (g *game) popUnits() []uniter {
	poppedUnits := make([]uniter, 0)
	for _, v := range g.unitPopTimeTable {
		if v.popTime <= g.currentFrame {
			// pop unit
			u := g.uniters[v.unitID]
			u.SetPosition(v.initialPosition.x, v.initialPosition.y)
			poppedUnits = append(poppedUnits, u)
			continue
		}
	}

	if len(poppedUnits) != 0 {
		// remove popped units from unitPopTimeTable
		g.unitPopTimeTable = g.unitPopTimeTable[len(poppedUnits):]
	}

	return poppedUnits
}

func (g *game) initialize() {
	g.pubsub = simra.NewPubSub()
	g.eventqueue = make(chan *command, 256)
	g.initField()
	g.initCtrlPanel()
	g.initPlayer()
	g.initUnits("") // TODO: input JSON string
	g.selection = &selection{}
	simra.GetInstance().AddTouchListener(g)
	g.pubsub.Subscribe("god", g)
	g.pubsub.Subscribe("selection", g.selection)
	g.updateGameState(gameStateInitial)
}

func (g *game) eventFetch() []*command {
	// note:
	// if new events are pushed while fetching,
	// they should be fetched next run loop to
	// avoid inifinite event fetching.

	qlen := len(g.eventqueue)
	if qlen == 0 {
		return nil
	}

	c := make([]*command, qlen)
	for i := 0; i < qlen; i++ {
		c[i] = <-g.eventqueue
	}
	return c
}

func (g *game) OnEvent(i interface{}) {
	c, ok := i.(*command)
	if !ok {
		// should be a command. ignore.
		return
	}

	_, ok = c.data.(*game)
	if !ok {
		// this is not for me
		return
	}

	switch c.commandtype {
	case commandGoToInitialState:
		g.updateGameState(gameStateInitial)
	case commandGoToRunningState:
		g.updateGameState(gameStateRunning)
	}
}

func (g *game) initialRunLoop() {
	commands := g.eventFetch()
	for _, v := range commands {
		g.pubsub.Publish(v)
	}
	g.players["player"].DoAction()
}

func (g *game) runningRunLoop() {
	poppedUnits := g.popUnits()
	for _, v := range poppedUnits {
		err := g.pubsub.Subscribe(v.GetID(), v)
		if err != nil {
			panic("failed to subscribe. fatal.")
		}

		// generate spawn command
		g.eventqueue <- newCommand(commandSpawn, v)
	}

	// event fetch and publish to all subscribers
	commands := g.eventFetch()
	for _, v := range commands {
		g.pubsub.Publish(v)
	}

	// invoke action for all units
	for _, v := range g.players {
		v.DoAction()
	}
	for _, v := range g.uniters {
		v.DoAction()
	}
}

// Drive is called from simra.
// This is used to update sprites position.
// This will be called 60 times per sec.
func (g *game) Drive() {
	defer func() {
		g.currentFrame++
	}()

	if g.nextScene != nil {
		simra.GetInstance().SetScene(g.nextScene)
	}
	g.currentRunLoop()
}

// OnTouchBegin is called when game scene is Touched.
func (g *game) OnTouchBegin(x, y float32) {
	// nop
}

// OnTouchMove is called when game scene is Touched and moved.
func (g *game) OnTouchMove(x, y float32) {
	// nop
}

// OnTouchEnd is called when game scene is Touched and it is released.
func (g *game) OnTouchEnd(x, y float32) {
	if g.gameState == gameStateInitial {
		if y > ctrlPanelHeight {
			g.players["player"].SetPosition(x, y)
			g.eventqueue <- newCommand(commandSpawn, g.players["player"])
			g.eventqueue <- newCommand(commandGoToRunningState, g)
		}
	}
	//g.nextScene = &result{currentStage: g.currentStage}
}
<|endoftext|>


<commit_before>/*
 * MumbleDJ
 * By Matthieu Grieger
 * songqueue.go
 * Copyright (c) 2014 Matthieu Grieger (MIT License)
 */

package main

import (
	"errors"
)

// QueueItem type declaration. QueueItem is an interface that groups together Song and Playlist
// types in a queue.
type QueueItem interface {
	AddSkip(string) error
	RemoveSkip(string) error
	SkipReached(int) bool
	ItemType() string
}

// SongQueue type declaration. Serves as a wrapper around the queue structure defined in queue.go.
type SongQueue struct {
	queue []QueueItem
}

// Initializes a new queue and returns the new SongQueue.
func NewSongQueue() *SongQueue {
	return &SongQueue{
		queue: make([]QueueItem, 0),
	}
}

// Adds an item to the SongQueue.
func (q *SongQueue) AddItem(i QueueItem) error {
	beforeLen := q.Len()
	q.queue = append(q.queue, i)
	if len(q.queue) == beforeLen+1 {
		return nil
	} else {
		return errors.New("Could not add QueueItem to the SongQueue.")
	}
}

// Returns the current QueueItem.
func (q *SongQueue) CurrentItem() QueueItem {
	return q.queue[0]
}

// Moves to the next item in SongQueue. NextItem() removes the first value in the queue.
func (q *SongQueue) NextItem() {
	q.queue = q.queue[1:]
}

// Returns the length of the SongQueue.
func (q *SongQueue) Len() int {
	return len(q.queue)
}

// A traversal function for SongQueue. Allows a visit function to be passed in which performs
// the specified action on each queue item. Traverses all individual songs, and all songs
// within playlists.
func (q *SongQueue) Traverse(visit func(i int, item QueueItem)) {
	for iQueue, queueItem := range q.queue {
		if queueItem.ItemType() == "playlist" {
			for iPlaylist, playlistItem := range q.queue[iQueue].(*Playlist).songs.queue {
				visit(iPlaylist, playlistItem)
			}
		} else {
			visit(iQueue, queueItem)
		}
	}
}

// OnItemFinished event. Deletes item that just finished playing, then queues the next item.
func (q *SongQueue) OnItemFinished() {
	if q.Len() != 0 {
		if q.CurrentItem().ItemType() == "playlist" {
			if err := q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Delete(); err == nil {
				if q.CurrentItem().(*Playlist).skipped == true {
					if q.Len() > 1 {
						q.NextItem()
						q.PrepareAndPlayNextItem()
					} else {
						q.queue = q.queue[:0]
					}
				} else if q.CurrentItem().(*Playlist).songs.Len() > 1 {
					q.CurrentItem().(*Playlist).songs.NextItem()
					q.PrepareAndPlayNextItem()
				} else {
					if q.Len() > 1 {
						q.NextItem()
						q.PrepareAndPlayNextItem()
					} else {
						q.queue = q.queue[:0]
					}
				}
			} else {
				panic(err)
			}
		} else {
			if err := q.CurrentItem().(*Song).Delete(); err == nil {
				if q.Len() > 1 {
					q.NextItem()
					q.PrepareAndPlayNextItem()
				} else {
					q.queue = q.queue[:0]
				}
			} else {
				panic(err)
			}
		}
	}
}

func (q *SongQueue) PrepareAndPlayNextItem() {
	if q.Len() != 0 {
		if q.CurrentItem().ItemType() == "playlist" {
			if err := q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Download(); err == nil {
				q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Play()
			} else {
				username := q.CurrentItem().(*Playlist).submitter
				user := dj.client.Self().Channel().Users().Find(username)
				user.Send(AUDIO_FAIL_MSG)
				q.OnItemFinished()
			}
		} else {
			if err := q.CurrentItem().(*Song).Download(); err == nil {
				q.CurrentItem().(*Song).Play()
			} else {
				username := q.CurrentItem().(*Song).submitter
				user := dj.client.Self().Channel().Users().Find(username)
				user.Send(AUDIO_FAIL_MSG)
				q.OnItemFinished()
			}
		}
	}
}
<commit_msg>Add SongQueue.PeekNext()<commit_after>/*
 * MumbleDJ
 * By Matthieu Grieger
 * songqueue.go
 * Copyright (c) 2014 Matthieu Grieger (MIT License)
 */

package main

import (
	"errors"
)

// QueueItem type declaration. QueueItem is an interface that groups together Song and Playlist
// types in a queue.
type QueueItem interface {
	AddSkip(string) error
	RemoveSkip(string) error
	SkipReached(int) bool
	ItemType() string
}

// SongQueue type declaration. Serves as a wrapper around the queue structure defined in queue.go.
type SongQueue struct {
	queue []QueueItem
}

// Initializes a new queue and returns the new SongQueue.
func NewSongQueue() *SongQueue {
	return &SongQueue{
		queue: make([]QueueItem, 0),
	}
}

// Adds an item to the SongQueue.
func (q *SongQueue) AddItem(i QueueItem) error {
	beforeLen := q.Len()
	q.queue = append(q.queue, i)
	if len(q.queue) == beforeLen+1 {
		return nil
	} else {
		return errors.New("Could not add QueueItem to the SongQueue.")
	}
}

// Returns the current QueueItem.
func (q *SongQueue) CurrentItem() QueueItem {
	return q.queue[0]
}

// Moves to the next item in SongQueue. NextItem() removes the first value in the queue.
func (q *SongQueue) NextItem() {
	q.queue = q.queue[1:]
}

// Peeks at the next item and returns the title, submitter, and error status.
func (q *SongQueue) PeekNext() (string, string, error) {
	var title, submitter string
	if q.Len() > 1 {
		if q.queue[1].ItemType == "playlist" {
			title = q.queue[1].(*Playlist).songs.queue[0].(*Song).title
			submitter = q.queue[1].(*Playlist).submitter
		} else {
			title = q.queue[1].(*Song).title
			submitter = q.queue[1].(*Song).submitter
		}
		return title, submitter, nil
	} else if q.CurrentItem().ItemType == "playlist" {
		title = q.CurrentItem().(*Playlist).songs.queue[1].(*Song).title
		submitter = q.CurrentItem().(*Playlist).submitter
		return title, submitter, nil
	} else {
		return "", "", errors.New("There is no item next in the queue.")
	}
}

// Returns the length of the SongQueue.
func (q *SongQueue) Len() int {
	return len(q.queue)
}

// A traversal function for SongQueue. Allows a visit function to be passed in which performs
// the specified action on each queue item. Traverses all individual songs, and all songs
// within playlists.
func (q *SongQueue) Traverse(visit func(i int, item QueueItem)) {
	for iQueue, queueItem := range q.queue {
		if queueItem.ItemType() == "playlist" {
			for iPlaylist, playlistItem := range q.queue[iQueue].(*Playlist).songs.queue {
				visit(iPlaylist, playlistItem)
			}
		} else {
			visit(iQueue, queueItem)
		}
	}
}

// OnItemFinished event. Deletes item that just finished playing, then queues the next item.
func (q *SongQueue) OnItemFinished() {
	if q.Len() != 0 {
		if q.CurrentItem().ItemType() == "playlist" {
			if err := q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Delete(); err == nil {
				if q.CurrentItem().(*Playlist).skipped == true {
					if q.Len() > 1 {
						q.NextItem()
						q.PrepareAndPlayNextItem()
					} else {
						q.queue = q.queue[:0]
					}
				} else if q.CurrentItem().(*Playlist).songs.Len() > 1 {
					q.CurrentItem().(*Playlist).songs.NextItem()
					q.PrepareAndPlayNextItem()
				} else {
					if q.Len() > 1 {
						q.NextItem()
						q.PrepareAndPlayNextItem()
					} else {
						q.queue = q.queue[:0]
					}
				}
			} else {
				panic(err)
			}
		} else {
			if err := q.CurrentItem().(*Song).Delete(); err == nil {
				if q.Len() > 1 {
					q.NextItem()
					q.PrepareAndPlayNextItem()
				} else {
					q.queue = q.queue[:0]
				}
			} else {
				panic(err)
			}
		}
	}
}

func (q *SongQueue) PrepareAndPlayNextItem() {
	if q.Len() != 0 {
		if q.CurrentItem().ItemType() == "playlist" {
			if err := q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Download(); err == nil {
				q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Play()
			} else {
				username := q.CurrentItem().(*Playlist).submitter
				user := dj.client.Self().Channel().Users().Find(username)
				user.Send(AUDIO_FAIL_MSG)
				q.OnItemFinished()
			}
		} else {
			if err := q.CurrentItem().(*Song).Download(); err == nil {
				q.CurrentItem().(*Song).Play()
			} else {
				username := q.CurrentItem().(*Song).submitter
				user := dj.client.Self().Channel().Users().Find(username)
				user.Send(AUDIO_FAIL_MSG)
				q.OnItemFinished()
			}
		}
	}
}
<|endoftext|>


<commit_before>// Package auth0 implements the OAuth2 protocol for authenticating users through uber.
// This package can be used as a reference implementation of an OAuth2 provider for Goth.
package auth0

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"

	"fmt"

	"github.com/markbates/goth"
	"golang.org/x/oauth2"
)

const (
	authEndpoint    string = "/authorize"
	tokenEndpoint   string = "/oauth/token"
	endpointProfile string = "/userinfo"
	protocol        string = "https://"
)

// Provider is the implementation of `goth.Provider` for accessing Auth0.
type Provider struct {
	ClientKey    string
	Secret       string
	CallbackURL  string
	Domain       string
	HTTPClient   *http.Client
	config       *oauth2.Config
	providerName string
}

type auth0UserResp struct {
	Name      string `json:"name"`
	NickName  string `json:"nickname"`
	Email     string `json:"email"`
	UserID    string `json:"user_id"`
	AvatarURL string `json:"picture"`
}

// New creates a new Auth0 provider and sets up important connection details.
// You should always call `auth0.New` to get a new provider.  Never try to
// create one manually.
func New(clientKey, secret, callbackURL string, auth0Domain string, scopes ...string) *Provider {
	p := &Provider{
		ClientKey:    clientKey,
		Secret:       secret,
		CallbackURL:  callbackURL,
		Domain:       auth0Domain,
		providerName: "auth0",
	}
	p.config = newConfig(p, scopes)
	return p
}

// Name is the name used to retrieve this provider later.
func (p *Provider) Name() string {
	return p.providerName
}

// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
func (p *Provider) SetName(name string) {
	p.providerName = name
}

func (p *Provider) Client() *http.Client {
	return goth.HTTPClientWithFallBack(p.HTTPClient)
}

// Debug is a no-op for the auth0 package.
func (p *Provider) Debug(debug bool) {}

// BeginAuth asks Auth0 for an authentication end-point.
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
	return &Session{
		AuthURL: p.config.AuthCodeURL(state),
	}, nil
}

// FetchUser will go to Auth0 and access basic information about the user.
// the full response will be included in RawData
// https://auth0.com/docs/api/authentication#get-user-info

func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
	s := session.(*Session)
	user := goth.User{
		AccessToken:  s.AccessToken,
		Provider:     p.Name(),
		RefreshToken: s.RefreshToken,
		ExpiresAt:    s.ExpiresAt,
	}

	if user.AccessToken == "" {
		// data is not yet retrieved since accessToken is still empty
		return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
	}

	userProfileURL := protocol + p.Domain + endpointProfile
	req, err := http.NewRequest("GET", userProfileURL, nil)
	if err != nil {
		return user, err
	}
	req.Header.Set("Authorization", "Bearer "+s.AccessToken)
	resp, err := p.Client().Do(req)
	if err != nil {
		if resp != nil {
			resp.Body.Close()
		}
		return user, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, resp.StatusCode)
	}

	err = userFromReader(resp.Body, &user)
	return user, err
}

func newConfig(provider *Provider, scopes []string) *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     provider.ClientKey,
		ClientSecret: provider.Secret,
		RedirectURL:  provider.CallbackURL,
		Endpoint: oauth2.Endpoint{
			AuthURL:  protocol + provider.Domain + authEndpoint,
			TokenURL: protocol + provider.Domain + tokenEndpoint,
		},
		Scopes: []string{},
	}

	if len(scopes) > 0 {
		for _, scope := range scopes {
			c.Scopes = append(c.Scopes, scope)
		}
	} else {
		c.Scopes = append(c.Scopes, "profile", "openid")
	}

	return c
}

func userFromReader(r io.Reader, user *goth.User) error {
	var rawData map[string]interface{}

	buf := new(bytes.Buffer)
	buf.ReadFrom(r)
	err := json.Unmarshal(buf.Bytes(), &rawData)
	if err != nil {
		return err
	}

	u := auth0UserResp{}
	err = json.Unmarshal(buf.Bytes(), &u)
	if err != nil {
		return err
	}
	user.Email = u.Email
	user.Name = u.Name
	user.NickName = u.NickName
	user.UserID = u.UserID
	user.AvatarURL = u.AvatarURL
	user.RawData = rawData
	return nil
}

//RefreshTokenAvailable refresh token is provided by auth provider or not
func (p *Provider) RefreshTokenAvailable() bool {
	return true
}

//RefreshToken get new access token based on the refresh token
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
	token := &oauth2.Token{RefreshToken: refreshToken}
	ts := p.config.TokenSource(oauth2.NoContext, token)
	newToken, err := ts.Token()
	if err != nil {
		return nil, err
	}
	return newToken, err
}
<commit_msg>Fix Auth0 provider, get UserID from sub<commit_after>// Package auth0 implements the OAuth2 protocol for authenticating users through uber.
// This package can be used as a reference implementation of an OAuth2 provider for Goth.
package auth0

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"

	"fmt"

	"github.com/markbates/goth"
	"golang.org/x/oauth2"
)

const (
	authEndpoint    string = "/authorize"
	tokenEndpoint   string = "/oauth/token"
	endpointProfile string = "/userinfo"
	protocol        string = "https://"
)

// Provider is the implementation of `goth.Provider` for accessing Auth0.
type Provider struct {
	ClientKey    string
	Secret       string
	CallbackURL  string
	Domain       string
	HTTPClient   *http.Client
	config       *oauth2.Config
	providerName string
}

type auth0UserResp struct {
	Name      string `json:"name"`
	NickName  string `json:"nickname"`
	Email     string `json:"email"`
	UserID    string `json:"sub"`
	AvatarURL string `json:"picture"`
}

// New creates a new Auth0 provider and sets up important connection details.
// You should always call `auth0.New` to get a new provider.  Never try to
// create one manually.
func New(clientKey, secret, callbackURL string, auth0Domain string, scopes ...string) *Provider {
	p := &Provider{
		ClientKey:    clientKey,
		Secret:       secret,
		CallbackURL:  callbackURL,
		Domain:       auth0Domain,
		providerName: "auth0",
	}
	p.config = newConfig(p, scopes)
	return p
}

// Name is the name used to retrieve this provider later.
func (p *Provider) Name() string {
	return p.providerName
}

// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
func (p *Provider) SetName(name string) {
	p.providerName = name
}

func (p *Provider) Client() *http.Client {
	return goth.HTTPClientWithFallBack(p.HTTPClient)
}

// Debug is a no-op for the auth0 package.
func (p *Provider) Debug(debug bool) {}

// BeginAuth asks Auth0 for an authentication end-point.
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
	return &Session{
		AuthURL: p.config.AuthCodeURL(state),
	}, nil
}

// FetchUser will go to Auth0 and access basic information about the user.
// the full response will be included in RawData
// https://auth0.com/docs/api/authentication#get-user-info

func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
	s := session.(*Session)
	user := goth.User{
		AccessToken:  s.AccessToken,
		Provider:     p.Name(),
		RefreshToken: s.RefreshToken,
		ExpiresAt:    s.ExpiresAt,
	}

	if user.AccessToken == "" {
		// data is not yet retrieved since accessToken is still empty
		return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
	}

	userProfileURL := protocol + p.Domain + endpointProfile
	req, err := http.NewRequest("GET", userProfileURL, nil)
	if err != nil {
		return user, err
	}
	req.Header.Set("Authorization", "Bearer "+s.AccessToken)
	resp, err := p.Client().Do(req)
	if err != nil {
		if resp != nil {
			resp.Body.Close()
		}
		return user, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, resp.StatusCode)
	}

	err = userFromReader(resp.Body, &user)
	return user, err
}

func newConfig(provider *Provider, scopes []string) *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     provider.ClientKey,
		ClientSecret: provider.Secret,
		RedirectURL:  provider.CallbackURL,
		Endpoint: oauth2.Endpoint{
			AuthURL:  protocol + provider.Domain + authEndpoint,
			TokenURL: protocol + provider.Domain + tokenEndpoint,
		},
		Scopes: []string{},
	}

	if len(scopes) > 0 {
		for _, scope := range scopes {
			c.Scopes = append(c.Scopes, scope)
		}
	} else {
		c.Scopes = append(c.Scopes, "profile", "openid")
	}

	return c
}

func userFromReader(r io.Reader, user *goth.User) error {
	var rawData map[string]interface{}

	buf := new(bytes.Buffer)
	buf.ReadFrom(r)
	err := json.Unmarshal(buf.Bytes(), &rawData)
	if err != nil {
		return err
	}

	u := auth0UserResp{}
	err = json.Unmarshal(buf.Bytes(), &u)
	if err != nil {
		return err
	}
	user.Email = u.Email
	user.Name = u.Name
	user.NickName = u.NickName
	user.UserID = u.UserID
	user.AvatarURL = u.AvatarURL
	user.RawData = rawData
	return nil
}

//RefreshTokenAvailable refresh token is provided by auth provider or not
func (p *Provider) RefreshTokenAvailable() bool {
	return true
}

//RefreshToken get new access token based on the refresh token
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
	token := &oauth2.Token{RefreshToken: refreshToken}
	ts := p.config.TokenSource(oauth2.NoContext, token)
	newToken, err := ts.Token()
	if err != nil {
		return nil, err
	}
	return newToken, err
}
<|endoftext|>


<commit_before>package main

import (
  "errors"
  "flag"
  "fmt"
  "github.com/gorilla/context"
  "github.com/gorilla/sessions"
  "github.com/keep94/appcommon/db"
  "github.com/keep94/appcommon/db/sqlite_db"
  "github.com/keep94/appcommon/http_util"
  "github.com/keep94/appcommon/logging"
  "github.com/keep94/gosqlite/sqlite"
  "github.com/keep94/ramstore"
  "github.com/keep94/vsafe"
  "github.com/keep94/vsafe/apps/vsafe/catedit"
  "github.com/keep94/vsafe/apps/vsafe/common"
  "github.com/keep94/vsafe/apps/vsafe/chpasswd"
  "github.com/keep94/vsafe/apps/vsafe/home"
  "github.com/keep94/vsafe/apps/vsafe/login"
  "github.com/keep94/vsafe/apps/vsafe/logout"
  "github.com/keep94/vsafe/apps/vsafe/single"
  "github.com/keep94/vsafe/apps/vsafe/static"
  "github.com/keep94/vsafe/vsafedb/for_sqlite"
  "github.com/keep94/weblogs"
  "net/http"
  "strconv"
)

const (
  // Set to the same thing as kXsrfTimeout in common/common.go
  kSessionTimeout = 900
)

var (
  fSSLCrt string
  fSSLKey string
  fPort string
  fDb string
  fIcon string
)

var (
  errNotLoggedIn = errors.New("vsafe app: not logged in.")
)

var (
  kDoer db.Doer
  kStore for_sqlite.Store
  kSessionStore = ramstore.NewRAMStore(kSessionTimeout)
  kPollingStore = asPollingStore(kSessionStore)
)

func main() {
  flag.Parse()
  if fDb == "" {
    fmt.Println("Need to specify at least -db flag.")
    flag.Usage()
    return
  }
  setupDb(fDb)
  mux := http.NewServeMux()
  http.HandleFunc("/", rootRedirect)
  http.Handle("/static/", http.StripPrefix("/static", static.New()))
  if fIcon != "" {
    err := http_util.AddStaticFromFile(
        http.DefaultServeMux, "/images/favicon.ico", fIcon)
    if err != nil {
      fmt.Printf("Icon file not found - %s\n", fIcon)
    }
  }
  http.Handle(
      "/auth/login",
      &login.Handler{SessionStore: kSessionStore, Store: kStore})
  http.Handle(
      "/auth/poll", pollHandler{})
  http.Handle(
      "/vsafe/", &authHandler{mux})
  mux.Handle("/vsafe/catedit", &catedit.Handler{Store: kStore, Doer: kDoer})
  mux.Handle("/vsafe/chpasswd", &chpasswd.Handler{Store: kStore, Doer: kDoer})
  mux.Handle("/vsafe/home", &home.Handler{Store: kStore})
  mux.Handle("/vsafe/logout", &logout.Handler{})
  mux.Handle("/vsafe/single", &single.Handler{Store: kStore, Doer: kDoer})
  defaultHandler := context.ClearHandler(
      weblogs.HandlerWithOptions(
          http.DefaultServeMux,
          &weblogs.Options{Logger: logging.ApacheCommonLoggerWithLatency()}))
  if fSSLCrt != "" && fSSLKey != "" {
    if err := http.ListenAndServeTLS(
        fPort, fSSLCrt, fSSLKey, defaultHandler); err != nil {
      fmt.Println(err)
    }
    return
  }
  if err := http.ListenAndServe(fPort, defaultHandler); err != nil {
    fmt.Println(err)
  }
}

type authHandler struct {
  *http.ServeMux
}

func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  user, _, err := authorizeSession(r, kSessionStore)
  if err == errNotLoggedIn {
    http_util.Redirect(
        w,
        r,
        http_util.NewUrl("/auth/login", "prev", r.URL.String()).String())
    return
  }
  if err != nil {
    http_util.ReportError(w, "Error reading database.", err)
    return
  }
  logging.SetUserName(r, user.Name)
  h.ServeMux.ServeHTTP(w, r)
}

type pollHandler struct {
}

func (h pollHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  r.ParseForm()
  keyId, _ := strconv.ParseInt(r.Form.Get("kid"), 10, 64)
  user, key, err := authorizeSession(r, kPollingStore)
  if err == errNotLoggedIn {
    http_util.Error(w, 401)
    return
  }
  if err != nil {
    http_util.ReportError(w, "Error reading database.", err)
    return
  }
  logging.SetUserName(r, user.Name)
  if keyId != key.Id {
    http_util.Error(w, 401)
    return
  }
  http_util.Error(w, 200)
}

func authorizeSession(
    r *http.Request,
    sessionStore sessions.Store) (*vsafe.User, *vsafe.Key, error) {
  session, err := common.NewUserSession(kStore, sessionStore, r)
  if err != nil {
    return nil, nil, err
  }
  key := session.Key()
  if session.User == nil || key == nil || key.Id != session.User.GetOwner() {
    return nil, nil, errNotLoggedIn
  }
  return session.User, key, nil
}

// TODO
func rootRedirect(w http.ResponseWriter, r *http.Request) {
  if r.URL.Path == "/" {
    http_util.Redirect(w, r, "/vsafe/home")
  } else {
    http_util.Error(w, http.StatusNotFound)
  }
}

func init() {
  flag.StringVar(&fSSLCrt, "ssl_crt", "", "SSL Certificate file")
  flag.StringVar(&fSSLKey, "ssl_key", "", "SSL Key file")
  flag.StringVar(&fPort, "http", ":8080", "Port to bind")
  flag.StringVar(&fDb, "db", "", "Path to database file")
  flag.StringVar(&fIcon, "icon", "", "Path to icon file")
}

func setupDb(filepath string) {
  conn, err := sqlite.Open(filepath)
  if err != nil {
    panic(err)
  }
  dbase := sqlite_db.New(conn)
  kDoer = sqlite_db.NewDoer(dbase)
  kStore = for_sqlite.New(dbase)
}

func asPollingStore(store *ramstore.RAMStore) *ramstore.RAMStore {
  result := *store
  result.SData = result.Data.AsPoller()
  result.Data = nil
  return &result
}

<commit_msg>Login page must not redirect to logout page.<commit_after>package main

import (
  "errors"
  "flag"
  "fmt"
  "github.com/gorilla/context"
  "github.com/gorilla/sessions"
  "github.com/keep94/appcommon/db"
  "github.com/keep94/appcommon/db/sqlite_db"
  "github.com/keep94/appcommon/http_util"
  "github.com/keep94/appcommon/logging"
  "github.com/keep94/gosqlite/sqlite"
  "github.com/keep94/ramstore"
  "github.com/keep94/vsafe"
  "github.com/keep94/vsafe/apps/vsafe/catedit"
  "github.com/keep94/vsafe/apps/vsafe/common"
  "github.com/keep94/vsafe/apps/vsafe/chpasswd"
  "github.com/keep94/vsafe/apps/vsafe/home"
  "github.com/keep94/vsafe/apps/vsafe/login"
  "github.com/keep94/vsafe/apps/vsafe/logout"
  "github.com/keep94/vsafe/apps/vsafe/single"
  "github.com/keep94/vsafe/apps/vsafe/static"
  "github.com/keep94/vsafe/vsafedb/for_sqlite"
  "github.com/keep94/weblogs"
  "net/http"
  "strconv"
)

const (
  // Set to the same thing as kXsrfTimeout in common/common.go
  kSessionTimeout = 900
)

var (
  fSSLCrt string
  fSSLKey string
  fPort string
  fDb string
  fIcon string
)

var (
  errNotLoggedIn = errors.New("vsafe app: not logged in.")
)

var (
  kDoer db.Doer
  kStore for_sqlite.Store
  kSessionStore = ramstore.NewRAMStore(kSessionTimeout)
  kPollingStore = asPollingStore(kSessionStore)
)

func main() {
  flag.Parse()
  if fDb == "" {
    fmt.Println("Need to specify at least -db flag.")
    flag.Usage()
    return
  }
  setupDb(fDb)
  mux := http.NewServeMux()
  http.HandleFunc("/", rootRedirect)
  http.Handle("/static/", http.StripPrefix("/static", static.New()))
  if fIcon != "" {
    err := http_util.AddStaticFromFile(
        http.DefaultServeMux, "/images/favicon.ico", fIcon)
    if err != nil {
      fmt.Printf("Icon file not found - %s\n", fIcon)
    }
  }
  http.Handle(
      "/auth/login",
      &login.Handler{SessionStore: kSessionStore, Store: kStore})
  http.Handle(
      "/auth/poll", pollHandler{})
  http.Handle(
      "/vsafe/", &authHandler{mux})
  mux.Handle("/vsafe/catedit", &catedit.Handler{Store: kStore, Doer: kDoer})
  mux.Handle("/vsafe/chpasswd", &chpasswd.Handler{Store: kStore, Doer: kDoer})
  mux.Handle("/vsafe/home", &home.Handler{Store: kStore})
  mux.Handle("/vsafe/logout", &logout.Handler{})
  mux.Handle("/vsafe/single", &single.Handler{Store: kStore, Doer: kDoer})
  defaultHandler := context.ClearHandler(
      weblogs.HandlerWithOptions(
          http.DefaultServeMux,
          &weblogs.Options{Logger: logging.ApacheCommonLoggerWithLatency()}))
  if fSSLCrt != "" && fSSLKey != "" {
    if err := http.ListenAndServeTLS(
        fPort, fSSLCrt, fSSLKey, defaultHandler); err != nil {
      fmt.Println(err)
    }
    return
  }
  if err := http.ListenAndServe(fPort, defaultHandler); err != nil {
    fmt.Println(err)
  }
}

type authHandler struct {
  *http.ServeMux
}

func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  user, _, err := authorizeSession(r, kSessionStore)
  if err == errNotLoggedIn {
    redirectString := r.URL.String()
    // Never have login page redirect to logout page
    if redirectString == "/vsafe/logout" {
      redirectString = "/vsafe/home"
    }
    http_util.Redirect(
        w,
        r,
        http_util.NewUrl("/auth/login", "prev", redirectString).String())
    return
  }
  if err != nil {
    http_util.ReportError(w, "Error reading database.", err)
    return
  }
  logging.SetUserName(r, user.Name)
  h.ServeMux.ServeHTTP(w, r)
}

type pollHandler struct {
}

func (h pollHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  r.ParseForm()
  keyId, _ := strconv.ParseInt(r.Form.Get("kid"), 10, 64)
  user, key, err := authorizeSession(r, kPollingStore)
  if err == errNotLoggedIn {
    http_util.Error(w, 401)
    return
  }
  if err != nil {
    http_util.ReportError(w, "Error reading database.", err)
    return
  }
  logging.SetUserName(r, user.Name)
  if keyId != key.Id {
    http_util.Error(w, 401)
    return
  }
  http_util.Error(w, 200)
}

func authorizeSession(
    r *http.Request,
    sessionStore sessions.Store) (*vsafe.User, *vsafe.Key, error) {
  session, err := common.NewUserSession(kStore, sessionStore, r)
  if err != nil {
    return nil, nil, err
  }
  key := session.Key()
  if session.User == nil || key == nil || key.Id != session.User.GetOwner() {
    return nil, nil, errNotLoggedIn
  }
  return session.User, key, nil
}

func rootRedirect(w http.ResponseWriter, r *http.Request) {
  if r.URL.Path == "/" {
    http_util.Redirect(w, r, "/vsafe/home")
  } else {
    http_util.Error(w, http.StatusNotFound)
  }
}

func init() {
  flag.StringVar(&fSSLCrt, "ssl_crt", "", "SSL Certificate file")
  flag.StringVar(&fSSLKey, "ssl_key", "", "SSL Key file")
  flag.StringVar(&fPort, "http", ":8080", "Port to bind")
  flag.StringVar(&fDb, "db", "", "Path to database file")
  flag.StringVar(&fIcon, "icon", "", "Path to icon file")
}

func setupDb(filepath string) {
  conn, err := sqlite.Open(filepath)
  if err != nil {
    panic(err)
  }
  dbase := sqlite_db.New(conn)
  kDoer = sqlite_db.NewDoer(dbase)
  kStore = for_sqlite.New(dbase)
}

func asPollingStore(store *ramstore.RAMStore) *ramstore.RAMStore {
  result := *store
  result.SData = result.Data.AsPoller()
  result.Data = nil
  return &result
}

<|endoftext|>


<commit_before>package util

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"time"

	"github.com/getlantern/eventual"
	"github.com/getlantern/fronted"
	"github.com/getlantern/golog"
	"github.com/getlantern/keyman"
)

var (
	log = golog.LoggerFor("flashlight.util")

	// This is for doing direct domain fronting if necessary. We store this as
	// an instance variable because it caches TLS session configs.
	direct = fronted.NewDirect()
)

// HTTPFetcher is a simple interface for types that are able to fetch data over HTTP.
type HTTPFetcher interface {
	Do(req *http.Request) (*http.Response, error)
}

func success(resp *http.Response) bool {
	return resp.StatusCode > 199 && resp.StatusCode < 400
}

// NewChainedAndFronted creates a new struct for accessing resources using chained
// and direct fronted servers in parallel.
func NewChainedAndFronted(proxyAddrFN eventual.Getter) *chainedAndFronted {
	cf := &chainedAndFronted{proxyAddrFN: proxyAddrFN}
	cf.fetcher = &dualFetcher{cf}
	return cf
}

// ChainedAndFronted fetches HTTP data in parallel using both chained and fronted
// servers.
type chainedAndFronted struct {
	proxyAddrFN eventual.Getter
	fetcher     HTTPFetcher
}

// Do will attempt to execute the specified HTTP request using only a chained fetcher
func (cf *chainedAndFronted) Do(req *http.Request) (*http.Response, error) {
	resp, err := cf.fetcher.Do(req)
	if err != nil {
		// If there's an error, switch back to using the dual fetcher.
		cf.fetcher = &dualFetcher{cf}
	} else if !success(resp) {
		cf.fetcher = &dualFetcher{cf}
	}
	return resp, err
}

type chainedFetcher struct {
	proxyAddrFN eventual.Getter
}

// Do will attempt to execute the specified HTTP request using only a chained fetcher
func (cf *chainedFetcher) Do(req *http.Request) (*http.Response, error) {
	log.Debugf("Using chained fronter")
	if client, err := HTTPClient("", cf.proxyAddrFN); err != nil {
		log.Errorf("Could not create HTTP client: %v", err)
		return nil, err
	} else {
		return client.Do(req)
	}
}

type dualFetcher struct {
	cf *chainedAndFronted
}

// Do will attempt to execute the specified HTTP request using both
// chained and fronted servers, simply returning the first response to
// arrive. Callers MUST use the Lantern-Fronted-URL HTTP header to
// specify the fronted URL to use.
func (df *dualFetcher) Do(req *http.Request) (*http.Response, error) {
	log.Debugf("Using dual fronter")
	frontedUrl := req.Header.Get("Lantern-Fronted-URL")
	req.Header.Del("Lantern-Fronted-URL")

	if frontedUrl == "" {
		return nil, errors.New("Callers MUST specify the fronted URL in the Lantern-Fronted-URL header")
	}
	responses := make(chan *http.Response, 2)
	errs := make(chan error, 2)

	request := func(client HTTPFetcher, req *http.Request) error {
		if resp, err := client.Do(req); err != nil {
			log.Errorf("Could not complete request with: %v, %v", frontedUrl, err)
			errs <- err
			return err
		} else {
			if success(resp) {
				log.Debugf("Got successful HTTP call!")
				responses <- resp
				return nil
			} else {
				// If the local proxy can't connect to any upstread proxies, for example,
				// it will return a 502.
				err := fmt.Errorf("Bad response code: %v", resp.StatusCode)
				errs <- err
				return err
			}
		}
	}

	go func() {
		if req, err := http.NewRequest("GET", frontedUrl, nil); err != nil {
			log.Errorf("Could not create request for: %v, %v", frontedUrl, err)
			errs <- err
		} else {
			log.Debug("Sending request via DDF")
			if err := request(direct, req); err != nil {
				log.Errorf("Fronted request failed: %v", err)
			} else {
				log.Debug("Fronted request succeeded")
			}
		}
	}()
	go func() {
		if client, err := HTTPClient("", df.cf.proxyAddrFN); err != nil {
			log.Errorf("Could not create HTTP client: %v", err)
			errs <- err
		} else {
			log.Debug("Sending chained request")
			if err := request(client, req); err != nil {
				log.Errorf("Chained request failed %v", err)
			} else {
				log.Debug("Switching to chained fronter for future requests since it succeeded")
				df.cf.fetcher = &chainedFetcher{}
			}
		}
	}()

	// Create channels for the final response or error. The response channel will be filled
	// in the case of any successful response as well as a non-error response for the second
	// response received. The error channel will only be filled if the first response is
	// unsuccessful and the second is an error.
	finalResponseCh := make(chan *http.Response, 1)
	finalErrorCh := make(chan error, 1)

	go readResponses(finalResponseCh, responses, finalErrorCh, errs)

	select {
	case resp := <-finalResponseCh:
		return resp, nil
	case err := <-finalErrorCh:
		return nil, err
	}
}

func readResponses(finalResponse chan *http.Response, responses chan *http.Response, finalErr chan error, errs chan error) {
	select {
	case resp := <-responses:
		if success(resp) {
			log.Debug("Got good first response")
			finalResponse <- resp

			// Just ignore the second response, but still process it.
			select {
			case resp := <-responses:
				log.Debug("Closing second response body")
				_ = resp.Body.Close()
				return
			case <-errs:
				log.Debug("Ignoring error on second response")
				return
			}
		} else {
			log.Debugf("Got bad first response -- wait for second")
			_ = resp.Body.Close()
			// Just use whatever we get from the second response.
			select {
			case resp := <-responses:
				finalResponse <- resp
			case err := <-errs:
				finalErr <- err
			}
		}
	case err := <-errs:
		log.Debugf("Got an error: %v", err)
		// Just use whatever we get from the second response.
		select {
		case resp := <-responses:
			finalResponse <- resp
		case err := <-errs:
			finalErr <- err
		}
	}
}

// PersistentHTTPClient creates an http.Client that persists across requests.
// If rootCA is specified, the client will validate the server's certificate
// on TLS connections against that RootCA. If proxyAddr is specified, the client
// will proxy through the given http proxy.
func PersistentHTTPClient(rootCA string, proxyAddrFN eventual.Getter) (*http.Client, error) {
	return httpClient(rootCA, proxyAddrFN, true)
}

// HTTPClient creates an http.Client. If rootCA is specified, the client will
// validate the server's certificate on TLS connections against that RootCA. If
// proxyAddr is specified, the client will proxy through the given http proxy.
func HTTPClient(rootCA string, proxyAddrFN eventual.Getter) (*http.Client, error) {
	return httpClient(rootCA, proxyAddrFN, false)
}

// httpClient creates an http.Client. If rootCA is specified, the client will
// validate the server's certificate on TLS connections against that RootCA. If
// proxyAddr is specified, the client will proxy through the given http proxy.
func httpClient(rootCA string, proxyAddrFN eventual.Getter, persistent bool) (*http.Client, error) {
	log.Debugf("Creating new HTTPClient with proxyAddrFN: %v", proxyAddrFN)

	tr := &http.Transport{
		Dial: (&net.Dialer{
			Timeout:   60 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,

		// This method is typically used for creating a one-off HTTP client
		// that we don't want to keep around for future calls, making
		// persistent connections a potential source of file descriptor
		// leaks. Note the name of this variable is misleading -- it would
		// be clearer to call it DisablePersistentConnections -- i.e. it has
		// nothing to do with TCP keep alives along the lines of the KeepAlive
		// variable in net.Dialer.
		DisableKeepAlives: !persistent,
	}

	if rootCA != "" {
		caCert, err := keyman.LoadCertificateFromPEMBytes([]byte(rootCA))
		if err != nil {
			return nil, fmt.Errorf("Unable to decode rootCA: %s", err)
		}
		tr.TLSClientConfig = &tls.Config{
			RootCAs: caCert.PoolContainingCert(),
		}
	}

	if proxyAddrFN != nil {
		log.Debug("Waiting for proxy server to come online")
		proxyAddr, ok := proxyAddrFN(60 * time.Second)
		if !ok {
			// Instead of finishing here we just log the error and continue, the client
			// we are going to create will surely fail when used and return errors,
			// those errors should be handled by the code that depends on such client.
			log.Errorf("Proxy never came online")
		}
		log.Debugf("Connected to proxy")

		tr.Proxy = func(req *http.Request) (*url.URL, error) {
			return url.Parse("http://" + proxyAddr.(string))
		}
	} else {
		log.Errorf("Using direct http client with no proxyAddr")
	}
	return &http.Client{Transport: tr}, nil
}
<commit_msg>Synchronized access to chainedAndFronted.fetcher, closes getlantern/lantern#3540<commit_after>package util

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"

	"github.com/getlantern/eventual"
	"github.com/getlantern/fronted"
	"github.com/getlantern/golog"
	"github.com/getlantern/keyman"
)

var (
	log = golog.LoggerFor("flashlight.util")

	// This is for doing direct domain fronting if necessary. We store this as
	// an instance variable because it caches TLS session configs.
	direct = fronted.NewDirect()
)

// HTTPFetcher is a simple interface for types that are able to fetch data over HTTP.
type HTTPFetcher interface {
	Do(req *http.Request) (*http.Response, error)
}

func success(resp *http.Response) bool {
	return resp.StatusCode > 199 && resp.StatusCode < 400
}

// NewChainedAndFronted creates a new struct for accessing resources using chained
// and direct fronted servers in parallel.
func NewChainedAndFronted(proxyAddrFN eventual.Getter) *chainedAndFronted {
	cf := &chainedAndFronted{proxyAddrFN: proxyAddrFN}
	cf.setFetcher(&dualFetcher{cf})
	return cf
}

// ChainedAndFronted fetches HTTP data in parallel using both chained and fronted
// servers.
type chainedAndFronted struct {
	proxyAddrFN eventual.Getter
	_fetcher    HTTPFetcher
	mu          sync.RWMutex
}

func (cf *chainedAndFronted) getFetcher() HTTPFetcher {
	cf.mu.RLock()
	result := cf._fetcher
	cf.mu.RUnlock()
	return result
}

func (cf *chainedAndFronted) setFetcher(fetcher HTTPFetcher) {
	cf.mu.Lock()
	cf._fetcher = fetcher
	cf.mu.Unlock()
}

// Do will attempt to execute the specified HTTP request using only a chained fetcher
func (cf *chainedAndFronted) Do(req *http.Request) (*http.Response, error) {
	resp, err := cf.getFetcher().Do(req)
	if err != nil {
		// If there's an error, switch back to using the dual fetcher.
		cf.setFetcher(&dualFetcher{cf})
	} else if !success(resp) {
		cf.setFetcher(&dualFetcher{cf})
	}
	return resp, err
}

type chainedFetcher struct {
	proxyAddrFN eventual.Getter
}

// Do will attempt to execute the specified HTTP request using only a chained fetcher
func (cf *chainedFetcher) Do(req *http.Request) (*http.Response, error) {
	log.Debugf("Using chained fronter")
	if client, err := HTTPClient("", cf.proxyAddrFN); err != nil {
		log.Errorf("Could not create HTTP client: %v", err)
		return nil, err
	} else {
		return client.Do(req)
	}
}

type dualFetcher struct {
	cf *chainedAndFronted
}

// Do will attempt to execute the specified HTTP request using both
// chained and fronted servers, simply returning the first response to
// arrive. Callers MUST use the Lantern-Fronted-URL HTTP header to
// specify the fronted URL to use.
func (df *dualFetcher) Do(req *http.Request) (*http.Response, error) {
	log.Debugf("Using dual fronter")
	frontedUrl := req.Header.Get("Lantern-Fronted-URL")
	req.Header.Del("Lantern-Fronted-URL")

	if frontedUrl == "" {
		return nil, errors.New("Callers MUST specify the fronted URL in the Lantern-Fronted-URL header")
	}
	responses := make(chan *http.Response, 2)
	errs := make(chan error, 2)

	request := func(client HTTPFetcher, req *http.Request) error {
		if resp, err := client.Do(req); err != nil {
			log.Errorf("Could not complete request with: %v, %v", frontedUrl, err)
			errs <- err
			return err
		} else {
			if success(resp) {
				log.Debugf("Got successful HTTP call!")
				responses <- resp
				return nil
			} else {
				// If the local proxy can't connect to any upstread proxies, for example,
				// it will return a 502.
				err := fmt.Errorf("Bad response code: %v", resp.StatusCode)
				errs <- err
				return err
			}
		}
	}

	go func() {
		if req, err := http.NewRequest("GET", frontedUrl, nil); err != nil {
			log.Errorf("Could not create request for: %v, %v", frontedUrl, err)
			errs <- err
		} else {
			log.Debug("Sending request via DDF")
			if err := request(direct, req); err != nil {
				log.Errorf("Fronted request failed: %v", err)
			} else {
				log.Debug("Fronted request succeeded")
			}
		}
	}()
	go func() {
		if client, err := HTTPClient("", df.cf.proxyAddrFN); err != nil {
			log.Errorf("Could not create HTTP client: %v", err)
			errs <- err
		} else {
			log.Debug("Sending chained request")
			if err := request(client, req); err != nil {
				log.Errorf("Chained request failed %v", err)
			} else {
				log.Debug("Switching to chained fronter for future requests since it succeeded")
				df.cf.setFetcher(&chainedFetcher{})
			}
		}
	}()

	// Create channels for the final response or error. The response channel will be filled
	// in the case of any successful response as well as a non-error response for the second
	// response received. The error channel will only be filled if the first response is
	// unsuccessful and the second is an error.
	finalResponseCh := make(chan *http.Response, 1)
	finalErrorCh := make(chan error, 1)

	go readResponses(finalResponseCh, responses, finalErrorCh, errs)

	select {
	case resp := <-finalResponseCh:
		return resp, nil
	case err := <-finalErrorCh:
		return nil, err
	}
}

func readResponses(finalResponse chan *http.Response, responses chan *http.Response, finalErr chan error, errs chan error) {
	select {
	case resp := <-responses:
		if success(resp) {
			log.Debug("Got good first response")
			finalResponse <- resp

			// Just ignore the second response, but still process it.
			select {
			case resp := <-responses:
				log.Debug("Closing second response body")
				_ = resp.Body.Close()
				return
			case <-errs:
				log.Debug("Ignoring error on second response")
				return
			}
		} else {
			log.Debugf("Got bad first response -- wait for second")
			_ = resp.Body.Close()
			// Just use whatever we get from the second response.
			select {
			case resp := <-responses:
				finalResponse <- resp
			case err := <-errs:
				finalErr <- err
			}
		}
	case err := <-errs:
		log.Debugf("Got an error: %v", err)
		// Just use whatever we get from the second response.
		select {
		case resp := <-responses:
			finalResponse <- resp
		case err := <-errs:
			finalErr <- err
		}
	}
}

// PersistentHTTPClient creates an http.Client that persists across requests.
// If rootCA is specified, the client will validate the server's certificate
// on TLS connections against that RootCA. If proxyAddr is specified, the client
// will proxy through the given http proxy.
func PersistentHTTPClient(rootCA string, proxyAddrFN eventual.Getter) (*http.Client, error) {
	return httpClient(rootCA, proxyAddrFN, true)
}

// HTTPClient creates an http.Client. If rootCA is specified, the client will
// validate the server's certificate on TLS connections against that RootCA. If
// proxyAddr is specified, the client will proxy through the given http proxy.
func HTTPClient(rootCA string, proxyAddrFN eventual.Getter) (*http.Client, error) {
	return httpClient(rootCA, proxyAddrFN, false)
}

// httpClient creates an http.Client. If rootCA is specified, the client will
// validate the server's certificate on TLS connections against that RootCA. If
// proxyAddr is specified, the client will proxy through the given http proxy.
func httpClient(rootCA string, proxyAddrFN eventual.Getter, persistent bool) (*http.Client, error) {
	log.Debugf("Creating new HTTPClient with proxyAddrFN: %v", proxyAddrFN)

	tr := &http.Transport{
		Dial: (&net.Dialer{
			Timeout:   60 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,

		// This method is typically used for creating a one-off HTTP client
		// that we don't want to keep around for future calls, making
		// persistent connections a potential source of file descriptor
		// leaks. Note the name of this variable is misleading -- it would
		// be clearer to call it DisablePersistentConnections -- i.e. it has
		// nothing to do with TCP keep alives along the lines of the KeepAlive
		// variable in net.Dialer.
		DisableKeepAlives: !persistent,
	}

	if rootCA != "" {
		caCert, err := keyman.LoadCertificateFromPEMBytes([]byte(rootCA))
		if err != nil {
			return nil, fmt.Errorf("Unable to decode rootCA: %s", err)
		}
		tr.TLSClientConfig = &tls.Config{
			RootCAs: caCert.PoolContainingCert(),
		}
	}

	if proxyAddrFN != nil {
		log.Debug("Waiting for proxy server to come online")
		proxyAddr, ok := proxyAddrFN(60 * time.Second)
		if !ok {
			// Instead of finishing here we just log the error and continue, the client
			// we are going to create will surely fail when used and return errors,
			// those errors should be handled by the code that depends on such client.
			log.Errorf("Proxy never came online")
		}
		log.Debugf("Connected to proxy")

		tr.Proxy = func(req *http.Request) (*url.URL, error) {
			return url.Parse("http://" + proxyAddr.(string))
		}
	} else {
		log.Errorf("Using direct http client with no proxyAddr")
	}
	return &http.Client{Transport: tr}, nil
}
<|endoftext|>


<commit_before>package cli

import (
	"bytes"
	"fmt"
	"log"
	"os/exec"
	"strings"

	"github.com/flynn/flynn/pkg/cluster"
)

var logs = map[string]string{
	"upstart-flynn-host.log": "/var/log/upstart/flynn-host.log",
	"tmp-flynn-host.log":     "/tmp/flynn-host.log",
}

var debugCmds = [][]string{
	{"ps", "faux"},
	{"ifconfig"},
	{"uname", "-a"},
	{"lsb_release", "-a"},
	{"date"},
	{"free", "-m"},
	{"dpkg-query", "-W", "-f", "${Package}: ${Version}\n", "libvirt-bin", "flynn-host"},
	{"virsh", "-c", "lxc:///", "list"},
	{"virsh", "-c", "lxc:///", "net-list"},
}

func init() {
	Register("upload-debug-info", runUploadDebugInfo, `
usage: flynn-host upload-debug-info

Upload debug information to an anonymous gist`)
}

func runUploadDebugInfo() error {
	gist := &Gist{
		Description: "Flynn debug information",
		Public:      false,
		Files:       make(map[string]File),
	}

	for name, filepath := range logs {
		if err := gist.AddLocalFile(name, filepath); err != nil {
			log.Printf("error adding %s: %s", name, err)
		}
	}

	if err := captureJobs(gist); err != nil {
		log.Println(err)
	}

	var debugOutput string
	for _, cmd := range debugCmds {
		output, err := captureCmd(cmd[0], cmd[1:]...)
		if err != nil {
			log.Printf("could not capture command '%s': %s", strings.Join(cmd, " "), err)
			continue
		}
		debugOutput += fmt.Sprintln("===>", strings.Replace(strings.Join(cmd, " "), "\n", `\n`, -1), "\n", output)
	}
	gist.AddFile("0-debug-output.log", debugOutput)

	if err := gist.Upload(); err != nil {
		return err
	}

	log.Println("Debug information uploaded to:", gist.URL)
	return nil
}

func captureCmd(name string, arg ...string) (string, error) {
	var buf bytes.Buffer
	c := exec.Command(name, arg...)
	c.Stdout = &buf
	if err := c.Run(); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func captureJobs(gist *Gist) error {
	client, err := cluster.NewClient()
	if err != nil {
		return err
	}
	defer client.Close()

	jobs, err := jobList(client, true)
	if err != nil {
		return err
	}

	var buf bytes.Buffer
	printJobs(jobs, &buf)
	gist.AddFile("1-jobs.log", buf.String())

	for _, job := range jobs {
		var name string
		if app, ok := job.Job.Metadata["flynn-controller.app_name"]; ok {
			name += app + "-"
		}
		if typ, ok := job.Job.Metadata["flynn-controller.type"]; ok {
			name += typ + "-"
		}
		name += job.Job.ID + ".log"

		var content bytes.Buffer
		printJobDesc(&job, &content)
		fmt.Fprint(&content, "\n\n***** ***** ***** ***** ***** ***** ***** ***** ***** *****\n\n")
		getLog(job.HostID, job.Job.ID, client, false, &content, &content)

		gist.AddFile(name, content.String())
	}

	return nil
}
<commit_msg>host/cli: Add version to upload-debug-info<commit_after>package cli

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"

	"github.com/flynn/flynn/pkg/cluster"
)

var logs = map[string]string{
	"upstart-flynn-host.log": "/var/log/upstart/flynn-host.log",
	"tmp-flynn-host.log":     "/tmp/flynn-host.log",
}

var debugCmds = [][]string{
	{"ps", "faux"},
	{"ifconfig"},
	{"uname", "-a"},
	{"lsb_release", "-a"},
	{"date"},
	{"free", "-m"},
	{"dpkg-query", "-W", "-f", "${Package}: ${Version}\n", "libvirt-bin", "flynn-host"},
	{os.Args[0], "version"},
	{"virsh", "-c", "lxc:///", "list"},
	{"virsh", "-c", "lxc:///", "net-list"},
}

func init() {
	Register("upload-debug-info", runUploadDebugInfo, `
usage: flynn-host upload-debug-info

Upload debug information to an anonymous gist`)
}

func runUploadDebugInfo() error {
	gist := &Gist{
		Description: "Flynn debug information",
		Public:      false,
		Files:       make(map[string]File),
	}

	for name, filepath := range logs {
		if err := gist.AddLocalFile(name, filepath); err != nil {
			log.Printf("error adding %s: %s", name, err)
		}
	}

	if err := captureJobs(gist); err != nil {
		log.Println(err)
	}

	var debugOutput string
	for _, cmd := range debugCmds {
		output, err := captureCmd(cmd[0], cmd[1:]...)
		if err != nil {
			log.Printf("could not capture command '%s': %s", strings.Join(cmd, " "), err)
			continue
		}
		debugOutput += fmt.Sprintln("===>", strings.Replace(strings.Join(cmd, " "), "\n", `\n`, -1), "\n", output)
	}
	gist.AddFile("0-debug-output.log", debugOutput)

	if err := gist.Upload(); err != nil {
		return err
	}

	log.Println("Debug information uploaded to:", gist.URL)
	return nil
}

func captureCmd(name string, arg ...string) (string, error) {
	var buf bytes.Buffer
	c := exec.Command(name, arg...)
	c.Stdout = &buf
	if err := c.Run(); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func captureJobs(gist *Gist) error {
	client, err := cluster.NewClient()
	if err != nil {
		return err
	}
	defer client.Close()

	jobs, err := jobList(client, true)
	if err != nil {
		return err
	}

	var buf bytes.Buffer
	printJobs(jobs, &buf)
	gist.AddFile("1-jobs.log", buf.String())

	for _, job := range jobs {
		var name string
		if app, ok := job.Job.Metadata["flynn-controller.app_name"]; ok {
			name += app + "-"
		}
		if typ, ok := job.Job.Metadata["flynn-controller.type"]; ok {
			name += typ + "-"
		}
		name += job.Job.ID + ".log"

		var content bytes.Buffer
		printJobDesc(&job, &content)
		fmt.Fprint(&content, "\n\n***** ***** ***** ***** ***** ***** ***** ***** ***** *****\n\n")
		getLog(job.HostID, job.Job.ID, client, false, &content, &content)

		gist.AddFile(name, content.String())
	}

	return nil
}
<|endoftext|>


<commit_before>/****************************************************************************
 * Copyright (c) 2013, Scott Ferguson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the software nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

package goat

import (
	"encoding/gob"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"net"
	"net/http"
	"net/url"
	"strconv"
)

const (
	GET    = 1 << 0
	POST   = 1 << 1
	PUT    = 1 << 2
	DELETE = 1 << 3

	methodGet    = "GET"
	methodPost   = "POST"
	methodPut    = "PUT"
	methodDelete = "DELETE"
)

type Goat struct {
	Router       *mux.Router
	routes       map[string]*route
	middleware   []Middleware
	dbsession    *mgo.Session
	dbname       string
	sessionstore sessions.Store
	listener     *net.TCPListener
	servemux     *http.ServeMux
}

type Handler func(http.ResponseWriter, *http.Request, *Context) error

type route struct {
	*Goat
	path        string
	name        string
	handler     Handler
	interceptor Interceptor
}

func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var c *Context
	var err error

	if c, err = NewContext(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	defer c.Close()

	// Execute Middleware
	for _, m := range r.middleware {
		m(req, c)
	}

	// Execute the handler
	if r.handler != nil {
		err = r.handler(w, req, c)
	} else if r.interceptor != nil {
		rh := r.interceptor(w, req, c)
		err = rh(w, req, c)
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func methodList(methods int) (r []string) {
	if methods&GET == GET {
		r = append(r, methodGet)
	}

	if methods&POST == POST {
		r = append(r, methodPost)
	}

	if methods&PUT == PUT {
		r = append(r, methodPut)
	}

	if methods&DELETE == DELETE {
		r = append(r, methodDelete)
	}

	return
}

func NewGoat() *Goat {
	// Initialize session store
	gob.Register(bson.ObjectId(""))
	s := sessions.NewCookieStore([]byte("sevenbyelevensecretbomberboy"))
	r := mux.NewRouter()

	mx := http.NewServeMux()
	mx.Handle("/", r)

	return &Goat{
		Router:       r,
		sessionstore: s,
		routes:       make(map[string]*route),
		servemux:     mx,
	}
}

func (g *Goat) RegisterRoute(path, name string, method int, handler interface{}) {
	// Initialize the HTTP router
	r := new(route)
	r.Goat = g
	r.path = path
	r.name = name

	if g.routes[r.name] != nil {
		return
	}

	g.routes[r.name] = r

	if h, ok := handler.(func(http.ResponseWriter, *http.Request, *Context) error); ok {
		r.handler = h
	} else if h, ok := handler.(Handler); ok {
		r.handler = h
	} else if i, ok := handler.(Interceptor); ok {
		r.interceptor = i
	} else if h, ok := handler.(http.Handler); ok {
		g.Router.Handle(path, h)
		return
	} else {
		panic("Unknown handler passed to RegisterRoute")
	}

	methods := methodList(method)
	g.Router.Handle(path, r).Methods(methods...).Name(r.name)
}

func (g *Goat) CopyDB() *mgo.Database {
	return g.dbsession.Copy().DB(g.dbname)
}

func (g *Goat) CloneDB() *mgo.Database {
	return g.dbsession.Clone().DB(g.dbname)
}

func (g *Goat) RegisterStaticFileHandler(remote, local string) {
	// Static file handler
	http.Handle(remote, http.FileServer(http.Dir(local)))
}

func (g *Goat) RegisterMiddleware(m Middleware) {
	g.middleware = append(g.middleware, m)
}

func (g *Goat) Reverse(root string, params ...string) (*url.URL, error) {
	return g.Router.Get(root).URL(params...)
}

func (g *Goat) ListenAndServe(port string) error {
	p := 8080

	if port != "" {
		p, _ = strconv.Atoi(port)
	}

	server := &http.Server{
		Handler: g.servemux,
	}

	g.listener, _ = net.ListenTCP("tcp", &net.TCPAddr{
		Port: p,
	})

	return server.Serve(g.listener)
}

func (g *Goat) ListenAndServeTLS(cert, key, addr string) error {
	server := &http.Server{
		Addr:    addr,
		Handler: g.servemux,
	}
	return server.ListenAndServeTLS(cert, key)
}

func (g *Goat) Close() {
	g.listener.Close()
}
<commit_msg>Use SPDY when serving up via SSL<commit_after>/****************************************************************************
 * Copyright (c) 2013, Scott Ferguson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the software nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SCOTT FERGUSON ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL SCOTT FERGUSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

package goat

import (
	"encoding/gob"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/scottferg/gospdy"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"net"
	"net/http"
	"net/url"
	"strconv"
)

const (
	GET    = 1 << 0
	POST   = 1 << 1
	PUT    = 1 << 2
	DELETE = 1 << 3

	methodGet    = "GET"
	methodPost   = "POST"
	methodPut    = "PUT"
	methodDelete = "DELETE"
)

type Goat struct {
	Router       *mux.Router
	routes       map[string]*route
	middleware   []Middleware
	dbsession    *mgo.Session
	dbname       string
	sessionstore sessions.Store
	listener     *net.TCPListener
	servemux     *http.ServeMux
}

type Handler func(http.ResponseWriter, *http.Request, *Context) error

type route struct {
	*Goat
	path        string
	name        string
	handler     Handler
	interceptor Interceptor
}

func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var c *Context
	var err error

	if c, err = NewContext(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	defer c.Close()

	// Execute Middleware
	for _, m := range r.middleware {
		m(req, c)
	}

	// Execute the handler
	if r.handler != nil {
		err = r.handler(w, req, c)
	} else if r.interceptor != nil {
		rh := r.interceptor(w, req, c)
		err = rh(w, req, c)
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func methodList(methods int) (r []string) {
	if methods&GET == GET {
		r = append(r, methodGet)
	}

	if methods&POST == POST {
		r = append(r, methodPost)
	}

	if methods&PUT == PUT {
		r = append(r, methodPut)
	}

	if methods&DELETE == DELETE {
		r = append(r, methodDelete)
	}

	return
}

func NewGoat() *Goat {
	// Initialize session store
	gob.Register(bson.ObjectId(""))
	s := sessions.NewCookieStore([]byte("sevenbyelevensecretbomberboy"))
	r := mux.NewRouter()

	mx := http.NewServeMux()
	mx.Handle("/", r)

	return &Goat{
		Router:       r,
		sessionstore: s,
		routes:       make(map[string]*route),
		servemux:     mx,
	}
}

func (g *Goat) RegisterRoute(path, name string, method int, handler interface{}) {
	// Initialize the HTTP router
	r := new(route)
	r.Goat = g
	r.path = path
	r.name = name

	if g.routes[r.name] != nil {
		return
	}

	g.routes[r.name] = r

	if h, ok := handler.(func(http.ResponseWriter, *http.Request, *Context) error); ok {
		r.handler = h
	} else if h, ok := handler.(Handler); ok {
		r.handler = h
	} else if i, ok := handler.(Interceptor); ok {
		r.interceptor = i
	} else if h, ok := handler.(http.Handler); ok {
		g.Router.Handle(path, h)
		return
	} else {
		panic("Unknown handler passed to RegisterRoute")
	}

	methods := methodList(method)
	g.Router.Handle(path, r).Methods(methods...).Name(r.name)
}

func (g *Goat) CopyDB() *mgo.Database {
	return g.dbsession.Copy().DB(g.dbname)
}

func (g *Goat) CloneDB() *mgo.Database {
	return g.dbsession.Clone().DB(g.dbname)
}

func (g *Goat) RegisterStaticFileHandler(remote, local string) {
	// Static file handler
	http.Handle(remote, http.FileServer(http.Dir(local)))
}

func (g *Goat) RegisterMiddleware(m Middleware) {
	g.middleware = append(g.middleware, m)
}

func (g *Goat) Reverse(root string, params ...string) (*url.URL, error) {
	return g.Router.Get(root).URL(params...)
}

func (g *Goat) ListenAndServe(port string) error {
	p := 8080

	if port != "" {
		p, _ = strconv.Atoi(port)
	}

	server := &http.Server{
		Handler: g.servemux,
	}

	g.listener, _ = net.ListenTCP("tcp", &net.TCPAddr{
		Port: p,
	})

	return server.Serve(g.listener)
}

func (g *Goat) ListenAndServeTLS(cert, key, addr string) error {
	return spdy.ListenAndServeTLS(addr, cert, key, g.servemux)
}

func (g *Goat) Close() {
	g.listener.Close()
}
<|endoftext|>


<commit_before>package gogadgets

import (
	"bitbucket.org/cswank/gogadgets/utils"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"syscall"
)

//GPIO interacts with the linux sysfs interface for GPIO
//to turn pins on and off.  The pins that are listed in
//gogadgets.Pins have been found to be availabe by default
//but by using the device tree overlay you can make more
//pins available.
//GPIO also has a Wait method and can poll a pin and wait
//for a change of direction.
type GPIO struct {
	OutputDevice
	Poller
	units         string
	export        string
	exportPath    string
	directionPath string
	valuePath     string
	edgePath      string
	direction     string
	edge          string
	fd            int
	fdSet         *syscall.FdSet
	buf           []byte
}

func NewGPIO(pin *Pin) (OutputDevice, error) {
	var portMap, export string
	var ok bool
	if pin.Platform == "rpi" {
		export, ok = PiPins[pin.Pin]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such pin: %s", pin.Pin))
		}
	} else {
		portMap, ok = Pins["gpio"][pin.Port]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such port: %s", pin.Port))
		}
		export, ok = portMap[pin.Pin]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such pin: %s", pin.Pin))
		}
	}
	if pin.Direction == "" {
		pin.Direction = "out"
	}
	g := &GPIO{
		export:        export,
		exportPath:    "/sys/class/gpio/export",
		directionPath: fmt.Sprintf("/sys/class/gpio/gpio%s/direction", export),
		edgePath:      fmt.Sprintf("/sys/class/gpio/gpio%s/edge", export),
		valuePath:     fmt.Sprintf("/sys/class/gpio/gpio%s/value", export),
		direction:     pin.Direction,
		edge:          pin.Edge,
	}
	err := g.Init()
	return g, err
}

func (g *GPIO) Init() error {
	var err error
	if !utils.FileExists(g.directionPath) {
		err = g.writeValue(g.exportPath, g.export)
	}
	if err == nil {
		err = g.writeValue(g.directionPath, g.direction)
		if err == nil && g.direction == "out" {
			err = g.writeValue(g.valuePath, "0")
		} else if err == nil && g.edge != "" {
			err = g.writeValue(g.edgePath, g.edge)
		}
	}
	return err
}

func (g *GPIO) Update(msg *Message) {

}

func (g *GPIO) On(val *Value) error {
	return g.writeValue(g.valuePath, "1")
}

func (g *GPIO) Status() interface{} {
	data, err := ioutil.ReadFile(g.valuePath)
	return err == nil && string(data) == "1\n"
}

func (g *GPIO) Off() error {
	return g.writeValue(g.valuePath, "0")
}

func (g *GPIO) writeValue(path, value string) error {
	return ioutil.WriteFile(path, []byte(value), os.ModeDevice)
}

func (g *GPIO) Wait() (bool, error) {
	if g.fd == 0 {
		fd, err := syscall.Open(g.valuePath, syscall.O_RDONLY, 0666)
		if err != nil {
			return false, err
		}
		g.fd = fd
		g.fdSet = new(syscall.FdSet)
		FD_SET(g.fd, g.fdSet)
		g.buf = make([]byte, 64)
		syscall.Read(g.fd, g.buf)
	}
	syscall.Select(g.fd+1, nil, nil, g.fdSet, nil)
	syscall.Seek(g.fd, 0, 0)
	_, err := syscall.Read(g.fd, g.buf)
	if err != nil {
		return false, err
	}
	return string(g.buf[:2]) == "1\n", nil
}

func FD_SET(fd int, p *syscall.FdSet) {
	p.Bits[fd/32] |= 1 << (uint(fd) % 32)
}
<commit_msg>fixing newgpio<commit_after>package gogadgets

import (
	"bitbucket.org/cswank/gogadgets/utils"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"syscall"
)

//GPIO interacts with the linux sysfs interface for GPIO
//to turn pins on and off.  The pins that are listed in
//gogadgets.Pins have been found to be availabe by default
//but by using the device tree overlay you can make more
//pins available.
//GPIO also has a Wait method and can poll a pin and wait
//for a change of direction.
type GPIO struct {
	OutputDevice
	Poller
	units         string
	export        string
	exportPath    string
	directionPath string
	valuePath     string
	edgePath      string
	direction     string
	edge          string
	fd            int
	fdSet         *syscall.FdSet
	buf           []byte
}

func NewGPIO(pin *Pin) (OutputDevice, error) {
	var export string

	var ok bool
	if pin.Platform == "rpi" {
		export, ok = PiPins[pin.Pin]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such pin: %s", pin.Pin))
		}
	} else {
		var portMap map[string]string
		portMap, ok = Pins["gpio"][pin.Port]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such port: %s", pin.Port))
		}
		export, ok = portMap[pin.Pin]
		if !ok {
			return nil, errors.New(fmt.Sprintf("no such pin: %s", pin.Pin))
		}
	}
	if pin.Direction == "" {
		pin.Direction = "out"
	}
	g := &GPIO{
		export:        export,
		exportPath:    "/sys/class/gpio/export",
		directionPath: fmt.Sprintf("/sys/class/gpio/gpio%s/direction", export),
		edgePath:      fmt.Sprintf("/sys/class/gpio/gpio%s/edge", export),
		valuePath:     fmt.Sprintf("/sys/class/gpio/gpio%s/value", export),
		direction:     pin.Direction,
		edge:          pin.Edge,
	}
	err := g.Init()
	return g, err
}

func (g *GPIO) Init() error {
	var err error
	if !utils.FileExists(g.directionPath) {
		err = g.writeValue(g.exportPath, g.export)
	}
	if err == nil {
		err = g.writeValue(g.directionPath, g.direction)
		if err == nil && g.direction == "out" {
			err = g.writeValue(g.valuePath, "0")
		} else if err == nil && g.edge != "" {
			err = g.writeValue(g.edgePath, g.edge)
		}
	}
	return err
}

func (g *GPIO) Update(msg *Message) {

}

func (g *GPIO) On(val *Value) error {
	return g.writeValue(g.valuePath, "1")
}

func (g *GPIO) Status() interface{} {
	data, err := ioutil.ReadFile(g.valuePath)
	return err == nil && string(data) == "1\n"
}

func (g *GPIO) Off() error {
	return g.writeValue(g.valuePath, "0")
}

func (g *GPIO) writeValue(path, value string) error {
	return ioutil.WriteFile(path, []byte(value), os.ModeDevice)
}

func (g *GPIO) Wait() (bool, error) {
	if g.fd == 0 {
		fd, err := syscall.Open(g.valuePath, syscall.O_RDONLY, 0666)
		if err != nil {
			return false, err
		}
		g.fd = fd
		g.fdSet = new(syscall.FdSet)
		FD_SET(g.fd, g.fdSet)
		g.buf = make([]byte, 64)
		syscall.Read(g.fd, g.buf)
	}
	syscall.Select(g.fd+1, nil, nil, g.fdSet, nil)
	syscall.Seek(g.fd, 0, 0)
	_, err := syscall.Read(g.fd, g.buf)
	if err != nil {
		return false, err
	}
	return string(g.buf[:2]) == "1\n", nil
}

func FD_SET(fd int, p *syscall.FdSet) {
	p.Bits[fd/32] |= 1 << (uint(fd) % 32)
}
<|endoftext|>


<commit_before>package grid

import (
	"fmt"
	"log"
	"runtime"

	"github.com/go-gl/gl/v3.3-core/gl"
	"github.com/go-gl/glfw/v3.1/glfw"
)

const (
	OpenGlVerMajor = 3
	OpenGLVerMinor = 3
)

type Game struct {
	Width      int32
	Height     int32
	Fullscreen bool
	Resizable  bool
	Title      string
	Scene      Scene
}

type Scene interface {
	Setup()
	Draw(dt float32)
	Update(dt float32)
}

func Run(g *Game) error {
	if g.Scene == nil {
		return fmt.Errorf("Scene property of given Game struct is empty")
	}

	// GLFW event handling must run on the main OS thread
	runtime.LockOSThread()
	if err := glfw.Init(); err != nil {
		return fmt.Errorf("glfw.Init failed: %s", err)
	}
	defer glfw.Terminate()

	glfw.WindowHint(glfw.ContextVersionMajor, OpenGLVerMinor)
	glfw.WindowHint(glfw.ContextVersionMinor, OpenGLVerMinor)

	glfw.WindowHint(glfw.Resizable, glfw.False)
	if g.Resizable {
		glfw.WindowHint(glfw.Resizable, glfw.True)
	}

	glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
	glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

	window, err := glfw.CreateWindow(int(g.Width), int(g.Height), g.Title, nil, nil)
	if err != nil {
		return fmt.Errorf("glfw.CreateWindow failed: %s", err)
	}
	window.MakeContextCurrent()

	// Initialize Glow
	if err := gl.Init(); err != nil {
		return fmt.Errorf("gl.Init failed:", err)
	}

	version := gl.GoStr(gl.GetString(gl.VERSION))
	log.Println("gl.Init successful, OpenGL version:", version)

	var previousTime, deltaTime, time float32
	previousTime = float32(glfw.GetTime()) - 1.0/60.0

	g.Scene.Setup()

	for !window.ShouldClose() {
		glfw.PollEvents()

		time = float32(glfw.GetTime())
		deltaTime = time - previousTime

		gl.ClearColor(0.2, 0.3, 0.3, 0.5)
		gl.Clear(gl.COLOR_BUFFER_BIT)

		g.Scene.Update(deltaTime)
		g.Scene.Draw(deltaTime)

		previousTime = time

		window.SwapBuffers()
	}

	return nil
}
<commit_msg>Include glfw.PollEvents in deltaTime<commit_after>package grid

import (
	"fmt"
	"log"
	"runtime"

	"github.com/go-gl/gl/v3.3-core/gl"
	"github.com/go-gl/glfw/v3.1/glfw"
)

const (
	OpenGlVerMajor = 3
	OpenGLVerMinor = 3
)

type Game struct {
	Width      int32
	Height     int32
	Fullscreen bool
	Resizable  bool
	Title      string
	Scene      Scene
}

type Scene interface {
	Setup()
	Draw(dt float32)
	Update(dt float32)
}

func Run(g *Game) error {
	if g.Scene == nil {
		return fmt.Errorf("Scene property of given Game struct is empty")
	}

	// GLFW event handling must run on the main OS thread
	runtime.LockOSThread()
	if err := glfw.Init(); err != nil {
		return fmt.Errorf("glfw.Init failed: %s", err)
	}
	defer glfw.Terminate()

	glfw.WindowHint(glfw.ContextVersionMajor, OpenGLVerMinor)
	glfw.WindowHint(glfw.ContextVersionMinor, OpenGLVerMinor)

	glfw.WindowHint(glfw.Resizable, glfw.False)
	if g.Resizable {
		glfw.WindowHint(glfw.Resizable, glfw.True)
	}

	glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
	glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

	window, err := glfw.CreateWindow(int(g.Width), int(g.Height), g.Title, nil, nil)
	if err != nil {
		return fmt.Errorf("glfw.CreateWindow failed: %s", err)
	}
	window.MakeContextCurrent()

	// Initialize Glow
	if err := gl.Init(); err != nil {
		return fmt.Errorf("gl.Init failed:", err)
	}

	version := gl.GoStr(gl.GetString(gl.VERSION))
	log.Println("gl.Init successful, OpenGL version:", version)

	var previousTime, deltaTime, time float32
	previousTime = float32(glfw.GetTime()) - 1.0/60.0

	g.Scene.Setup()

	for !window.ShouldClose() {
		time = float32(glfw.GetTime())
		deltaTime = time - previousTime

		glfw.PollEvents()

		gl.ClearColor(0.2, 0.3, 0.3, 0.5)
		gl.Clear(gl.COLOR_BUFFER_BIT)

		g.Scene.Update(deltaTime)
		g.Scene.Draw(deltaTime)
		previousTime = time

		window.SwapBuffers()
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"regexp"
	"path/filepath"
	"io/ioutil"
	"net/http"
)

func GetUsernames(installationDir string) []string {
	files, err := ioutil.ReadDir(installationDir)
	if err != nil {
		panic(err)
	}

	names := make([]string, 0)

	for _, userDir := range files {
		userId := userDir.Name()
		configFile := filepath.Join(installationDir, userId, "config", "localconfig.vdf")

		configBytes, err := ioutil.ReadFile(configFile)
		if err != nil {
			panic(err)
		}
		pattern := regexp.MustCompile(`"PersonaName"\s*"(.+?)"`)
		username := pattern.FindStringSubmatch(string(configBytes))[1]
		names = append(names, username)
	}

	return names
}

const urlFormat = `http://steamcommunity.com/id/%v/games?tab=all`
func GetProfile(username string) string {
	fmt.Println("Processing user", username)
	response, err := http.Get(fmt.Sprintf(urlFormat, username))
	if err != nil {
		panic(err)
	}

	if response.StatusCode != 200 {
		panic("Profile not found, server returned " + response.Status)
	}
	contentBytes, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	if err != nil {
		panic(err)
	}

	return string(contentBytes)
}

type Game struct {
	Id string
	Name string
}

const gamePattern = `\{"appid":\s*(\d+),\s*"name":\s*"(.+?)"`
func GetGames(username string) []Game {
	pattern := regexp.MustCompile(gamePattern)

	games := make([]Game, 0)

	profile := GetProfile(username)
	for _, groups := range pattern.FindAllStringSubmatch(profile, -1) {
		games = append(games, Game{groups[1], groups[2]})
	}

	return games
}

func main() {
	for _, username := range GetUsernames(`C:\Program Files (x86)\Steam\userdata`) {
		for _, game := range GetGames(username) {
			fmt.Println(game.Id, game.Name)
		}
	}
}
<commit_msg>Add DownloadImage function and User struct<commit_after>package main

import (
	"fmt"
	"regexp"
	"path/filepath"
	"io/ioutil"
	"net/http"
)

type User struct {
	Name string
	Dir string
}

func GetUsers(installationDir string) []User {
	files, err := ioutil.ReadDir(installationDir)
	if err != nil {
		panic(err)
	}

	users := make([]User, 0)

	for _, userDir := range files {
		userId := userDir.Name()
		userDir := filepath.Join(installationDir, userId, "config")
		configFile := filepath.Join(userDir, "localconfig.vdf")

		configBytes, err := ioutil.ReadFile(configFile)
		if err != nil {
			panic(err)
		}
		pattern := regexp.MustCompile(`"PersonaName"\s*"(.+?)"`)
		username := pattern.FindStringSubmatch(string(configBytes))[1]
		users = append(users, User{username, userDir})
	}

	return users
}

const urlFormat = `http://steamcommunity.com/id/%v/games?tab=all`
func GetProfile(username string) string {
	response, err := http.Get(fmt.Sprintf(urlFormat, username))
	if err != nil {
		panic(err)
	}

	contentBytes, err := ioutil.ReadAll(response.Body)
	response.Body.Close()
	if err != nil {
		panic(err)
	}

	return string(contentBytes)
}

type Game struct {
	Id string
	Name string
}

const gamePattern = `\{"appid":\s*(\d+),\s*"name":\s*"(.+?)"`
func GetGames(username string) []Game {
	pattern := regexp.MustCompile(gamePattern)

	games := make([]Game, 0)

	profile := GetProfile(username)
	for _, groups := range pattern.FindAllStringSubmatch(profile, -1) {
		games = append(games, Game{groups[1], groups[2]})
	}

	return games
}

const imageUrlFormat = `http://cdn.steampowered.com/v/gfx/apps/STORE_APP_ID_HERE/header.jpg`
func DownloadImage(gameid string, filename string) error {
	response, err := http.Get(imageUrlFormat)
	if err != nil {
		return err
	}

	var imageBytes []byte
	imageBytes, err = ioutil.ReadAll(response.Body)
	response.Body.Close()
	return ioutil.WriteFile(filename, imageBytes, 0666)
}

func main() {
	installationDir := `C:\Program Files (x86)\Steam\userdata`
	for _, user := range GetUsers(installationDir) {
		fmt.Printf("Found user %v. Fetching game list...\n", user.Name)
		games := GetGames(user.Name)
		continue

		fmt.Printf("Found %v games. Download images...\n", len(games))
		for _, game := range games {
			gridDir := filepath.Join(user.Dir, "grid")
			err := DownloadImage(game.Id, gridDir)
			fmt.Println(game.Id, game.Name)
			if err != nil {
				panic(err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	// create 1-dimension array with default value
	var a [5]int
	var b [5]string

	// short assignment
	aa := [5]int{1, 2, 3} // aa[3] ~ aa[9] are 0
	bb := [...]string{"good", "bad"}
	cc := [5]int{1, 2}

	fmt.Println(a, b)
	fmt.Println(aa)
	fmt.Println(bb)
	fmt.Println(cc)

	// create 2-dimension array
	a2 := [2][2]int{}
	//a2 := [2][2]int  // not valid

	fmt.Println("a2 is: ", a2)

	// slice creation
	sl := make([]int, 5)
	//[ ][ ][ ][ ][ ]
	// | sl
	//             | len: 5
	//             | cap: 5
	fmt.Println("sl len:", len(sl), "cap:", cap(sl))
	sl = sl[:3]
	//[ ][ ][ ][ ][ ]
	// | sl
	//       | len: 3
	//             | cap: 5
	fmt.Println("sl len:", len(sl), "cap:", cap(sl))
	sl = sl[2:]
	//[ ][ ][ ][ ][ ]
	//       | sl
	//       | len: 1
	//             | cap: 3
	fmt.Println("sl len:", len(sl), "cap:", cap(sl))
	sl = append(sl, 10)
	fmt.Println("sl len:", len(sl), "cap:", cap(sl))

	sl = append(sl, []int{20, 30, 40}...) // must use three dots
	fmt.Println("sl len:", len(sl), "cap:", cap(sl))

	m := map[string]int{
		"zero":  0,
		"one":   1,
		"two":   2,
		"three": 3,
		"four":  4, // must contain last dot
	}

	fmt.Println(m["zero"])

	m["five"] = 5 // add new item
	fmt.Println(m)

	for i := 0; i < 3; i++ {
		switch i {
		case 0: // do nothing, six not in m
		case 1:
			m["six"] = 6 // add six
		case 2:
			delete(m, "six") //delete six
		}
		if value, ok := m["six"]; ok {
			fmt.Println("Have six, which is", value)
		} else {
			fmt.Println("Not have six")
		}
	}
}
<commit_msg>fix: non-existent key has default value in map<commit_after>package main

import "fmt"

func main() {
	// create 1-dimension array with default value
	var a [5]int
	var b [5]string

	// short assignment
	aa := [5]int{1, 2, 3} // aa[3] ~ aa[9] are 0
	bb := [...]string{"good", "bad"}
	cc := [5]int{1, 2}

	fmt.Println(a, b)
	fmt.Println(aa)
	fmt.Println(bb)
	fmt.Println(cc)

	// create 2-dimension array
	a2 := [2][2]int{}
	//a2 := [2][2]int  // not valid

	fmt.Println("a2 is: ", a2)

	// slice creation
	sl := make([]int, 5)
	//[ ][ ][ ][ ][ ]
	// | sl
	//             | len: 5
	//             | cap: 5
	fmt.Println("sl len:", len(sl), "cap:", cap(sl))
	sl = sl[:3]
	//[ ][ ][ ][ ][ ]
	// | sl
	//       | len: 3
	//             | cap: 5
	fmt.Println("sl len:", len(sl), "cap:", cap(sl))
	sl = sl[2:]
	//[ ][ ][ ][ ][ ]
	//       | sl
	//       | len: 1
	//             | cap: 3
	fmt.Println("sl len:", len(sl), "cap:", cap(sl))
	sl = append(sl, 10)
	fmt.Println("sl len:", len(sl), "cap:", cap(sl))

	sl = append(sl, []int{20, 30, 40}...) // must use three dots
	fmt.Println("sl len:", len(sl), "cap:", cap(sl))

	m := map[string]int{
		"zero":  0,
		"one":   1,
		"two":   2,
		"three": 3,
		"four":  4, // must contain last dot
	}

	fmt.Println(m["zero"])

	m["five"] = 5 // add new item
	fmt.Println(m)

	// non-existent key will return default value
	fmt.Println(m["six"])

	for i := 0; i < 3; i++ {
		switch i {
		case 0: // do nothing, six not in m
		case 1:
			m["six"] = 6 // add six
		case 2:
			delete(m, "six") //delete six
		}
		if value, ok := m["six"]; ok {
			fmt.Println("Have six, which is", value)
		} else {
			fmt.Println("Not have six")
		}
	}
}
<|endoftext|>