<commit_before>// Copyright 2013 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package main

import (
	"fmt"
	"github.com/maruel/tulib"
	"github.com/maruel/wi/wi-plugin"
	"github.com/nsf/termbox-go"
	"log"
	"strings"
)

var singleBorder = []rune{'\u2500', '\u2502', '\u250D', '\u2510', '\u2514', '\u2518'}
var doubleBorder = []rune{'\u2550', '\u2551', '\u2554', '\u2557', '\u255a', '\u255d'}

func isEqual(lhs tulib.Rect, rhs tulib.Rect) bool {
	return lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Width == rhs.Width && lhs.Height == rhs.Height
}

type drawnBorder int

const (
	drawnBorderNone drawnBorder = iota
	drawnBorderLeft
	drawnBorderRight
	drawnBorderTop
	drawnBorderBottom
	drawnBorderAll
)

// window implements wi.Window. It keeps its own buffer of its display.
type window struct {
	parent          *window
	childrenWindows []*window
	windowBuffer    tulib.Buffer // includes the border
	rect            tulib.Rect   // Window Rect as described in wi.Window.Rect().
	clientAreaRect  tulib.Rect   // Usable area within the Window, the part not obscured by borders.
	viewRect        tulib.Rect   // Window View Rect, which is the client area not used by childrenWindows.
	view            wi.View
	docking         wi.DockingType
	border          wi.BorderType
	effectiveBorder drawnBorder       // effectiveBorder automatically collapses borders when the Window Rect is too small and is based on docking.
	fg              termbox.Attribute // Default text color, to be used in borders.
	bg              termbox.Attribute // Default background color, to be used in borders
}

func (w *window) String() string {
	return fmt.Sprintf("Window(%s, %v)", w.View().Title(), w.Rect())
}

// Returns a string representing the tree.
func (w *window) Tree() string {
	// Not the most performant implementation but does the job.
	out := w.String() + "\n"
	for _, child := range w.childrenWindows {
		for _, line := range strings.Split(child.Tree(), "\n") {
			if line != "" {
				out += ("  " + line + "\n")
			}
		}
	}
	return out
}

func (w *window) Parent() wi.Window {
	// TODO(maruel): Understand why this is necessary at all.
	if w.parent != nil {
		return w.parent
	}
	return nil
}

func (w *window) ChildrenWindows() []wi.Window {
	out := make([]wi.Window, len(w.childrenWindows))
	for i, v := range w.childrenWindows {
		out[i] = v
	}
	return out
}

func (w *window) NewChildWindow(view wi.View, docking wi.DockingType) wi.Window {
	log.Printf("Window(%s).NewChildWindow(%s, %s)", w.View().Title(), view.Title(), docking)
	for _, child := range w.childrenWindows {
		if child.Docking() == docking {
			panic("TODO(maruel): Likely not a panic, maybe a fallback?")
			return nil
		}
	}
	child := makeWindow(w, view, docking)
	w.childrenWindows = append(w.childrenWindows, child)
	w.resizeChildren()
	return child
}

func (w *window) Remove(child wi.Window) {
	for i, v := range w.childrenWindows {
		if v == child {
			copy(w.childrenWindows[i:], w.childrenWindows[i+1:])
			w.childrenWindows[len(w.childrenWindows)-1] = nil
			w.childrenWindows = w.childrenWindows[:len(w.childrenWindows)-1]
			return
		}
	}
	panic("Trying to remove a non-child Window")
}

func (w *window) Rect() tulib.Rect {
	return w.rect
}

func (w *window) SetRect(rect tulib.Rect) {
	// SetRect() recreates the buffer and immediately draws the borders.
	if !isEqual(w.rect, rect) {
		w.rect = rect
		// Internal consistency check.
		if w.parent != nil {
			if !w.rect.FitsIn(w.parent.clientAreaRect) {
				panic(fmt.Sprintf("Child %v doesn't fit parent's client area %v: %v; %v", w, w.parent, w.parent.clientAreaRect, w.rect.Intersection(w.parent.clientAreaRect)))
			}
		}

		w.windowBuffer = tulib.NewBuffer(w.rect.Width, w.rect.Height)
		w.updateBorder()
	}
	// Still flow the call through children Window, so DockingFloating are
	// properly updated.
	w.resizeChildren()
}

// calculateEffectiveBorder calculates window.effectiveBorder.
func calculateEffectiveBorder(r tulib.Rect, d wi.DockingType) drawnBorder {
	switch d {
	case wi.DockingFill:
		return drawnBorderNone

	case wi.DockingFloating:
		if r.Width >= 5 && r.Height >= 3 {
			return drawnBorderAll
		}
		return drawnBorderNone

	case wi.DockingLeft:
		if r.Width > 1 && r.Height > 0 {
			return drawnBorderRight
		}
		return drawnBorderNone

	case wi.DockingRight:
		if r.Width > 1 && r.Height > 0 {
			return drawnBorderLeft
		}
		return drawnBorderNone

	case wi.DockingTop:
		if r.Height > 1 && r.Width > 0 {
			return drawnBorderBottom
		}
		return drawnBorderNone

	case wi.DockingBottom:
		if r.Height > 1 && r.Width > 0 {
			return drawnBorderTop
		}
		return drawnBorderNone

	default:
		panic("Unknown DockingType")
	}
}

// resizeChildren() resizes all the children Window.
func (w *window) resizeChildren() {
	log.Printf("%s.resizeChildren()", w)
	// When borders are used, w.clientAreaRect.X and .Y are likely 1.
	remaining := w.clientAreaRect
	var fill wi.Window
	for _, child := range w.childrenWindows {
		switch child.Docking() {
		case wi.DockingFill:
			fill = child

		case wi.DockingFloating:
			// Floating uses its own thing.
			child.SetRect(child.Rect())

		case wi.DockingLeft:
			width, _ := child.View().NaturalSize()
			if width >= remaining.Width {
				width = remaining.Width
			} else if child.border != wi.BorderNone {
				width += 1
			}
			tmp := remaining
			tmp.Width = width
			remaining.X += width
			remaining.Width -= width
			child.SetRect(tmp)

		case wi.DockingRight:
			width, _ := child.View().NaturalSize()
			if width >= remaining.Width {
				width = remaining.Width
			} else if child.border != wi.BorderNone {
				width += 1
			}
			tmp := remaining
			tmp.X += (remaining.Width - width)
			tmp.Width = width
			remaining.Width -= width
			child.SetRect(tmp)

		case wi.DockingTop:
			_, height := child.View().NaturalSize()
			if height >= remaining.Height {
				height = remaining.Height
			} else if child.border != wi.BorderNone {
				height += 1
			}
			tmp := remaining
			tmp.Height = height
			remaining.Y += height
			remaining.Height -= height
			child.SetRect(tmp)

		case wi.DockingBottom:
			_, height := child.View().NaturalSize()
			if height >= remaining.Height {
				height = remaining.Height
			} else if child.border != wi.BorderNone {
				height += 1
			}
			tmp := remaining
			tmp.Y += (remaining.Height - height)
			tmp.Height = height
			remaining.Height -= height
			child.SetRect(tmp)

		default:
			panic("Fill me")
		}
	}
	if fill != nil {
		fill.SetRect(remaining)
		w.viewRect.X = 0
		w.viewRect.Y = 0
		w.viewRect.Width = 0
		w.viewRect.Height = 0
		w.view.SetSize(0, 0)
	} else {
		w.viewRect = remaining
		w.view.SetSize(w.viewRect.Width, w.viewRect.Height)
	}
}

func (w *window) Buffer() *tulib.Buffer {
	if w.viewRect.Width != 0 && w.viewRect.Height != 0 {
		w.windowBuffer.Blit(w.viewRect, 0, 0, w.view.Buffer())
	}
	return &w.windowBuffer
}

func (w *window) Docking() wi.DockingType {
	return w.docking
}

func (w *window) SetDocking(docking wi.DockingType) {
	if w.docking != docking {
		w.docking = docking
	}
}

func (w *window) SetView(view wi.View) {
	panic("To test")
	if view != w.view {
		w.view = view
		w.windowBuffer.Fill(w.viewRect, w.cell(' '))
	}
}

// updateBorder calculates w.effectiveBorder, w.clientAreaRect and draws the
// borders right away in the Window's buffer.
//
// It's called by SetRect() and will be called by SetBorder (if ever
// implemented).
func (w *window) updateBorder() {
	if w.border == wi.BorderNone {
		w.effectiveBorder = drawnBorderNone
	} else {
		w.effectiveBorder = calculateEffectiveBorder(w.rect, w.docking)
	}

	s := doubleBorder
	if w.border == wi.BorderSingle {
		s = singleBorder
	}

	switch w.effectiveBorder {
	case drawnBorderNone:
		w.clientAreaRect = tulib.Rect{0, 0, w.rect.Width, w.rect.Height}

	case drawnBorderLeft:
		w.clientAreaRect = tulib.Rect{1, 0, w.rect.Width - 1, w.rect.Height}
		w.windowBuffer.Fill(tulib.Rect{0, 0, 1, w.rect.Height}, w.cell(s[1]))

	case drawnBorderRight:
		w.clientAreaRect = tulib.Rect{0, 0, w.rect.Width - 1, w.rect.Height}
		w.windowBuffer.Fill(tulib.Rect{w.rect.Width - 1, 1, 0, w.rect.Height}, w.cell(s[1]))

	case drawnBorderTop:
		w.clientAreaRect = tulib.Rect{0, 1, w.rect.Width, w.rect.Height - 1}
		w.windowBuffer.Fill(tulib.Rect{0, 0, w.rect.Width, 1}, w.cell(s[0]))

	case drawnBorderBottom:
		w.clientAreaRect = tulib.Rect{0, 0, w.rect.Width, w.rect.Height - 1}
		w.windowBuffer.Fill(tulib.Rect{0, w.rect.Height - 1, w.rect.Width, 1}, w.cell(s[0]))

	case drawnBorderAll:
		w.clientAreaRect = tulib.Rect{1, 1, w.rect.Width - 2, w.rect.Height - 2}
		// Corners.
		w.windowBuffer.Set(0, 0, w.cell(s[2]))
		w.windowBuffer.Set(0, w.rect.Height-1, w.cell(s[4]))
		w.windowBuffer.Set(w.rect.Width-1, 0, w.cell(s[3]))
		w.windowBuffer.Set(w.rect.Width-1, w.rect.Height-1, w.cell(s[5]))
		// Lines.
		w.windowBuffer.Fill(tulib.Rect{1, 0, w.rect.Width - 2, 1}, w.cell(s[0]))
		w.windowBuffer.Fill(tulib.Rect{1, w.rect.Height - 1, w.rect.Width - 2, w.rect.Height - 1}, w.cell(s[0]))
		w.windowBuffer.Fill(tulib.Rect{0, 1, 1, w.rect.Height - 2}, w.cell(s[1]))
		w.windowBuffer.Fill(tulib.Rect{w.rect.Width - 1, 1, w.rect.Width - 1, w.rect.Height - 2}, w.cell(s[1]))

	default:
		panic("Unknown drawnBorder")
	}

	if w.clientAreaRect.Width < 0 {
		panic("Fix this case")
		w.clientAreaRect.Width = 0
	}
	if w.clientAreaRect.Height < 0 {
		panic("Fix this case")
		w.clientAreaRect.Height = 0
	}
}

func (w *window) cell(r rune) termbox.Cell {
	return termbox.Cell{r, w.fg, w.bg}
}

func (w *window) View() wi.View {
	return w.view
}

func makeWindow(parent *window, view wi.View, docking wi.DockingType) *window {
	return &window{
		parent:  parent,
		view:    view,
		docking: docking,
		//border:  wi.BorderNone,
		border: wi.BorderDouble,
		fg:     termbox.ColorWhite,
		bg:     termbox.ColorBlack,
	}
}

func drawRecurse(w *window, offsetX, offsetY int, out *tulib.Buffer) {
	log.Printf("drawRecurse(%s, %d, %d); %v", w.View().Title(), offsetX, offsetY, w.Rect())
	if w.Docking() == wi.DockingFloating {
		// Floating Window are relative to the screen, not the parent Window.
		offsetX = 0
		offsetY = 0
	}
	// TODO(maruel): Only draw the non-occuled frames!
	dest := w.Rect()
	dest.X += offsetX
	dest.Y += offsetY
	out.Blit(dest, 0, 0, w.Buffer())
	for _, child := range w.childrenWindows {
		drawRecurse(child, dest.X, dest.Y, out)
	}
}
<commit_msg>Fixed drawnBorderRight<commit_after>// Copyright 2013 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package main

import (
	"fmt"
	"github.com/maruel/tulib"
	"github.com/maruel/wi/wi-plugin"
	"github.com/nsf/termbox-go"
	"log"
	"strings"
)

var singleBorder = []rune{'\u2500', '\u2502', '\u250D', '\u2510', '\u2514', '\u2518'}
var doubleBorder = []rune{'\u2550', '\u2551', '\u2554', '\u2557', '\u255a', '\u255d'}

func isEqual(lhs tulib.Rect, rhs tulib.Rect) bool {
	return lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Width == rhs.Width && lhs.Height == rhs.Height
}

type drawnBorder int

const (
	drawnBorderNone drawnBorder = iota
	drawnBorderLeft
	drawnBorderRight
	drawnBorderTop
	drawnBorderBottom
	drawnBorderAll
)

// window implements wi.Window. It keeps its own buffer of its display.
type window struct {
	parent          *window
	childrenWindows []*window
	windowBuffer    tulib.Buffer // includes the border
	rect            tulib.Rect   // Window Rect as described in wi.Window.Rect().
	clientAreaRect  tulib.Rect   // Usable area within the Window, the part not obscured by borders.
	viewRect        tulib.Rect   // Window View Rect, which is the client area not used by childrenWindows.
	view            wi.View
	docking         wi.DockingType
	border          wi.BorderType
	effectiveBorder drawnBorder       // effectiveBorder automatically collapses borders when the Window Rect is too small and is based on docking.
	fg              termbox.Attribute // Default text color, to be used in borders.
	bg              termbox.Attribute // Default background color, to be used in borders
}

func (w *window) String() string {
	return fmt.Sprintf("Window(%s, %v)", w.View().Title(), w.Rect())
}

// Returns a string representing the tree.
func (w *window) Tree() string {
	// Not the most performant implementation but does the job.
	out := w.String() + "\n"
	for _, child := range w.childrenWindows {
		for _, line := range strings.Split(child.Tree(), "\n") {
			if line != "" {
				out += ("  " + line + "\n")
			}
		}
	}
	return out
}

func (w *window) Parent() wi.Window {
	// TODO(maruel): Understand why this is necessary at all.
	if w.parent != nil {
		return w.parent
	}
	return nil
}

func (w *window) ChildrenWindows() []wi.Window {
	out := make([]wi.Window, len(w.childrenWindows))
	for i, v := range w.childrenWindows {
		out[i] = v
	}
	return out
}

func (w *window) NewChildWindow(view wi.View, docking wi.DockingType) wi.Window {
	log.Printf("Window(%s).NewChildWindow(%s, %s)", w.View().Title(), view.Title(), docking)
	for _, child := range w.childrenWindows {
		if child.Docking() == docking {
			panic("TODO(maruel): Likely not a panic, maybe a fallback?")
			return nil
		}
	}
	child := makeWindow(w, view, docking)
	w.childrenWindows = append(w.childrenWindows, child)
	w.resizeChildren()
	return child
}

func (w *window) Remove(child wi.Window) {
	for i, v := range w.childrenWindows {
		if v == child {
			copy(w.childrenWindows[i:], w.childrenWindows[i+1:])
			w.childrenWindows[len(w.childrenWindows)-1] = nil
			w.childrenWindows = w.childrenWindows[:len(w.childrenWindows)-1]
			return
		}
	}
	panic("Trying to remove a non-child Window")
}

func (w *window) Rect() tulib.Rect {
	return w.rect
}

func (w *window) SetRect(rect tulib.Rect) {
	// SetRect() recreates the buffer and immediately draws the borders.
	if !isEqual(w.rect, rect) {
		w.rect = rect
		// Internal consistency check.
		if w.parent != nil {
			if !w.rect.FitsIn(w.parent.clientAreaRect) {
				panic(fmt.Sprintf("Child %v doesn't fit parent's client area %v: %v; %v", w, w.parent, w.parent.clientAreaRect, w.rect.Intersection(w.parent.clientAreaRect)))
			}
		}

		w.windowBuffer = tulib.NewBuffer(w.rect.Width, w.rect.Height)
		w.updateBorder()
	}
	// Still flow the call through children Window, so DockingFloating are
	// properly updated.
	w.resizeChildren()
}

// calculateEffectiveBorder calculates window.effectiveBorder.
func calculateEffectiveBorder(r tulib.Rect, d wi.DockingType) drawnBorder {
	switch d {
	case wi.DockingFill:
		return drawnBorderNone

	case wi.DockingFloating:
		if r.Width >= 5 && r.Height >= 3 {
			return drawnBorderAll
		}
		return drawnBorderNone

	case wi.DockingLeft:
		if r.Width > 1 && r.Height > 0 {
			return drawnBorderRight
		}
		return drawnBorderNone

	case wi.DockingRight:
		if r.Width > 1 && r.Height > 0 {
			return drawnBorderLeft
		}
		return drawnBorderNone

	case wi.DockingTop:
		if r.Height > 1 && r.Width > 0 {
			return drawnBorderBottom
		}
		return drawnBorderNone

	case wi.DockingBottom:
		if r.Height > 1 && r.Width > 0 {
			return drawnBorderTop
		}
		return drawnBorderNone

	default:
		panic("Unknown DockingType")
	}
}

// resizeChildren() resizes all the children Window.
func (w *window) resizeChildren() {
	log.Printf("%s.resizeChildren()", w)
	// When borders are used, w.clientAreaRect.X and .Y are likely 1.
	remaining := w.clientAreaRect
	var fill wi.Window
	for _, child := range w.childrenWindows {
		switch child.Docking() {
		case wi.DockingFill:
			fill = child

		case wi.DockingFloating:
			// Floating uses its own thing.
			child.SetRect(child.Rect())

		case wi.DockingLeft:
			width, _ := child.View().NaturalSize()
			if width >= remaining.Width {
				width = remaining.Width
			} else if child.border != wi.BorderNone {
				width += 1
			}
			tmp := remaining
			tmp.Width = width
			remaining.X += width
			remaining.Width -= width
			child.SetRect(tmp)

		case wi.DockingRight:
			width, _ := child.View().NaturalSize()
			if width >= remaining.Width {
				width = remaining.Width
			} else if child.border != wi.BorderNone {
				width += 1
			}
			tmp := remaining
			tmp.X += (remaining.Width - width)
			tmp.Width = width
			remaining.Width -= width
			child.SetRect(tmp)

		case wi.DockingTop:
			_, height := child.View().NaturalSize()
			if height >= remaining.Height {
				height = remaining.Height
			} else if child.border != wi.BorderNone {
				height += 1
			}
			tmp := remaining
			tmp.Height = height
			remaining.Y += height
			remaining.Height -= height
			child.SetRect(tmp)

		case wi.DockingBottom:
			_, height := child.View().NaturalSize()
			if height >= remaining.Height {
				height = remaining.Height
			} else if child.border != wi.BorderNone {
				height += 1
			}
			tmp := remaining
			tmp.Y += (remaining.Height - height)
			tmp.Height = height
			remaining.Height -= height
			child.SetRect(tmp)

		default:
			panic("Fill me")
		}
	}
	if fill != nil {
		fill.SetRect(remaining)
		w.viewRect.X = 0
		w.viewRect.Y = 0
		w.viewRect.Width = 0
		w.viewRect.Height = 0
		w.view.SetSize(0, 0)
	} else {
		w.viewRect = remaining
		w.view.SetSize(w.viewRect.Width, w.viewRect.Height)
	}
}

func (w *window) Buffer() *tulib.Buffer {
	if w.viewRect.Width != 0 && w.viewRect.Height != 0 {
		w.windowBuffer.Blit(w.viewRect, 0, 0, w.view.Buffer())
	}
	return &w.windowBuffer
}

func (w *window) Docking() wi.DockingType {
	return w.docking
}

func (w *window) SetDocking(docking wi.DockingType) {
	if w.docking != docking {
		w.docking = docking
	}
}

func (w *window) SetView(view wi.View) {
	panic("To test")
	if view != w.view {
		w.view = view
		w.windowBuffer.Fill(w.viewRect, w.cell(' '))
	}
}

// updateBorder calculates w.effectiveBorder, w.clientAreaRect and draws the
// borders right away in the Window's buffer.
//
// It's called by SetRect() and will be called by SetBorder (if ever
// implemented).
func (w *window) updateBorder() {
	if w.border == wi.BorderNone {
		w.effectiveBorder = drawnBorderNone
	} else {
		w.effectiveBorder = calculateEffectiveBorder(w.rect, w.docking)
	}

	s := doubleBorder
	if w.border == wi.BorderSingle {
		s = singleBorder
	}

	switch w.effectiveBorder {
	case drawnBorderNone:
		w.clientAreaRect = tulib.Rect{0, 0, w.rect.Width, w.rect.Height}

	case drawnBorderLeft:
		w.clientAreaRect = tulib.Rect{1, 0, w.rect.Width - 1, w.rect.Height}
		w.windowBuffer.Fill(tulib.Rect{0, 0, 1, w.rect.Height}, w.cell(s[1]))

	case drawnBorderRight:
		w.clientAreaRect = tulib.Rect{0, 0, w.rect.Width - 1, w.rect.Height}
		w.windowBuffer.Fill(tulib.Rect{w.rect.Width - 1, 0, 1, w.rect.Height}, w.cell(s[1]))

	case drawnBorderTop:
		w.clientAreaRect = tulib.Rect{0, 1, w.rect.Width, w.rect.Height - 1}
		w.windowBuffer.Fill(tulib.Rect{0, 0, w.rect.Width, 1}, w.cell(s[0]))

	case drawnBorderBottom:
		w.clientAreaRect = tulib.Rect{0, 0, w.rect.Width, w.rect.Height - 1}
		w.windowBuffer.Fill(tulib.Rect{0, w.rect.Height - 1, w.rect.Width, 1}, w.cell(s[0]))

	case drawnBorderAll:
		w.clientAreaRect = tulib.Rect{1, 1, w.rect.Width - 2, w.rect.Height - 2}
		// Corners.
		w.windowBuffer.Set(0, 0, w.cell(s[2]))
		w.windowBuffer.Set(0, w.rect.Height-1, w.cell(s[4]))
		w.windowBuffer.Set(w.rect.Width-1, 0, w.cell(s[3]))
		w.windowBuffer.Set(w.rect.Width-1, w.rect.Height-1, w.cell(s[5]))
		// Lines.
		w.windowBuffer.Fill(tulib.Rect{1, 0, w.rect.Width - 2, 1}, w.cell(s[0]))
		w.windowBuffer.Fill(tulib.Rect{1, w.rect.Height - 1, w.rect.Width - 2, w.rect.Height - 1}, w.cell(s[0]))
		w.windowBuffer.Fill(tulib.Rect{0, 1, 1, w.rect.Height - 2}, w.cell(s[1]))
		w.windowBuffer.Fill(tulib.Rect{w.rect.Width - 1, 1, w.rect.Width - 1, w.rect.Height - 2}, w.cell(s[1]))

	default:
		panic("Unknown drawnBorder")
	}

	if w.clientAreaRect.Width < 0 {
		panic("Fix this case")
		w.clientAreaRect.Width = 0
	}
	if w.clientAreaRect.Height < 0 {
		panic("Fix this case")
		w.clientAreaRect.Height = 0
	}
}

func (w *window) cell(r rune) termbox.Cell {
	return termbox.Cell{r, w.fg, w.bg}
}

func (w *window) View() wi.View {
	return w.view
}

func makeWindow(parent *window, view wi.View, docking wi.DockingType) *window {
	return &window{
		parent:  parent,
		view:    view,
		docking: docking,
		//border:  wi.BorderNone,
		border: wi.BorderDouble,
		fg:     termbox.ColorWhite,
		bg:     termbox.ColorBlack,
	}
}

func drawRecurse(w *window, offsetX, offsetY int, out *tulib.Buffer) {
	log.Printf("drawRecurse(%s, %d, %d); %v", w.View().Title(), offsetX, offsetY, w.Rect())
	if w.Docking() == wi.DockingFloating {
		// Floating Window are relative to the screen, not the parent Window.
		offsetX = 0
		offsetY = 0
	}
	// TODO(maruel): Only draw the non-occuled frames!
	dest := w.Rect()
	dest.X += offsetX
	dest.Y += offsetY
	out.Blit(dest, 0, 0, w.Buffer())
	for _, child := range w.childrenWindows {
		drawRecurse(child, dest.X, dest.Y, out)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"net/http"
	"strings"
	"time"

	"git.zxq.co/ripple/rippleapi/common"
	"git.zxq.co/x/rs"

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

var allowedPaths = [...]string{
	"/logout",
	"/2fa_gateway",
	"/2fa_gateway/verify",
	"/2fa_gateway/clear",
}

// middleware to deny all requests to non-allowed pages
func twoFALock(c *gin.Context) {
	// check it's not a static file
	if len(c.Request.URL.Path) >= 8 && c.Request.URL.Path[:8] == "/static/" {
		c.Next()
		return
	}

	ctx := getContext(c)
	if ctx.User.ID == 0 {
		c.Next()
		return
	}

	sess := getSession(c)
	if v, _ := sess.Get("2fa_must_validate").(bool); !v {
		// * check 2fa is enabled.
		//   if it is,
		//   * check whether the current ip is found in the database.
		//     if it is, move on and show the page.
		//     if it isn't, set 2fa_must_validate
		//   if it isn't, move on.
		enabled := is2faEnabled(ctx.User.ID)
		if enabled {
			err := db.QueryRow("SELECT 1 FROM ip_user WHERE userid = ? AND ip = ? LIMIT 1", ctx.User.ID, clientIP(c)).Scan(new(int))
			if err != sql.ErrNoRows {
				c.Next()
				return
			}
			sess.Set("2fa_must_validate", true)
		} else {
			c.Next()
			return
		}
	}

	// check it's one of the few approved paths
	for _, a := range allowedPaths {
		if a == c.Request.URL.Path {
			sess.Save()
			c.Next()
			return
		}
	}
	addMessage(c, warningMessage{"You need to complete the 2fa challenge first."})
	sess.Save()
	c.Redirect(302, "/2fa_gateway")
	c.Abort()
}

// is2faEnabled checks 2fa is enabled for an user.
func is2faEnabled(user int) bool {
	return db.QueryRow("SELECT 1 FROM 2fa_telegram WHERE userid = ?", user).Scan(new(int)) != sql.ErrNoRows
}

func tfaGateway(c *gin.Context) {
	sess := getSession(c)

	// check 2fa hasn't been disabled
	i, _ := sess.Get("userid").(int)
	if i == 0 {
		c.Redirect(302, "/")
	}
	if !is2faEnabled(i) {
		sess.Delete("2fa_must_validate")
		c.Redirect(302, "/")
		return
	}
	// check previous 2fa thing is still valid
	err := db.QueryRow("SELECT 1 FROM 2fa WHERE userid = ? AND ip = ? AND expire > ?",
		i, clientIP(c), time.Now().Unix()).Scan(new(int))
	if err != nil {
		db.Exec("INSERT INTO 2fa(userid, token, ip, expire, sent) VALUES (?, ?, ?, ?, 0);",
			i, strings.ToUpper(rs.String(8)), clientIP(c), time.Now().Add(time.Hour).Unix())
		http.Get("http://127.0.0.1:8888/update")
	}

	resp(c, 200, "2fa_gateway.html", &baseTemplateData{
		TitleBar:  "Two Factor Authentication",
		KyutGrill: "2fa.jpg",
	})
}

func clientIP(c *gin.Context) string {
	ff := c.Request.Header.Get("CF-Connecting-IP")
	if ff != "" {
		return ff
	}
	return c.ClientIP()
}

func clear2fa(c *gin.Context) {
	// basically deletes from db 2fa tokens, so that it gets regenerated when user hits gateway page
	sess := getSession(c)
	i, _ := sess.Get("userid").(int)
	if i == 0 {
		c.Redirect(302, "/")
	}
	db.Exec("DELETE FROM 2fa WHERE userid = ? AND ip = ?", i, clientIP(c))
	addMessage(c, successMessage{"A new code has been generated and sent to you through Telegram."})
	sess.Save()
	c.Redirect(302, "/2fa_gateway")
}

func verify2fa(c *gin.Context) {
	sess := getSession(c)
	i, _ := sess.Get("userid").(int)
	if i == 0 {
		c.Redirect(302, "/")
	}
	var id int
	var expire common.UnixTimestamp
	err := db.QueryRow("SELECT id, expire FROM 2fa WHERE userid = ? AND ip = ? AND token = ?", i, clientIP(c), strings.ToUpper(c.Query("token"))).Scan(&id, &expire)
	if err == sql.ErrNoRows {
		c.String(200, "1")
		return
	}
	if time.Now().After(time.Time(expire)) {
		c.String(200, "1")
		db.Exec("INSERT INTO 2fa(userid, token, ip, expire, sent) VALUES (?, ?, ?, ?, 0);",
			i, strings.ToUpper(rs.String(8)), clientIP(c), time.Now().Add(time.Hour).Unix())
		http.Get("http://127.0.0.1:8888/update")
		return
	}
	s, err := generateToken(i, c)
	if err != nil {
		resp500(c)
		c.Error(err)
		return
	}
	sess.Set("token", s)
	logIP(c, i)

	var country string
	db.Get(&country, "SELECT country FROM users_stats WHERE id = ?", i)
	if country == "XX" {
		setCountry(c, i)
	}

	addMessage(c, successMessage{"You've been successfully logged in."})
	sess.Delete("2fa_must_validate")
	sess.Save()
	db.Exec("DELETE FROM 2fa WHERE id = ?", id)
	c.String(200, "0")
}

// deletes expired 2fa confirmation tokens. gets current confirmation token.
// if it does not exist, generates one.
func get2faConfirmationToken(user int) (token string) {
	db.Exec("DELETE FROM 2fa_confirmation WHERE expire < ?", time.Now().Unix())
	db.Get(&token, "SELECT token FROM 2fa_confirmation WHERE userid = ? LIMIT 1", user)
	if token != "" {
		return
	}
	token = rs.String(32)
	db.Exec("INSERT INTO 2fa_confirmation (userid, token, expire) VALUES (?, ?, ?)",
		user, token, time.Now().Add(time.Hour).Unix())
	return
}

func disable2fa(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	s := getSession(c)
	var m message
	defer func() {
		addMessage(c, m)
		s.Save()
		c.Redirect(302, "/settings/2fa")
	}()

	exist := csrfExist(ctx.User.ID, c.PostForm("csrf"))
	if !exist {
		m = errorMessage{"Your session has expired. Please try redoing what you were trying to do."}
		return
	}

	db.Exec("DELETE FROM 2fa_telegram WHERE userid = ?", ctx.User.ID)
	m = successMessage{"2fa disabled successfully."}
}
<commit_msg>Add favicon to 2fa allowed paths<commit_after>package main

import (
	"database/sql"
	"net/http"
	"strings"
	"time"

	"git.zxq.co/ripple/rippleapi/common"
	"git.zxq.co/x/rs"

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

var allowedPaths = [...]string{
	"/logout",
	"/2fa_gateway",
	"/2fa_gateway/verify",
	"/2fa_gateway/clear",
	"/favicon.ico",
}

// middleware to deny all requests to non-allowed pages
func twoFALock(c *gin.Context) {
	// check it's not a static file
	if len(c.Request.URL.Path) >= 8 && c.Request.URL.Path[:8] == "/static/" {
		c.Next()
		return
	}

	ctx := getContext(c)
	if ctx.User.ID == 0 {
		c.Next()
		return
	}

	sess := getSession(c)
	if v, _ := sess.Get("2fa_must_validate").(bool); !v {
		// * check 2fa is enabled.
		//   if it is,
		//   * check whether the current ip is found in the database.
		//     if it is, move on and show the page.
		//     if it isn't, set 2fa_must_validate
		//   if it isn't, move on.
		enabled := is2faEnabled(ctx.User.ID)
		if enabled {
			err := db.QueryRow("SELECT 1 FROM ip_user WHERE userid = ? AND ip = ? LIMIT 1", ctx.User.ID, clientIP(c)).Scan(new(int))
			if err != sql.ErrNoRows {
				c.Next()
				return
			}
			sess.Set("2fa_must_validate", true)
		} else {
			c.Next()
			return
		}
	}

	// check it's one of the few approved paths
	for _, a := range allowedPaths {
		if a == c.Request.URL.Path {
			sess.Save()
			c.Next()
			return
		}
	}
	addMessage(c, warningMessage{"You need to complete the 2fa challenge first."})
	sess.Save()
	c.Redirect(302, "/2fa_gateway")
	c.Abort()
}

// is2faEnabled checks 2fa is enabled for an user.
func is2faEnabled(user int) bool {
	return db.QueryRow("SELECT 1 FROM 2fa_telegram WHERE userid = ?", user).Scan(new(int)) != sql.ErrNoRows
}

func tfaGateway(c *gin.Context) {
	sess := getSession(c)

	// check 2fa hasn't been disabled
	i, _ := sess.Get("userid").(int)
	if i == 0 {
		c.Redirect(302, "/")
	}
	if !is2faEnabled(i) {
		sess.Delete("2fa_must_validate")
		c.Redirect(302, "/")
		return
	}
	// check previous 2fa thing is still valid
	err := db.QueryRow("SELECT 1 FROM 2fa WHERE userid = ? AND ip = ? AND expire > ?",
		i, clientIP(c), time.Now().Unix()).Scan(new(int))
	if err != nil {
		db.Exec("INSERT INTO 2fa(userid, token, ip, expire, sent) VALUES (?, ?, ?, ?, 0);",
			i, strings.ToUpper(rs.String(8)), clientIP(c), time.Now().Add(time.Hour).Unix())
		http.Get("http://127.0.0.1:8888/update")
	}

	resp(c, 200, "2fa_gateway.html", &baseTemplateData{
		TitleBar:  "Two Factor Authentication",
		KyutGrill: "2fa.jpg",
	})
}

func clientIP(c *gin.Context) string {
	ff := c.Request.Header.Get("CF-Connecting-IP")
	if ff != "" {
		return ff
	}
	return c.ClientIP()
}

func clear2fa(c *gin.Context) {
	// basically deletes from db 2fa tokens, so that it gets regenerated when user hits gateway page
	sess := getSession(c)
	i, _ := sess.Get("userid").(int)
	if i == 0 {
		c.Redirect(302, "/")
	}
	db.Exec("DELETE FROM 2fa WHERE userid = ? AND ip = ?", i, clientIP(c))
	addMessage(c, successMessage{"A new code has been generated and sent to you through Telegram."})
	sess.Save()
	c.Redirect(302, "/2fa_gateway")
}

func verify2fa(c *gin.Context) {
	sess := getSession(c)
	i, _ := sess.Get("userid").(int)
	if i == 0 {
		c.Redirect(302, "/")
	}
	var id int
	var expire common.UnixTimestamp
	err := db.QueryRow("SELECT id, expire FROM 2fa WHERE userid = ? AND ip = ? AND token = ?", i, clientIP(c), strings.ToUpper(c.Query("token"))).Scan(&id, &expire)
	if err == sql.ErrNoRows {
		c.String(200, "1")
		return
	}
	if time.Now().After(time.Time(expire)) {
		c.String(200, "1")
		db.Exec("INSERT INTO 2fa(userid, token, ip, expire, sent) VALUES (?, ?, ?, ?, 0);",
			i, strings.ToUpper(rs.String(8)), clientIP(c), time.Now().Add(time.Hour).Unix())
		http.Get("http://127.0.0.1:8888/update")
		return
	}
	s, err := generateToken(i, c)
	if err != nil {
		resp500(c)
		c.Error(err)
		return
	}
	sess.Set("token", s)
	logIP(c, i)

	var country string
	db.Get(&country, "SELECT country FROM users_stats WHERE id = ?", i)
	if country == "XX" {
		setCountry(c, i)
	}

	addMessage(c, successMessage{"You've been successfully logged in."})
	sess.Delete("2fa_must_validate")
	sess.Save()
	db.Exec("DELETE FROM 2fa WHERE id = ?", id)
	c.String(200, "0")
}

// deletes expired 2fa confirmation tokens. gets current confirmation token.
// if it does not exist, generates one.
func get2faConfirmationToken(user int) (token string) {
	db.Exec("DELETE FROM 2fa_confirmation WHERE expire < ?", time.Now().Unix())
	db.Get(&token, "SELECT token FROM 2fa_confirmation WHERE userid = ? LIMIT 1", user)
	if token != "" {
		return
	}
	token = rs.String(32)
	db.Exec("INSERT INTO 2fa_confirmation (userid, token, expire) VALUES (?, ?, ?)",
		user, token, time.Now().Add(time.Hour).Unix())
	return
}

func disable2fa(c *gin.Context) {
	ctx := getContext(c)
	if ctx.User.ID == 0 {
		resp403(c)
		return
	}

	s := getSession(c)
	var m message
	defer func() {
		addMessage(c, m)
		s.Save()
		c.Redirect(302, "/settings/2fa")
	}()

	exist := csrfExist(ctx.User.ID, c.PostForm("csrf"))
	if !exist {
		m = errorMessage{"Your session has expired. Please try redoing what you were trying to do."}
		return
	}

	db.Exec("DELETE FROM 2fa_telegram WHERE userid = ?", ctx.User.ID)
	m = successMessage{"2fa disabled successfully."}
}
<|endoftext|>


<commit_before>package ugen

import "math"

type Sin struct {
	UGenBase
	freq float32
	phase float32
	freqChan chan float32
	phaseChan chan float32
	universe *Universe // needs samplerate
	quitchan chan q
}

func init() {
	var _ UGen = &Sin{}
}

func NewSin(f, p float32, u *Universe) *Sin {
	var s Sin
	s.freq = f
	s.phase = p
	s.freqChan = make(chan float32, 1)
	s.phaseChan = make(chan float32, 1)
	s.quitchan = make(chan q)
	s.universe = u
	s.outchans = append(s.outchans, make(chan float32))
	return &s
}

func (s *Sin) Start() error {
	go func() {
		var samplenum int64
		select {
		case f := <- s.freqChan:
			s.freq = f
		case p := <- s.phaseChan:
			s.phase = p
		default:
		}

		y := math.Sin(float64(s.freq) * float64(samplenum) / s.universe.SampleRate + float64(s.phase))
		println("y",y)
		select {
		case <- s.quitchan:
			return
		case s.outchans[0] <- float32(y):
			println("sent fucko")
		}

		samplenum++
	}()
	return nil
}

func (s *Sin) Stop() error {
	s.quitchan <- q{}
	return nil
}<commit_msg>remove swears<commit_after>package ugen

import "math"

type Sin struct {
	UGenBase
	freq float32
	phase float32
	freqChan chan float32
	phaseChan chan float32
	universe *Universe // needs samplerate
	quitchan chan q
}

func init() {
	var _ UGen = &Sin{}
}

func NewSin(f, p float32, u *Universe) *Sin {
	var s Sin
	s.freq = f
	s.phase = p
	s.freqChan = make(chan float32, 1)
	s.phaseChan = make(chan float32, 1)
	s.quitchan = make(chan q)
	s.universe = u
	s.outchans = append(s.outchans, make(chan float32, 1))
	return &s
}

func (s *Sin) Start() error {
	go func() {
		var samplenum int64
		select {
		case f := <- s.freqChan:
			s.freq = f
		case p := <- s.phaseChan:
			s.phase = p
		default:
		}

		y := math.Sin(float64(s.freq) * float64(samplenum) / s.universe.SampleRate + float64(s.phase))
		println("y",y)
		select {
		case <- s.quitchan:
			return
		case s.outchans[0] <- float32(y):
			println("sent")
		}

		samplenum++
	}()
	return nil
}

func (s *Sin) Stop() error {
	s.quitchan <- q{}
	return nil
}<|endoftext|>


<commit_before>/*
MIT License

Copyright (c) 2017 Curtis La Graff

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// pad provides basic functionallity for padding sides of a string.
package pad

import "bytes"

type direction bool

// LeftDir is used to specify text will be padded on the left side.
const LeftDir direction = false

// RightDir is used to specify text will be padded on the right side.
const RightDir direction = true

// Pad is used to pad a given string using the specified character to satisfy the difference between the text length and the length specified.
func Pad(text string, dir direction, length int, char byte) string {
	startLength := len(text)
	diff := length - startLength

	// If no difference exists, return the text as it satisfies the length.
	if diff <= 0 {
		return text
	}

	// Writing to a buffer to improve performance.
	buff := bytes.NewBuffer(nil)
	if dir == RightDir {
		buff.WriteString(text)
	}

	for i := 0; i < diff; i++ {
		buff.WriteByte(char)
	}

	if dir == LeftDir {
		buff.WriteString(text)
	}

	return buff.String()
}

// Left will pad the left of the string with spaces to meet the specified length.
func Left(text string, length int) string {
	return Pad(text, LeftDir, length, ' ')
}

// LeftChar will pad the left of the string with the specified character to meet
// the specified length.
func LeftChar(text string, length int, char byte) string {
	return Pad(text, LeftDir, length, char)
}

// Right will pad the right of the string with spaces to meet the
// specified length.
func Right(text string, length int) string {
	return Pad(text, RightDir, length, ' ')
}

// RIghtChar will pad the right of the string with the specified character to
// meet the specified length.
func RightChar(text string, length int, char byte) string {
	return Pad(text, RightDir, length, char)
}
<commit_msg>fixed capitalization error<commit_after>/*
MIT License

Copyright (c) 2017 Curtis La Graff

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// pad provides basic functionallity for padding sides of a string.
package pad

import "bytes"

type direction bool

// LeftDir is used to specify text will be padded on the left side.
const LeftDir direction = false

// RightDir is used to specify text will be padded on the right side.
const RightDir direction = true

// Pad is used to pad a given string using the specified character to satisfy the difference between the text length and the length specified.
func Pad(text string, dir direction, length int, char byte) string {
	startLength := len(text)
	diff := length - startLength

	// If no difference exists, return the text as it satisfies the length.
	if diff <= 0 {
		return text
	}

	// Writing to a buffer to improve performance.
	buff := bytes.NewBuffer(nil)
	if dir == RightDir {
		buff.WriteString(text)
	}

	for i := 0; i < diff; i++ {
		buff.WriteByte(char)
	}

	if dir == LeftDir {
		buff.WriteString(text)
	}

	return buff.String()
}

// Left will pad the left of the string with spaces to meet the specified length.
func Left(text string, length int) string {
	return Pad(text, LeftDir, length, ' ')
}

// LeftChar will pad the left of the string with the specified character to meet
// the specified length.
func LeftChar(text string, length int, char byte) string {
	return Pad(text, LeftDir, length, char)
}

// Right will pad the right of the string with spaces to meet the
// specified length.
func Right(text string, length int) string {
	return Pad(text, RightDir, length, ' ')
}

// RightChar will pad the right of the string with the specified character to
// meet the specified length.
func RightChar(text string, length int, char byte) string {
	return Pad(text, RightDir, length, char)
}
<|endoftext|>


<commit_before>package host

import (
	"net"
	"sync/atomic"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// rpcSettingsDeprecated is a specifier for a deprecated settings request.
var rpcSettingsDeprecated = types.Specifier{'S', 'e', 't', 't', 'i', 'n', 'g', 's'}

// threadedUpdateHostname periodically runs 'managedLearnHostname', which
// checks if the host's hostname has changed, and makes an updated host
// announcement if so.
//
// TODO: This thread doesn't actually have clean shutdown, because the sleep is
// outside of of the resource lock.
func (h *Host) threadedUpdateHostname() {
	for {
		h.resourceLock.RLock()
		if h.closed {
			// The host is closed, the goroutine can exit.
			h.resourceLock.RUnlock()
			break
		}
		h.managedLearnHostname()
		h.resourceLock.RUnlock()

		// Wait 30 minutes to check again. If the hostname is changing
		// regularly (more than once a week), we want the host to be able to be
		// seen as having 95% uptime. Every minute that the announcement is
		// pointing to the wrong address is a minute of perceived downtime to
		// the renters.
		time.Sleep(time.Minute * 30)
	}
}

// initNetworking performs actions like port forwarding, and gets the host
// established on the network.
func (h *Host) initNetworking(address string) (err error) {
	// Create listener and set address.
	h.listener, err = h.dependencies.listen("tcp", address)
	if err != nil {
		return err
	}
	_, port, err := net.SplitHostPort(h.listener.Addr().String())
	if err != nil {
		return err
	}
	h.mu.Lock()
	h.port = port
	if build.Release == "testing" {
		h.autoAddress = modules.NetAddress(net.JoinHostPort("localhost", h.port))
	}
	h.mu.Unlock()

	// Non-blocking, perform port forwarding and hostname discovery.
	go func() {
		h.resourceLock.RLock()
		defer h.resourceLock.RUnlock()
		if h.closed {
			return
		}

		err := h.managedForwardPort()
		if err != nil {
			h.log.Println("ERROR: failed to forward port:", err)
		}

		// Spin up the hostname checker. The hostname checker should not be
		// spun up until after the port has been forwarded, because it can
		// result in announcements being submitted to the blockchain.
		go h.threadedUpdateHostname()
	}()

	// Launch the listener.
	go h.threadedListen()
	return nil
}

// threadedHandleConn handles an incoming connection to the host, typically an
// RPC.
func (h *Host) threadedHandleConn(conn net.Conn) {
	h.resourceLock.RLock()
	defer h.resourceLock.RUnlock()
	if h.closed {
		return
	}

	// Set an initial duration that is generous, but finite. RPCs can extend
	// this if desired.
	err := conn.SetDeadline(time.Now().Add(5 * time.Minute))
	if err != nil {
		h.log.Println("WARN: could not set deadline on connection:", err)
		return
	}
	defer conn.Close()

	// Read a specifier indicating which action is being called.
	var id types.Specifier
	if err := encoding.ReadObject(conn, &id, 16); err != nil {
		atomic.AddUint64(&h.atomicUnrecognizedCalls, 1)
		h.log.Debugf("WARN: incoming conn %v was malformed: %v", conn.RemoteAddr(), err)
		return
	}

	switch id {
	case modules.RPCDownload:
		atomic.AddUint64(&h.atomicDownloadCalls, 1)
		err = h.managedRPCDownload(conn)
	case modules.RPCRenewContract:
		atomic.AddUint64(&h.atomicRenewCalls, 1)
		err = h.managedRPCRenewContract(conn)
	case modules.RPCFormContract:
		atomic.AddUint64(&h.atomicFormContractCalls, 1)
		err = h.managedRPCFormContract(conn)
	case modules.RPCReviseContract:
		atomic.AddUint64(&h.atomicReviseCalls, 1)
		err = h.managedRPCReviseContract(conn)
	case modules.RPCRecentRevision:
		atomic.AddUint64(&h.atomicRecentRevisionCalls, 1)
		_, _, err = h.managedRPCRecentRevision(conn)
	case modules.RPCSettings:
		atomic.AddUint64(&h.atomicSettingsCalls, 1)
		err = h.managedRPCSettings(conn)
	default:
		h.log.Debugf("WARN: incoming conn %v requested unknown RPC \"%v\"", conn.RemoteAddr(), id)
		if id != rpcSettingsDeprecated {
			// Only mark the call as unrecognized if it is not one of the
			// legacy calls.
			atomic.AddUint64(&h.atomicUnrecognizedCalls, 1)
		}
	}
	if err != nil {
		atomic.AddUint64(&h.atomicErroredCalls, 1)

		// If there have been less than 1000 errored rpcs, print the error
		// message. This is to help developers debug live systems that are
		// running into issues. Ultimately though, this error can be triggered
		// by a malicious actor, and therefore should not be logged except for
		// DEBUG builds.
		erroredCalls := atomic.LoadUint64(&h.atomicErroredCalls)
		if erroredCalls < 1e3 {
			h.log.Printf("WARN: incoming RPC \"%v\" failed: %v", id, err)
		} else {
			h.log.Debugf("WARN: incoming RPC \"%v\" failed: %v", id, err)
		}
	}
}

// listen listens for incoming RPCs and spawns an appropriate handler for each.
//
// TODO: Does not seem like this function ever actually lets go of the resource
// lock.
func (h *Host) threadedListen() {
	h.resourceLock.RLock()
	defer h.resourceLock.RUnlock()
	if h.closed {
		return
	}

	// Receive connections until an error is returned by the listener. When an
	// error is returned, there will be no more calls to receive.
	for {
		// Block until there is a connection to handle.
		conn, err := h.listener.Accept()
		if err != nil {
			return
		}

		// Grab the resource lock before creating a goroutine.
		go h.threadedHandleConn(conn)
	}
}

// NetAddress returns the address at which the host can be reached.
func (h *Host) NetAddress() modules.NetAddress {
	h.mu.RLock()
	defer h.mu.RUnlock()

	if h.settings.NetAddress != "" {
		return h.settings.NetAddress
	}
	return h.autoAddress
}

// NetworkMetrics returns information about the types of rpc calls that have
// been made to the host.
func (h *Host) NetworkMetrics() modules.HostNetworkMetrics {
	h.mu.RLock()
	defer h.mu.RUnlock()
	return modules.HostNetworkMetrics{
		// TODO: Up/Down bandwidth

		DownloadCalls:     atomic.LoadUint64(&h.atomicDownloadCalls),
		ErrorCalls:        atomic.LoadUint64(&h.atomicErroredCalls),
		FormContractCalls: atomic.LoadUint64(&h.atomicFormContractCalls),
		RenewCalls:        atomic.LoadUint64(&h.atomicRenewCalls),
		ReviseCalls:       atomic.LoadUint64(&h.atomicReviseCalls),
		SettingsCalls:     atomic.LoadUint64(&h.atomicSettingsCalls),
		UnrecognizedCalls: atomic.LoadUint64(&h.atomicUnrecognizedCalls),
	}
}
<commit_msg>better handling of deprecated settings calls (#1198)<commit_after>package host

import (
	"net"
	"sync/atomic"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// rpcSettingsDeprecated is a specifier for a deprecated settings request.
var rpcSettingsDeprecated = types.Specifier{'S', 'e', 't', 't', 'i', 'n', 'g', 's'}

// threadedUpdateHostname periodically runs 'managedLearnHostname', which
// checks if the host's hostname has changed, and makes an updated host
// announcement if so.
//
// TODO: This thread doesn't actually have clean shutdown, because the sleep is
// outside of of the resource lock.
func (h *Host) threadedUpdateHostname() {
	for {
		h.resourceLock.RLock()
		if h.closed {
			// The host is closed, the goroutine can exit.
			h.resourceLock.RUnlock()
			break
		}
		h.managedLearnHostname()
		h.resourceLock.RUnlock()

		// Wait 30 minutes to check again. If the hostname is changing
		// regularly (more than once a week), we want the host to be able to be
		// seen as having 95% uptime. Every minute that the announcement is
		// pointing to the wrong address is a minute of perceived downtime to
		// the renters.
		time.Sleep(time.Minute * 30)
	}
}

// initNetworking performs actions like port forwarding, and gets the host
// established on the network.
func (h *Host) initNetworking(address string) (err error) {
	// Create listener and set address.
	h.listener, err = h.dependencies.listen("tcp", address)
	if err != nil {
		return err
	}
	_, port, err := net.SplitHostPort(h.listener.Addr().String())
	if err != nil {
		return err
	}
	h.mu.Lock()
	h.port = port
	if build.Release == "testing" {
		h.autoAddress = modules.NetAddress(net.JoinHostPort("localhost", h.port))
	}
	h.mu.Unlock()

	// Non-blocking, perform port forwarding and hostname discovery.
	go func() {
		h.resourceLock.RLock()
		defer h.resourceLock.RUnlock()
		if h.closed {
			return
		}

		err := h.managedForwardPort()
		if err != nil {
			h.log.Println("ERROR: failed to forward port:", err)
		}

		// Spin up the hostname checker. The hostname checker should not be
		// spun up until after the port has been forwarded, because it can
		// result in announcements being submitted to the blockchain.
		go h.threadedUpdateHostname()
	}()

	// Launch the listener.
	go h.threadedListen()
	return nil
}

// threadedHandleConn handles an incoming connection to the host, typically an
// RPC.
func (h *Host) threadedHandleConn(conn net.Conn) {
	h.resourceLock.RLock()
	defer h.resourceLock.RUnlock()
	if h.closed {
		return
	}

	// Set an initial duration that is generous, but finite. RPCs can extend
	// this if desired.
	err := conn.SetDeadline(time.Now().Add(5 * time.Minute))
	if err != nil {
		h.log.Println("WARN: could not set deadline on connection:", err)
		return
	}
	defer conn.Close()

	// Read a specifier indicating which action is being called.
	var id types.Specifier
	if err := encoding.ReadObject(conn, &id, 16); err != nil {
		atomic.AddUint64(&h.atomicUnrecognizedCalls, 1)
		h.log.Debugf("WARN: incoming conn %v was malformed: %v", conn.RemoteAddr(), err)
		return
	}

	switch id {
	case modules.RPCDownload:
		atomic.AddUint64(&h.atomicDownloadCalls, 1)
		err = h.managedRPCDownload(conn)
	case modules.RPCRenewContract:
		atomic.AddUint64(&h.atomicRenewCalls, 1)
		err = h.managedRPCRenewContract(conn)
	case modules.RPCFormContract:
		atomic.AddUint64(&h.atomicFormContractCalls, 1)
		err = h.managedRPCFormContract(conn)
	case modules.RPCReviseContract:
		atomic.AddUint64(&h.atomicReviseCalls, 1)
		err = h.managedRPCReviseContract(conn)
	case modules.RPCRecentRevision:
		atomic.AddUint64(&h.atomicRecentRevisionCalls, 1)
		_, _, err = h.managedRPCRecentRevision(conn)
	case modules.RPCSettings:
		atomic.AddUint64(&h.atomicSettingsCalls, 1)
		err = h.managedRPCSettings(conn)
	case rpcSettingsDeprecated:
		h.log.Debugln("Received deprecated settings call")
	default:
		h.log.Debugf("WARN: incoming conn %v requested unknown RPC \"%v\"", conn.RemoteAddr(), id)
		atomic.AddUint64(&h.atomicUnrecognizedCalls, 1)
	}
	if err != nil {
		atomic.AddUint64(&h.atomicErroredCalls, 1)

		// If there have been less than 1000 errored rpcs, print the error
		// message. This is to help developers debug live systems that are
		// running into issues. Ultimately though, this error can be triggered
		// by a malicious actor, and therefore should not be logged except for
		// DEBUG builds.
		erroredCalls := atomic.LoadUint64(&h.atomicErroredCalls)
		if erroredCalls < 1e3 {
			h.log.Printf("WARN: incoming RPC \"%v\" failed: %v", id, err)
		} else {
			h.log.Debugf("WARN: incoming RPC \"%v\" failed: %v", id, err)
		}
	}
}

// listen listens for incoming RPCs and spawns an appropriate handler for each.
//
// TODO: Does not seem like this function ever actually lets go of the resource
// lock.
func (h *Host) threadedListen() {
	h.resourceLock.RLock()
	defer h.resourceLock.RUnlock()
	if h.closed {
		return
	}

	// Receive connections until an error is returned by the listener. When an
	// error is returned, there will be no more calls to receive.
	for {
		// Block until there is a connection to handle.
		conn, err := h.listener.Accept()
		if err != nil {
			return
		}

		// Grab the resource lock before creating a goroutine.
		go h.threadedHandleConn(conn)
	}
}

// NetAddress returns the address at which the host can be reached.
func (h *Host) NetAddress() modules.NetAddress {
	h.mu.RLock()
	defer h.mu.RUnlock()

	if h.settings.NetAddress != "" {
		return h.settings.NetAddress
	}
	return h.autoAddress
}

// NetworkMetrics returns information about the types of rpc calls that have
// been made to the host.
func (h *Host) NetworkMetrics() modules.HostNetworkMetrics {
	h.mu.RLock()
	defer h.mu.RUnlock()
	return modules.HostNetworkMetrics{
		// TODO: Up/Down bandwidth

		DownloadCalls:     atomic.LoadUint64(&h.atomicDownloadCalls),
		ErrorCalls:        atomic.LoadUint64(&h.atomicErroredCalls),
		FormContractCalls: atomic.LoadUint64(&h.atomicFormContractCalls),
		RenewCalls:        atomic.LoadUint64(&h.atomicRenewCalls),
		ReviseCalls:       atomic.LoadUint64(&h.atomicReviseCalls),
		SettingsCalls:     atomic.LoadUint64(&h.atomicSettingsCalls),
		UnrecognizedCalls: atomic.LoadUint64(&h.atomicUnrecognizedCalls),
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package envelope transforms values for storage at rest using a Envelope provider
package envelope

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/binary"
	"fmt"

	"k8s.io/apiserver/pkg/storage/value"

	lru "github.com/hashicorp/golang-lru"
)

// defaultCacheSize is the number of decrypted DEKs which would be cached by the transformer.
const defaultCacheSize = 1000

// Service allows encrypting and decrypting data using an external Key Management Service.
type Service interface {
	// Decrypt a given data string to obtain the original byte data.
	Decrypt(data string) ([]byte, error)
	// Encrypt bytes to a string ciphertext.
	Encrypt(data []byte) (string, error)
}

type envelopeTransformer struct {
	envelopeService Service

	// transformers is a thread-safe LRU cache which caches decrypted DEKs indexed by their encrypted form.
	transformers *lru.Cache

	// cacheSize is the maximum number of DEKs that are cached.
	cacheSize int

	// baseTransformerFunc creates a new transformer for encrypting the data with the DEK.
	baseTransformerFunc func(cipher.Block) value.Transformer
}

// NewEnvelopeTransformer returns a transformer which implements a KEK-DEK based envelope encryption scheme.
// It uses envelopeService to encrypt and decrypt DEKs. Respective DEKs (in encrypted form) are prepended to
// the data items they encrypt. A cache (of size cacheSize) is maintained to store the most recently
// used decrypted DEKs in memory.
func NewEnvelopeTransformer(envelopeService Service, cacheSize int, baseTransformerFunc func(cipher.Block) value.Transformer) (value.Transformer, error) {
	if cacheSize == 0 {
		cacheSize = defaultCacheSize
	}
	cache, err := lru.New(cacheSize)
	if err != nil {
		return nil, err
	}
	return &envelopeTransformer{
		envelopeService:     envelopeService,
		transformers:        cache,
		cacheSize:           cacheSize,
		baseTransformerFunc: baseTransformerFunc,
	}, nil
}

// TransformFromStorage decrypts data encrypted by this transformer using envelope encryption.
func (t *envelopeTransformer) TransformFromStorage(data []byte, context value.Context) ([]byte, bool, error) {
	// Read the 16 bit length-of-DEK encoded at the start of the encrypted DEK. 16 bits can
	// represent a maximum key length of 65536 bytes. We are using a 256 bit key, whose
	// length cannot fit in 8 bits (1 byte). Thus, we use 16 bits (2 bytes) to store the length.
	keyLen := int(binary.BigEndian.Uint16(data[:2]))
	if keyLen+2 > len(data) {
		return nil, false, fmt.Errorf("invalid data encountered by genvelope transformer, length longer than available bytes: %q", data)
	}
	encKey := string(data[2 : keyLen+2])
	encData := data[2+keyLen:]

	var transformer value.Transformer
	// Look up the decrypted DEK from cache or Envelope.
	_transformer, found := t.transformers.Get(encKey)
	if found {
		transformer = _transformer.(value.Transformer)
	} else {
		key, err := t.envelopeService.Decrypt(encKey)
		if err != nil {
			return nil, false, fmt.Errorf("error while decrypting key: %q", err)
		}
		transformer, err = t.addTransformer(encKey, key)
		if err != nil {
			return nil, false, err
		}
	}
	return transformer.TransformFromStorage(encData, context)
}

// TransformToStorage encrypts data to be written to disk using envelope encryption.
func (t *envelopeTransformer) TransformToStorage(data []byte, context value.Context) ([]byte, error) {
	newKey, err := generateKey(32)
	if err != nil {
		return nil, err
	}

	encKey, err := t.envelopeService.Encrypt(newKey)
	if err != nil {
		return nil, err
	}

	transformer, err := t.addTransformer(encKey, newKey)
	if err != nil {
		return nil, err
	}

	// Append the length of the encrypted DEK as the first 2 bytes.
	encKeyLen := make([]byte, 2)
	encKeyBytes := []byte(encKey)
	binary.BigEndian.PutUint16(encKeyLen, uint16(len(encKeyBytes)))

	prefix := append(encKeyLen, encKeyBytes...)

	prefixedData := make([]byte, len(prefix), len(data)+len(prefix))
	copy(prefixedData, prefix)
	result, err := transformer.TransformToStorage(data, context)
	if err != nil {
		return nil, err
	}
	prefixedData = append(prefixedData, result...)
	return prefixedData, nil
}

var _ value.Transformer = &envelopeTransformer{}

// addTransformer inserts a new transformer to the Envelope cache of DEKs for future reads.
func (t *envelopeTransformer) addTransformer(encKey string, key []byte) (value.Transformer, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	transformer := t.baseTransformerFunc(block)
	t.transformers.Add(encKey, transformer)
	return transformer, nil
}

// generateKey generates a random key using system randomness.
func generateKey(length int) ([]byte, error) {
	key := make([]byte, length)
	_, err := rand.Read(key)
	if err != nil {
		return nil, err
	}

	return key, nil
}
<commit_msg>remove unused filed<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package envelope transforms values for storage at rest using a Envelope provider
package envelope

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/binary"
	"fmt"

	"k8s.io/apiserver/pkg/storage/value"

	lru "github.com/hashicorp/golang-lru"
)

// defaultCacheSize is the number of decrypted DEKs which would be cached by the transformer.
const defaultCacheSize = 1000

// Service allows encrypting and decrypting data using an external Key Management Service.
type Service interface {
	// Decrypt a given data string to obtain the original byte data.
	Decrypt(data string) ([]byte, error)
	// Encrypt bytes to a string ciphertext.
	Encrypt(data []byte) (string, error)
}

type envelopeTransformer struct {
	envelopeService Service

	// transformers is a thread-safe LRU cache which caches decrypted DEKs indexed by their encrypted form.
	transformers *lru.Cache

	// baseTransformerFunc creates a new transformer for encrypting the data with the DEK.
	baseTransformerFunc func(cipher.Block) value.Transformer
}

// NewEnvelopeTransformer returns a transformer which implements a KEK-DEK based envelope encryption scheme.
// It uses envelopeService to encrypt and decrypt DEKs. Respective DEKs (in encrypted form) are prepended to
// the data items they encrypt. A cache (of size cacheSize) is maintained to store the most recently
// used decrypted DEKs in memory.
func NewEnvelopeTransformer(envelopeService Service, cacheSize int, baseTransformerFunc func(cipher.Block) value.Transformer) (value.Transformer, error) {
	if cacheSize == 0 {
		cacheSize = defaultCacheSize
	}
	cache, err := lru.New(cacheSize)
	if err != nil {
		return nil, err
	}
	return &envelopeTransformer{
		envelopeService:     envelopeService,
		transformers:        cache,
		baseTransformerFunc: baseTransformerFunc,
	}, nil
}

// TransformFromStorage decrypts data encrypted by this transformer using envelope encryption.
func (t *envelopeTransformer) TransformFromStorage(data []byte, context value.Context) ([]byte, bool, error) {
	// Read the 16 bit length-of-DEK encoded at the start of the encrypted DEK. 16 bits can
	// represent a maximum key length of 65536 bytes. We are using a 256 bit key, whose
	// length cannot fit in 8 bits (1 byte). Thus, we use 16 bits (2 bytes) to store the length.
	keyLen := int(binary.BigEndian.Uint16(data[:2]))
	if keyLen+2 > len(data) {
		return nil, false, fmt.Errorf("invalid data encountered by genvelope transformer, length longer than available bytes: %q", data)
	}
	encKey := string(data[2 : keyLen+2])
	encData := data[2+keyLen:]

	var transformer value.Transformer
	// Look up the decrypted DEK from cache or Envelope.
	_transformer, found := t.transformers.Get(encKey)
	if found {
		transformer = _transformer.(value.Transformer)
	} else {
		key, err := t.envelopeService.Decrypt(encKey)
		if err != nil {
			return nil, false, fmt.Errorf("error while decrypting key: %q", err)
		}
		transformer, err = t.addTransformer(encKey, key)
		if err != nil {
			return nil, false, err
		}
	}
	return transformer.TransformFromStorage(encData, context)
}

// TransformToStorage encrypts data to be written to disk using envelope encryption.
func (t *envelopeTransformer) TransformToStorage(data []byte, context value.Context) ([]byte, error) {
	newKey, err := generateKey(32)
	if err != nil {
		return nil, err
	}

	encKey, err := t.envelopeService.Encrypt(newKey)
	if err != nil {
		return nil, err
	}

	transformer, err := t.addTransformer(encKey, newKey)
	if err != nil {
		return nil, err
	}

	// Append the length of the encrypted DEK as the first 2 bytes.
	encKeyLen := make([]byte, 2)
	encKeyBytes := []byte(encKey)
	binary.BigEndian.PutUint16(encKeyLen, uint16(len(encKeyBytes)))

	prefix := append(encKeyLen, encKeyBytes...)

	prefixedData := make([]byte, len(prefix), len(data)+len(prefix))
	copy(prefixedData, prefix)
	result, err := transformer.TransformToStorage(data, context)
	if err != nil {
		return nil, err
	}
	prefixedData = append(prefixedData, result...)
	return prefixedData, nil
}

var _ value.Transformer = &envelopeTransformer{}

// addTransformer inserts a new transformer to the Envelope cache of DEKs for future reads.
func (t *envelopeTransformer) addTransformer(encKey string, key []byte) (value.Transformer, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	transformer := t.baseTransformerFunc(block)
	t.transformers.Add(encKey, transformer)
	return transformer, nil
}

// generateKey generates a random key using system randomness.
func generateKey(length int) ([]byte, error) {
	key := make([]byte, length)
	_, err := rand.Read(key)
	if err != nil {
		return nil, err
	}

	return key, nil
}
<|endoftext|>


<commit_before>package main

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

	"github.com/spf13/cobra"
)

// CommandStatus processes 'git status --porcelain', and exports numbered
// env variables that contain the path of each affected file.
// Output is also more concise than standard 'git status'.
//
// Call with optional <group> parameter to filter by modification state:
// 1 || Staged,  2 || Unmerged,  3 || Unstaged,  4 || Untracked
func CommandStatus() *cobra.Command {

	var statusCmd = &cobra.Command{
		Use:   "status",
		Short: "Set and display numbered git status",
		Long: `
Processes 'git status --porcelain', and exports numbered env variables that
contain the path of each affected file.
Output is also more concise than standard 'git status'.
    `,
		Run: func(cmd *cobra.Command, args []string) {
			runStatus()
		},
	}

	// --relative
	// statusCmd.Flags().BoolVarP(
	// 	&expandRelative,
	// 	"relative",
	// 	"r",
	// 	false,
	// 	"TODO: DESCRIPTION HERE YO",
	// )

	return statusCmd
}

// StatusGroup encapsulates constants for mapping group status
type StatusGroup int

// constants representing an enum of all possible StatusGroups
const (
	Staged StatusGroup = iota
	Unmerged
	Unstaged
	Untracked
)

// ColorGroup encapsulates constants for mapping color output categories
type ColorGroup int

const (
	rst ColorGroup = iota
	del
	mod
	neu //'new' is reserved in Go
	ren
	cpy
	typ
	unt
	dark
	branch
	header
)

var colorMap = map[ColorGroup]string{
	rst:    "\033[0m",
	del:    "\033[0;31m",
	mod:    "\033[0;32m",
	neu:    "\033[0;33m",
	ren:    "\033[0;34m",
	cpy:    "\033[0;33m",
	typ:    "\033[0;35m",
	unt:    "\033[0;36m",
	dark:   "\033[2;37m",
	branch: "\033[1m",
	header: "\033[0m",
}

var groupColorMap = map[StatusGroup]string{
	Staged:    "33m",
	Unmerged:  "31m",
	Unstaged:  "32m",
	Untracked: "36m",
}

// StatusItem represents a single processed item of change from a 'git status'
type StatusItem struct {
	x, y  rune
	msg   string
	col   ColorGroup
	group StatusGroup
	file  string
}

// StatusList gives us a data structure to store all items of a git status
// organized by what group they fall under.
//
// This is helpful because we want to pull them out by group later, and don't
// want to bear the cost of filtering then.
//
// It also helps us map closer to the program logic of the Ruby code from
// scm_breeze, so hopefully easier to port.
type StatusList struct {
	// groups map[StatusGroup][]*StatusItem
	groups map[StatusGroup]*FileGroup
}

// FileGroup is a bucket of all file StatusItems for a particular StatusGroup
type FileGroup struct {
	desc  string
	items []*StatusItem
}

// NewStatusList is a constructor that initializes a new StatusList so that it's
// ready to use.
func NewStatusList() *StatusList {
	return &StatusList{
		groups: map[StatusGroup]*FileGroup{
			Staged: &FileGroup{
				desc:  "Changes to be committed",
				items: make([]*StatusItem, 0),
			},
			Unmerged: &FileGroup{
				desc:  "Unmerged paths",
				items: make([]*StatusItem, 0),
			},
			Unstaged: &FileGroup{
				desc:  "Changes not Staged for commit",
				items: make([]*StatusItem, 0),
			},
			Untracked: &FileGroup{
				desc:  "Untracked files",
				items: make([]*StatusItem, 0),
			},
		},
	}
}

// Returns the groups of a StatusList in a specific order.
//
// Since you can't range over maps in sequential order, we hard code the order
// here.
//
// We already have the keys as a const enum, so we could replace the map with a
// slice and use the StatsGroup as the index value, but I think it's clearer to
// use a map there even if uneccessary.
//
// If we ever really need to look at the performance of this, it might be worth
// seeing if using arrays is much faster (doubt it will make a difference in our
// case however.)
func (sl StatusList) orderedGroups() []*FileGroup {
	// use number literals rather than const names so that we can define the order
	// via the const definition.
	return []*FileGroup{sl.groups[0], sl.groups[1], sl.groups[2], sl.groups[3]}
}

func runStatus() {
	// TODO: fail if not git repo
	// TODO: git clear vars

	// TODO run commands to get status and branch
	gitStatusOutput, err := exec.Command("git", "status", "--porcelain").Output()
	if err == nil {
		// TODO: HANDLE
	}

	// gitBranchOutput, err := exec.Command("git", "branch", "-v").Output()
	// if err == nil {
	// 	// TODO: HANDLE
	// }

	// split the status output to get a list of changes as raw bytestrings
	changes := bytes.Split(bytes.TrimSpace(gitStatusOutput), []byte{'\n'})

	// allocate a StatusList to hold the results
	results := NewStatusList()

	// process each item, and store the results
	for _, change := range changes {
		rs := processChange(change)
		results.groups[rs.group].items = append(results.groups[rs.group].items, rs)
	}

	results.printAll()
}

func processChange(c []byte) *StatusItem {
	x := rune(c[0])
	y := rune(c[1])
	file := string(c[3:len(c)])
	msg, col, group := decodeChangeCode(x, y, file)

	ccc := StatusItem{
		x:     x,
		y:     y,
		file:  file,
		msg:   msg,
		col:   col,
		group: group,
	}
	return &ccc
}

func decodeChangeCode(x, y rune, file string) (string, ColorGroup, StatusGroup) {
	switch {
	case x == 'D' && y == 'D': //DD
		return "   both deleted", del, Unmerged
	case x == 'A' && y == 'U': //AU
		return "    added by us", neu, Unmerged
	case x == 'U' && y == 'D': //UD
		return "deleted by them", del, Unmerged
	case x == 'U' && y == 'A': //UA
		return "  added by them", neu, Unmerged
	case x == 'D' && y == 'U': //DU
		return "  deleted by us", del, Unmerged
	case x == 'A' && y == 'A': //AA
		return "     both added", neu, Unmerged
	case x == 'U' && y == 'U': //UU
		return "  both modified", mod, Unmerged
	case x == 'M': //          //M.
		return "  modified", mod, Staged
	case x == 'A': //          //A.
		return "  new file", neu, Staged
	case x == 'D': //          //D.
		return "   deleted", del, Staged
	case x == 'R': //          //R.
		return "   renamed", ren, Staged
	case x == 'C': //          //C.
		return "    copied", cpy, Staged
	case x == 'T': //          //T.
		return "typechange", typ, Staged
	case x == '?' && y == '?': //??
		return " Untracked", unt, Untracked
	// So here's the thing, below case should never match, because [R.] earlier
	// is going to nab it.  So I'm assuming it's an oversight in the script.
	//
	// it was introduced to scm_breeze in:
	//   https://github.com/ndbroadbent/scm_breeze/pull/145/files
	//
	// case x == 'R' && y == 'M': //RM
	case x != 'R' && y == 'M': //[!R]M
		return "  modified", mod, Unstaged
	case y == 'D' && y != 'D' && y != 'U': //[!D!U]D
		// Don't show deleted 'y' during a merge conflict.
		return "   deleted", del, Unstaged
	case y == 'T': //.T
		return "typechange", typ, Unstaged
	}

	panic("Failed to decode git status change code!")
}

func (sl StatusList) printAll() {
	for _, fg := range sl.orderedGroups() {
		fg.print()
	}
}

func (fg FileGroup) print() {
	fmt.Println(fg.desc)
	for _, i := range fg.items {
		fmt.Println(i)
	}
}
<commit_msg>don't print empty file groups<commit_after>package main

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

	"github.com/spf13/cobra"
)

// CommandStatus processes 'git status --porcelain', and exports numbered
// env variables that contain the path of each affected file.
// Output is also more concise than standard 'git status'.
//
// Call with optional <group> parameter to filter by modification state:
// 1 || Staged,  2 || Unmerged,  3 || Unstaged,  4 || Untracked
func CommandStatus() *cobra.Command {

	var statusCmd = &cobra.Command{
		Use:   "status",
		Short: "Set and display numbered git status",
		Long: `
Processes 'git status --porcelain', and exports numbered env variables that
contain the path of each affected file.
Output is also more concise than standard 'git status'.
    `,
		Run: func(cmd *cobra.Command, args []string) {
			runStatus()
		},
	}

	// --relative
	// statusCmd.Flags().BoolVarP(
	// 	&expandRelative,
	// 	"relative",
	// 	"r",
	// 	false,
	// 	"TODO: DESCRIPTION HERE YO",
	// )

	return statusCmd
}

// StatusGroup encapsulates constants for mapping group status
type StatusGroup int

// constants representing an enum of all possible StatusGroups
const (
	Staged StatusGroup = iota
	Unmerged
	Unstaged
	Untracked
)

// ColorGroup encapsulates constants for mapping color output categories
type ColorGroup int

const (
	rst ColorGroup = iota
	del
	mod
	neu //'new' is reserved in Go
	ren
	cpy
	typ
	unt
	dark
	branch
	header
)

var colorMap = map[ColorGroup]string{
	rst:    "\033[0m",
	del:    "\033[0;31m",
	mod:    "\033[0;32m",
	neu:    "\033[0;33m",
	ren:    "\033[0;34m",
	cpy:    "\033[0;33m",
	typ:    "\033[0;35m",
	unt:    "\033[0;36m",
	dark:   "\033[2;37m",
	branch: "\033[1m",
	header: "\033[0m",
}

var groupColorMap = map[StatusGroup]string{
	Staged:    "33m",
	Unmerged:  "31m",
	Unstaged:  "32m",
	Untracked: "36m",
}

// StatusItem represents a single processed item of change from a 'git status'
type StatusItem struct {
	x, y  rune
	msg   string
	col   ColorGroup
	group StatusGroup
	file  string
}

// StatusList gives us a data structure to store all items of a git status
// organized by what group they fall under.
//
// This is helpful because we want to pull them out by group later, and don't
// want to bear the cost of filtering then.
//
// It also helps us map closer to the program logic of the Ruby code from
// scm_breeze, so hopefully easier to port.
type StatusList struct {
	// groups map[StatusGroup][]*StatusItem
	groups map[StatusGroup]*FileGroup
}

// FileGroup is a bucket of all file StatusItems for a particular StatusGroup
type FileGroup struct {
	desc  string
	items []*StatusItem
}

// NewStatusList is a constructor that initializes a new StatusList so that it's
// ready to use.
func NewStatusList() *StatusList {
	return &StatusList{
		groups: map[StatusGroup]*FileGroup{
			Staged: &FileGroup{
				desc:  "Changes to be committed",
				items: make([]*StatusItem, 0),
			},
			Unmerged: &FileGroup{
				desc:  "Unmerged paths",
				items: make([]*StatusItem, 0),
			},
			Unstaged: &FileGroup{
				desc:  "Changes not Staged for commit",
				items: make([]*StatusItem, 0),
			},
			Untracked: &FileGroup{
				desc:  "Untracked files",
				items: make([]*StatusItem, 0),
			},
		},
	}
}

// Returns the groups of a StatusList in a specific order.
//
// Since you can't range over maps in sequential order, we hard code the order
// here.
//
// We already have the keys as a const enum, so we could replace the map with a
// slice and use the StatsGroup as the index value, but I think it's clearer to
// use a map there even if uneccessary.
//
// If we ever really need to look at the performance of this, it might be worth
// seeing if using arrays is much faster (doubt it will make a difference in our
// case however.)
func (sl StatusList) orderedGroups() []*FileGroup {
	// use number literals rather than const names so that we can define the order
	// via the const definition.
	return []*FileGroup{sl.groups[0], sl.groups[1], sl.groups[2], sl.groups[3]}
}

func runStatus() {
	// TODO: fail if not git repo
	// TODO: git clear vars

	// TODO run commands to get status and branch
	gitStatusOutput, err := exec.Command("git", "status", "--porcelain").Output()
	if err == nil {
		// TODO: HANDLE
	}

	// gitBranchOutput, err := exec.Command("git", "branch", "-v").Output()
	// if err == nil {
	// 	// TODO: HANDLE
	// }

	// split the status output to get a list of changes as raw bytestrings
	changes := bytes.Split(bytes.TrimSpace(gitStatusOutput), []byte{'\n'})

	// allocate a StatusList to hold the results
	results := NewStatusList()

	// process each item, and store the results
	for _, change := range changes {
		rs := processChange(change)
		results.groups[rs.group].items = append(results.groups[rs.group].items, rs)
	}

	results.printAll()
}

func processChange(c []byte) *StatusItem {
	x := rune(c[0])
	y := rune(c[1])
	file := string(c[3:len(c)])
	msg, col, group := decodeChangeCode(x, y, file)

	ccc := StatusItem{
		x:     x,
		y:     y,
		file:  file,
		msg:   msg,
		col:   col,
		group: group,
	}
	return &ccc
}

func decodeChangeCode(x, y rune, file string) (string, ColorGroup, StatusGroup) {
	switch {
	case x == 'D' && y == 'D': //DD
		return "   both deleted", del, Unmerged
	case x == 'A' && y == 'U': //AU
		return "    added by us", neu, Unmerged
	case x == 'U' && y == 'D': //UD
		return "deleted by them", del, Unmerged
	case x == 'U' && y == 'A': //UA
		return "  added by them", neu, Unmerged
	case x == 'D' && y == 'U': //DU
		return "  deleted by us", del, Unmerged
	case x == 'A' && y == 'A': //AA
		return "     both added", neu, Unmerged
	case x == 'U' && y == 'U': //UU
		return "  both modified", mod, Unmerged
	case x == 'M': //          //M.
		return "  modified", mod, Staged
	case x == 'A': //          //A.
		return "  new file", neu, Staged
	case x == 'D': //          //D.
		return "   deleted", del, Staged
	case x == 'R': //          //R.
		return "   renamed", ren, Staged
	case x == 'C': //          //C.
		return "    copied", cpy, Staged
	case x == 'T': //          //T.
		return "typechange", typ, Staged
	case x == '?' && y == '?': //??
		return " Untracked", unt, Untracked
	// So here's the thing, below case should never match, because [R.] earlier
	// is going to nab it.  So I'm assuming it's an oversight in the script.
	//
	// it was introduced to scm_breeze in:
	//   https://github.com/ndbroadbent/scm_breeze/pull/145/files
	//
	// case x == 'R' && y == 'M': //RM
	case x != 'R' && y == 'M': //[!R]M
		return "  modified", mod, Unstaged
	case y == 'D' && y != 'D' && y != 'U': //[!D!U]D
		// Don't show deleted 'y' during a merge conflict.
		return "   deleted", del, Unstaged
	case y == 'T': //.T
		return "typechange", typ, Unstaged
	}

	panic("Failed to decode git status change code!")
}

func (sl StatusList) printAll() {
	for _, fg := range sl.orderedGroups() {
		fg.print()
	}
}

func (fg FileGroup) print() {
	if len(fg.items) > 0 {
		fmt.Println(fg.desc)
		for _, i := range fg.items {
			fmt.Println(i)
		}
	}
}
<|endoftext|>


<commit_before>package async

import (
	"context"
	"errors"
	"sync"
	"time"
)

type Method func(ctx context.Context, args ...interface{}) (interface{}, error)
type LambdaMethod func() (interface{}, error)

func Lambda(method func()(interface{}, error), timeout time.Duration) (interface{}, error) {
	output := make(chan interface{})
	go func() {
		defer close(output)
		defer func() {
			if e := recover(); e != nil {
				output <- e.(error)
			}
		}()
		res, err := method()
		if err != nil {
			output <- err
		} else {
			output <- res
		}
	}()
	if timeout > 0 {
		timer := time.NewTimer(timeout)
		defer timer.Stop()
		select {
		case res := <-output:
			{
				switch err := res.(type) {
				case error:
					{
						return nil, err
					}
				default:
					{
						return res, nil
					}
				}
			}
		case <-timer.C:
			{
				return nil, errors.New("Async_Timeout")
			}
		}
	} else {
		res := <-output
		switch err := res.(type) {
		case error:
			{
				return nil, err
			}
		default:
			{
				return res, nil
			}
		}
	}
}

func Call(m Method, ctx context.Context, timeout time.Duration, args ...interface{}) (interface{}, error) {
	return Lambda(func() (interface{}, error) {
		return m(ctx, args...)
	}, timeout)
}

func All(methods []LambdaMethod, timeout time.Duration) []interface{} {
	var wg sync.WaitGroup
	result := make([]interface{}, len(methods))
	for i, m := range methods {
		wg.Add(1)
		go func(index int, method LambdaMethod) {
			defer wg.Done()
			res, err := Lambda(method, timeout)
			if err != nil {
				result[index] = err
			} else {
				result[index] = res
			}
		}(i, m)
	}
	wg.Wait()
	return result
}

func Serise(methods []LambdaMethod, timeout time.Duration) []interface{} {
	result := make([]interface{}, 0)
	for _, m := range methods {
		res, err := Lambda(m, timeout)
		if err != nil {
			result = append(result, err)
			return result
		} else {
			result = append(result, res)
		}
	}
	return result
}

func Flow(enter Method, ctx context.Context, args []interface{}, methods []Method, timeout time.Duration) (interface{}, error) {
	var (
		res interface{}
		err error
	)
	res, err = Call(enter, ctx, timeout, args...)
	if err != nil {
		return nil, err
	}
	for _, m := range methods {
		res, err = Call(m, ctx, timeout, res)
		if err != nil {
			return nil, err
		}
	}
	return res, nil
}

func Any(methods []LambdaMethod, timeout time.Duration) ([]interface{}, error) {
	resChan := make(chan []interface{}, 1)
	errChan := make(chan error, len(methods))
	go func() {
		defer func() {
			close(resChan)
			close(errChan)
		}()
		var wg sync.WaitGroup
		result := make([]interface{}, len(methods))
		for i, m := range methods {
			wg.Add(1)
			go func(index int, method LambdaMethod) {
				defer wg.Done()
				res, err := Lambda(method, timeout)
				if err != nil {
					errChan <- err
				} else {
					result[index] = res
				}
			}(i, m)
		}
		wg.Wait()
		resChan <- result
	}()
	select {
	case err := <-errChan:
		return nil, err
	case res := <-resChan:
		return res, nil
	}
}

func AnyOne(methods []LambdaMethod, timeout time.Duration) (interface{}, []error) {
	resChan := make(chan interface{}, len(methods))
	errChan := make(chan []error)
	go func() {
		defer func() {
			close(resChan)
			close(errChan)
		}()
		var wg sync.WaitGroup
		errs := make([]error, len(methods))
		for i, m := range methods {
			wg.Add(1)
			go func(index int, method LambdaMethod) {
				defer wg.Done()
				res, err := Lambda(method, timeout)
				if err != nil {
					errs[index] = err
				} else {
					resChan <- res
				}
			}(i, m)
		}
		wg.Wait()
		errChan <- errs
	}()
	select {
	case errs := <-errChan:
		return nil, errs
	case res := <-resChan:
		return res, nil
	}
}
<commit_msg>remove context<commit_after>package async

import (
	"errors"
	"sync"
	"time"
)

type Method func(args ...interface{}) (interface{}, error)
type LambdaMethod func() (interface{}, error)

func Lambda(method func()(interface{}, error), timeout time.Duration) (interface{}, error) {
	output := make(chan interface{})
	go func() {
		defer close(output)
		defer func() {
			if e := recover(); e != nil {
				output <- e.(error)
			}
		}()
		res, err := method()
		if err != nil {
			output <- err
		} else {
			output <- res
		}
	}()
	if timeout > 0 {
		timer := time.NewTimer(timeout)
		defer timer.Stop()
		select {
		case res := <-output:
			{
				switch err := res.(type) {
				case error:
					{
						return nil, err
					}
				default:
					{
						return res, nil
					}
				}
			}
		case <-timer.C:
			{
				return nil, errors.New("Async_Timeout")
			}
		}
	} else {
		res := <-output
		switch err := res.(type) {
		case error:
			{
				return nil, err
			}
		default:
			{
				return res, nil
			}
		}
	}
}

func Call(m Method, timeout time.Duration, args ...interface{}) (interface{}, error) {
	return Lambda(func() (interface{}, error) {
		return m(args...)
	}, timeout)
}

func All(methods []LambdaMethod, timeout time.Duration) []interface{} {
	var wg sync.WaitGroup
	result := make([]interface{}, len(methods))
	for i, m := range methods {
		wg.Add(1)
		go func(index int, method LambdaMethod) {
			defer wg.Done()
			res, err := Lambda(method, timeout)
			if err != nil {
				result[index] = err
			} else {
				result[index] = res
			}
		}(i, m)
	}
	wg.Wait()
	return result
}

func Serise(methods []LambdaMethod, timeout time.Duration) []interface{} {
	result := make([]interface{}, 0)
	for _, m := range methods {
		res, err := Lambda(m, timeout)
		if err != nil {
			result = append(result, err)
			return result
		} else {
			result = append(result, res)
		}
	}
	return result
}

func Flow(enter Method, args []interface{}, methods []Method, timeout time.Duration) (interface{}, error) {
	var (
		res interface{}
		err error
	)
	res, err = Call(enter, timeout, args...)
	if err != nil {
		return nil, err
	}
	for _, m := range methods {
		res, err = Call(m, timeout, res)
		if err != nil {
			return nil, err
		}
	}
	return res, nil
}

func Any(methods []LambdaMethod, timeout time.Duration) ([]interface{}, error) {
	resChan := make(chan []interface{}, 1)
	errChan := make(chan error, len(methods))
	go func() {
		defer func() {
			close(resChan)
			close(errChan)
		}()
		var wg sync.WaitGroup
		result := make([]interface{}, len(methods))
		for i, m := range methods {
			wg.Add(1)
			go func(index int, method LambdaMethod) {
				defer wg.Done()
				res, err := Lambda(method, timeout)
				if err != nil {
					errChan <- err
				} else {
					result[index] = res
				}
			}(i, m)
		}
		wg.Wait()
		resChan <- result
	}()
	select {
	case err := <-errChan:
		return nil, err
	case res := <-resChan:
		return res, nil
	}
}

func AnyOne(methods []LambdaMethod, timeout time.Duration) (interface{}, []error) {
	resChan := make(chan interface{}, len(methods))
	errChan := make(chan []error)
	go func() {
		defer func() {
			close(resChan)
			close(errChan)
		}()
		var wg sync.WaitGroup
		errs := make([]error, len(methods))
		for i, m := range methods {
			wg.Add(1)
			go func(index int, method LambdaMethod) {
				defer wg.Done()
				res, err := Lambda(method, timeout)
				if err != nil {
					errs[index] = err
				} else {
					resChan <- res
				}
			}(i, m)
		}
		wg.Wait()
		errChan <- errs
	}()
	select {
	case errs := <-errChan:
		return nil, errs
	case res := <-resChan:
		return res, nil
	}
}
<|endoftext|>


<commit_before><commit_msg>Run command in goroutine<commit_after><|endoftext|>


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

package dvr

import (
	"bytes"
	"crypto/rsa"
	"crypto/tls"
	"encoding/gob"
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"reflect"
)

//
// Error wrapper
//

// This is a safe type that will always be able to be gob encoded.
type gobSafeError string

// Error() for gobSafeError
func (g gobSafeError) Error() string {
	return string(g)
}

// This is the list of known encodable types saved as a map of name -> bool.
// This allows us to know if a given type will be decodable or not.. If not
// then we need to wrap the type in a gobSafeError structure.
var encodableTypes map[string]bool = map[string]bool{}

// Initialize encodableTypes
func init() {
	// Error return types.
	RegisterErrorType(new(gobSafeError))
	RegisterErrorType(new(http.ProtocolError))
	RegisterErrorType(new(net.AddrError))
	RegisterErrorType(new(net.DNSConfigError))
	RegisterErrorType(new(net.DNSError))
	RegisterErrorType(new(net.DNSError))
	RegisterErrorType(new(net.InvalidAddrError))
	RegisterErrorType(new(net.OpError))
	RegisterErrorType(new(net.ParseError))
	RegisterErrorType(new(net.UnknownNetworkError))
	RegisterErrorType(new(url.Error))
	RegisterErrorType(new(url.EscapeError))

	// Other objects that we might end up seeing.
	gob.Register(new(rsa.PublicKey))
	gob.Register(new(rsa.PrivateKey))
	gob.Register(new(net.TCPAddr))
}

// Adds an error interface object to the list of known types that this library
// will be able to encode. This is necessary due to the way that gob encodes
// interface object. The only error types here are those that will be returned
// from the RoundTripper object. Typical use cases should not need this at all.
// If you are using this you must do it via your modules init() otherwise
// results can be unpredictable.
func RegisterErrorType(err error) {
	// Walk the given interface all the way down to the raw object.
	value := reflect.ValueOf(err)
	for value.Kind() == reflect.Interface || value.Kind() == reflect.Ptr {
		if _, ok := value.Interface().(error); ok {
			gob.Register(value.Interface())
		}
		value = value.Elem()
	}
	typ := value.Type()
	id := fmt.Sprintf("%s.%s", typ.PkgPath(), typ.Name())
	encodableTypes[id] = true
}

// This type is used to store errors. Since some errors might contain private
// fields we need to ensure that we can still convert them as best as possible.
// Specifically this will convert them to a string error.
type gobError struct {
	Error error
}

// The encoder that allows us to safely convert errors.
func (g *gobError) GobEncode() ([]byte, error) {
	if g.Error == nil {
		return []byte{}, nil
	}

	// If we are encoding a known safe type then we write the types name and
	// then encode it into the byte stream, otherwise we are forced to convert
	// it into a gobSafeError type so it can be safely stored.
	value := reflect.ValueOf(g.Error)
	for value.Kind() == reflect.Interface || value.Kind() == reflect.Ptr {
		value = value.Elem()
	}

	// Figure out the name of this object type.
	id := fmt.Sprintf("%s.%s", value.Type().PkgPath(), value.Type().Name())

	// Make a safe error object for us to encode with.
	rawError := gobRawError{
		Error:             g.Error,
		ErrorsErrorString: id == "errors.errorString",
	}

	// If the object that we are encoding is not safe then we need to change
	// it into one that actually is.
	if _, ok := encodableTypes[id]; !ok {
		rawError.Error = gobSafeError(g.Error.Error())
	}

	// Encode the safe object and return the byte array.
	buffer := bytes.Buffer{}
	encoder := gob.NewEncoder(&buffer)
	err := encoder.Encode(&rawError)
	return buffer.Bytes(), err
}

// Decodes a gobError struct
func (g *gobError) GobDecode(data []byte) error {
	if len(data) == 0 {
		g.Error = nil
		return nil
	}
	buffer := bytes.NewBuffer(data)
	decoder := gob.NewDecoder(buffer)
	var rawError gobRawError
	if err := decoder.Decode(&rawError); err != nil {
		return err
	}

	if rawError.ErrorsErrorString {
		g.Error = errors.New(rawError.Error.Error())
	} else {
		g.Error = rawError.Error
	}
	return nil
}

// This is the object type that gobError will use when encoding and decoding.
// The assumption here is that the Error field in this object will be populated
// only with safe error types.
type gobRawError struct {
	Error error

	// This is set to true if the error was initially a 'errors.errorString'
	// so we know that we can convert it back in the decoding process.
	ErrorsErrorString bool
}

//
// bytes.Buffer with a Close() call.
//

// This structure is just a bytes.Buffer with a Close() method.
type bytesBufferCloser struct {
	bytes.Buffer
}

// Required to implement io.ReaderCloser
func (b *bytesBufferCloser) Close() error {
	return nil
}

//
// Request wrapper
//

// This is a gob encodable version of http.Request. It removes the possibility
// that a private field can be seen and therefor return an error.
type gobRequest struct {
	Method           string
	URL              string
	Proto            string
	ProtoMajor       int
	ProtoMinor       int
	Header           http.Header
	ContentLength    int64
	TransferEncoding []string
	Close            bool
	Host             string
	Form             url.Values
	PostForm         url.Values
	Trailer          http.Header
	RemoteAddr       string
	RequestURI       string
	TLS              *tls.ConnectionState

	// The request body and err returned when reading it.
	Body  []byte
	Error gobError
}

// This takes a Request object and returns a gob compatible gobRequest object.
func newGobRequest(req *http.Request) *gobRequest {
	if req == nil {
		return nil
	}

	r := new(gobRequest)
	r.Method = req.Method
	r.URL = req.URL.String()
	r.Proto = req.Proto
	r.ProtoMajor = req.ProtoMajor
	r.ProtoMinor = req.ProtoMinor
	r.Header = req.Header
	r.ContentLength = req.ContentLength
	r.TransferEncoding = req.TransferEncoding
	r.Close = req.Close
	r.Host = req.Host
	r.Form = req.Form
	r.PostForm = req.PostForm
	r.Trailer = req.Trailer
	r.RemoteAddr = req.RemoteAddr
	r.RequestURI = req.RequestURI
	newGobRequestVS(req, r)

	return r
}

//
// Response wrapper
//

// This object wraps an http.Response object in order to make it possible to
// gob encode it.
type gobResponse struct {
	Status           string
	StatusCode       int
	Proto            string
	ProtoMajor       int
	ProtoMinor       int
	Header           http.Header
	ContentLength    int64
	TransferEncoding []string
	Close            bool
	Trailer          http.Header
	TLS              *tls.ConnectionState

	// The response body and err returned when reading it.
	Body  []byte
	Error gobError
}

// This takes a Response object and returns a gob compatible gobResponse object.
func newGobResponse(resp *http.Response) *gobResponse {
	if resp == nil {
		return nil
	}

	r := new(gobResponse)
	r.Status = resp.Status
	r.StatusCode = resp.StatusCode
	r.Proto = resp.Proto
	r.ProtoMajor = resp.ProtoMajor
	r.ProtoMinor = resp.ProtoMinor
	r.Header = resp.Header
	r.ContentLength = resp.ContentLength
	r.TransferEncoding = resp.TransferEncoding
	r.Close = resp.Close
	r.Trailer = resp.Trailer
	newGobResponseVS(resp, r)

	return r
}
<commit_msg>remove bytesBufferCloser<commit_after>// Copyright 2015 ENDOH takanao.
// <https://github.com/MiCHiLU/go-gob-http>
//
// Copyright 2014 Orchestrate, Inc.
// <https://github.com/orchestrate-io/dvr>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dvr

import (
	"bytes"
	"crypto/rsa"
	"crypto/tls"
	"encoding/gob"
	"errors"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"reflect"
)

//
// Error wrapper
//

// This is a safe type that will always be able to be gob encoded.
type gobSafeError string

// Error() for gobSafeError
func (g gobSafeError) Error() string {
	return string(g)
}

// This is the list of known encodable types saved as a map of name -> bool.
// This allows us to know if a given type will be decodable or not.. If not
// then we need to wrap the type in a gobSafeError structure.
var encodableTypes map[string]bool = map[string]bool{}

// Initialize encodableTypes
func init() {
	// Error return types.
	RegisterErrorType(new(gobSafeError))
	RegisterErrorType(new(http.ProtocolError))
	RegisterErrorType(new(net.AddrError))
	RegisterErrorType(new(net.DNSConfigError))
	RegisterErrorType(new(net.DNSError))
	RegisterErrorType(new(net.DNSError))
	RegisterErrorType(new(net.InvalidAddrError))
	RegisterErrorType(new(net.OpError))
	RegisterErrorType(new(net.ParseError))
	RegisterErrorType(new(net.UnknownNetworkError))
	RegisterErrorType(new(url.Error))
	RegisterErrorType(new(url.EscapeError))

	// Other objects that we might end up seeing.
	gob.Register(new(rsa.PublicKey))
	gob.Register(new(rsa.PrivateKey))
	gob.Register(new(net.TCPAddr))
}

// Adds an error interface object to the list of known types that this library
// will be able to encode. This is necessary due to the way that gob encodes
// interface object. The only error types here are those that will be returned
// from the RoundTripper object. Typical use cases should not need this at all.
// If you are using this you must do it via your modules init() otherwise
// results can be unpredictable.
func RegisterErrorType(err error) {
	// Walk the given interface all the way down to the raw object.
	value := reflect.ValueOf(err)
	for value.Kind() == reflect.Interface || value.Kind() == reflect.Ptr {
		if _, ok := value.Interface().(error); ok {
			gob.Register(value.Interface())
		}
		value = value.Elem()
	}
	typ := value.Type()
	id := fmt.Sprintf("%s.%s", typ.PkgPath(), typ.Name())
	encodableTypes[id] = true
}

// This type is used to store errors. Since some errors might contain private
// fields we need to ensure that we can still convert them as best as possible.
// Specifically this will convert them to a string error.
type gobError struct {
	Error error
}

// The encoder that allows us to safely convert errors.
func (g *gobError) GobEncode() ([]byte, error) {
	if g.Error == nil {
		return []byte{}, nil
	}

	// If we are encoding a known safe type then we write the types name and
	// then encode it into the byte stream, otherwise we are forced to convert
	// it into a gobSafeError type so it can be safely stored.
	value := reflect.ValueOf(g.Error)
	for value.Kind() == reflect.Interface || value.Kind() == reflect.Ptr {
		value = value.Elem()
	}

	// Figure out the name of this object type.
	id := fmt.Sprintf("%s.%s", value.Type().PkgPath(), value.Type().Name())

	// Make a safe error object for us to encode with.
	rawError := gobRawError{
		Error:             g.Error,
		ErrorsErrorString: id == "errors.errorString",
	}

	// If the object that we are encoding is not safe then we need to change
	// it into one that actually is.
	if _, ok := encodableTypes[id]; !ok {
		rawError.Error = gobSafeError(g.Error.Error())
	}

	// Encode the safe object and return the byte array.
	buffer := bytes.Buffer{}
	encoder := gob.NewEncoder(&buffer)
	err := encoder.Encode(&rawError)
	return buffer.Bytes(), err
}

// Decodes a gobError struct
func (g *gobError) GobDecode(data []byte) error {
	if len(data) == 0 {
		g.Error = nil
		return nil
	}
	buffer := bytes.NewBuffer(data)
	decoder := gob.NewDecoder(buffer)
	var rawError gobRawError
	if err := decoder.Decode(&rawError); err != nil {
		return err
	}

	if rawError.ErrorsErrorString {
		g.Error = errors.New(rawError.Error.Error())
	} else {
		g.Error = rawError.Error
	}
	return nil
}

// This is the object type that gobError will use when encoding and decoding.
// The assumption here is that the Error field in this object will be populated
// only with safe error types.
type gobRawError struct {
	Error error

	// This is set to true if the error was initially a 'errors.errorString'
	// so we know that we can convert it back in the decoding process.
	ErrorsErrorString bool
}

//
// Request wrapper
//

// This is a gob encodable version of http.Request. It removes the possibility
// that a private field can be seen and therefor return an error.
type gobRequest struct {
	Method           string
	URL              string
	Proto            string
	ProtoMajor       int
	ProtoMinor       int
	Header           http.Header
	ContentLength    int64
	TransferEncoding []string
	Close            bool
	Host             string
	Form             url.Values
	PostForm         url.Values
	Trailer          http.Header
	RemoteAddr       string
	RequestURI       string
	TLS              *tls.ConnectionState

	// The request body and err returned when reading it.
	Body  []byte
	Error gobError
}

// This takes a Request object and returns a gob compatible gobRequest object.
func newGobRequest(req *http.Request) *gobRequest {
	if req == nil {
		return nil
	}

	r := new(gobRequest)
	r.Method = req.Method
	r.URL = req.URL.String()
	r.Proto = req.Proto
	r.ProtoMajor = req.ProtoMajor
	r.ProtoMinor = req.ProtoMinor
	r.Header = req.Header
	r.ContentLength = req.ContentLength
	r.TransferEncoding = req.TransferEncoding
	r.Close = req.Close
	r.Host = req.Host
	r.Form = req.Form
	r.PostForm = req.PostForm
	r.Trailer = req.Trailer
	r.RemoteAddr = req.RemoteAddr
	r.RequestURI = req.RequestURI
	newGobRequestVS(req, r)

	return r
}

//
// Response wrapper
//

// This object wraps an http.Response object in order to make it possible to
// gob encode it.
type gobResponse struct {
	Status           string
	StatusCode       int
	Proto            string
	ProtoMajor       int
	ProtoMinor       int
	Header           http.Header
	ContentLength    int64
	TransferEncoding []string
	Close            bool
	Trailer          http.Header
	TLS              *tls.ConnectionState

	// The response body and err returned when reading it.
	Body  []byte
	Error gobError
}

// This takes a Response object and returns a gob compatible gobResponse object.
func newGobResponse(resp *http.Response) *gobResponse {
	if resp == nil {
		return nil
	}

	r := new(gobResponse)
	r.Status = resp.Status
	r.StatusCode = resp.StatusCode
	r.Proto = resp.Proto
	r.ProtoMajor = resp.ProtoMajor
	r.ProtoMinor = resp.ProtoMinor
	r.Header = resp.Header
	r.ContentLength = resp.ContentLength
	r.TransferEncoding = resp.TransferEncoding
	r.Close = resp.Close
	r.Trailer = resp.Trailer
	newGobResponseVS(resp, r)

	return r
}
<|endoftext|>