<commit_before>package hijacker

import (
	"crypto/tls"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/concourse/atc"
	"github.com/concourse/fly/pty"
	"github.com/concourse/fly/rc"
	"github.com/gorilla/websocket"
	"github.com/mgutz/ansi"
	"github.com/tedsuo/rata"
)

type ProcessIO struct {
	In  io.Reader
	Out io.Writer
	Err io.Writer
}

type Hijacker struct {
	tlsConfig        *tls.Config
	requestGenerator *rata.RequestGenerator
	token            *rc.TargetToken

	interval         time.Duration
}

func New(tlsConfig *tls.Config, requestGenerator *rata.RequestGenerator, token *rc.TargetToken) *Hijacker {
	return &Hijacker{
		tlsConfig:        tlsConfig,
		requestGenerator: requestGenerator,
		token:            token,
		interval:         10 * time.Second,
	}
}

func (h *Hijacker) SetHeartbeatInterval(interval time.Duration) {
	h.interval = interval
}

func (h *Hijacker) Hijack(handle string, spec atc.HijackProcessSpec, pio ProcessIO) (int, error) {
	url, header, err := h.hijackRequestParts(handle)
	if err != nil {
		return -1, err
	}

	dialer := websocket.Dialer{
		TLSClientConfig: h.tlsConfig,
		Proxy: http.ProxyFromEnvironment,
	}
	conn, _, err := dialer.Dial(url, header)
	if err != nil {
		return -1, err
	}

	defer conn.Close()

	err = conn.WriteJSON(spec)
	if err != nil {
		return -1, err
	}

	inputs := make(chan atc.HijackInput, 1)
	finished := make(chan struct{}, 1)

	go h.monitorTTYSize(inputs, finished)
	go func() {
		io.Copy(&stdinWriter{inputs}, pio.In)
		inputs <- atc.HijackInput{Closed: true}
	}()
	go h.handleInput(conn, inputs, finished)

	exitStatus := h.handleOutput(conn, pio)

	close(finished)

	return exitStatus, nil
}

func (h *Hijacker) hijackRequestParts(handle string) (string, http.Header, error) {
	hijackReq, _ := h.requestGenerator.CreateRequest(
		atc.HijackContainer,
		rata.Params{"id": handle},
		nil,
	)

	if h.token != nil {
		hijackReq.Header.Add("Authorization", h.token.Type + " " + h.token.Value)
	}

	wsUrl := hijackReq.URL

	var found bool
	wsUrl.Scheme, found = websocketSchemeMap[wsUrl.Scheme]
	if !found {
		return "", nil, fmt.Errorf("unknown target scheme: %s", wsUrl.Scheme)
	}

	return wsUrl.String(), hijackReq.Header, nil
}

func (h *Hijacker) handleOutput(conn *websocket.Conn, pio ProcessIO) int {
	var exitStatus int
	for {
		var output atc.HijackOutput
		err := conn.ReadJSON(&output)
		if err != nil {
			if !websocket.IsCloseError(err) && !websocket.IsUnexpectedCloseError(err) {
				fmt.Println(err)
			}
			break
		}

		if output.ExitStatus != nil {
			exitStatus = *output.ExitStatus
		} else if len(output.Error) > 0 {
			fmt.Fprintf(os.Stderr, "%s\n", ansi.Color(output.Error, "red+b"))
			exitStatus = 255
		} else if len(output.Stdout) > 0 {
			pio.Out.Write(output.Stdout)
		} else if len(output.Stderr) > 0 {
			pio.Err.Write(output.Stderr)
		}
	}

	return exitStatus
}

func (h *Hijacker) handleInput(conn *websocket.Conn, inputs <-chan atc.HijackInput, finished chan struct{}) {
	ticker := time.NewTicker(h.interval)
	defer ticker.Stop()

	for {
		select {
		case input := <-inputs:
			err := conn.WriteJSON(input)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to send input: %s", err.Error())
				return
			}
		case t := <-ticker.C:
			err := conn.WriteControl(websocket.PingMessage, []byte(t.String()), time.Now().Add(time.Second))
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to send heartbeat: %s", err.Error())
			}
		case <-finished:
			return
		}
	}
}

func (h *Hijacker) monitorTTYSize(inputs chan <- atc.HijackInput, finished chan struct{}) {
	resized := pty.ResizeNotifier()

	for {
		select {
		case <-resized:
			rows, cols, err := pty.Getsize(os.Stdin)
			if err == nil {
				inputs <- atc.HijackInput{
					TTYSpec: &atc.HijackTTYSpec{
						WindowSize: atc.HijackWindowSize{
							Columns: cols,
							Rows:    rows,
						},
					},
				}
			}
		case <-finished:
			return
		}
	}
}

type stdinWriter struct {
	inputs chan <- atc.HijackInput
}

func (w *stdinWriter) Write(d []byte) (int, error) {
	w.inputs <- atc.HijackInput{
		Stdin: d,
	}

	return len(d), nil
}

var websocketSchemeMap = map[string]string{
	"http":  "ws",
	"https": "wss",
}
<commit_msg>go fmt<commit_after>package hijacker

import (
	"crypto/tls"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/concourse/atc"
	"github.com/concourse/fly/pty"
	"github.com/concourse/fly/rc"
	"github.com/gorilla/websocket"
	"github.com/mgutz/ansi"
	"github.com/tedsuo/rata"
)

type ProcessIO struct {
	In  io.Reader
	Out io.Writer
	Err io.Writer
}

type Hijacker struct {
	tlsConfig        *tls.Config
	requestGenerator *rata.RequestGenerator
	token            *rc.TargetToken

	interval time.Duration
}

func New(tlsConfig *tls.Config, requestGenerator *rata.RequestGenerator, token *rc.TargetToken) *Hijacker {
	return &Hijacker{
		tlsConfig:        tlsConfig,
		requestGenerator: requestGenerator,
		token:            token,
		interval:         10 * time.Second,
	}
}

func (h *Hijacker) SetHeartbeatInterval(interval time.Duration) {
	h.interval = interval
}

func (h *Hijacker) Hijack(handle string, spec atc.HijackProcessSpec, pio ProcessIO) (int, error) {
	url, header, err := h.hijackRequestParts(handle)
	if err != nil {
		return -1, err
	}

	dialer := websocket.Dialer{
		TLSClientConfig: h.tlsConfig,
		Proxy:           http.ProxyFromEnvironment,
	}
	conn, _, err := dialer.Dial(url, header)
	if err != nil {
		return -1, err
	}

	defer conn.Close()

	err = conn.WriteJSON(spec)
	if err != nil {
		return -1, err
	}

	inputs := make(chan atc.HijackInput, 1)
	finished := make(chan struct{}, 1)

	go h.monitorTTYSize(inputs, finished)
	go func() {
		io.Copy(&stdinWriter{inputs}, pio.In)
		inputs <- atc.HijackInput{Closed: true}
	}()
	go h.handleInput(conn, inputs, finished)

	exitStatus := h.handleOutput(conn, pio)

	close(finished)

	return exitStatus, nil
}

func (h *Hijacker) hijackRequestParts(handle string) (string, http.Header, error) {
	hijackReq, _ := h.requestGenerator.CreateRequest(
		atc.HijackContainer,
		rata.Params{"id": handle},
		nil,
	)

	if h.token != nil {
		hijackReq.Header.Add("Authorization", h.token.Type+" "+h.token.Value)
	}

	wsUrl := hijackReq.URL

	var found bool
	wsUrl.Scheme, found = websocketSchemeMap[wsUrl.Scheme]
	if !found {
		return "", nil, fmt.Errorf("unknown target scheme: %s", wsUrl.Scheme)
	}

	return wsUrl.String(), hijackReq.Header, nil
}

func (h *Hijacker) handleOutput(conn *websocket.Conn, pio ProcessIO) int {
	var exitStatus int
	for {
		var output atc.HijackOutput
		err := conn.ReadJSON(&output)
		if err != nil {
			if !websocket.IsCloseError(err) && !websocket.IsUnexpectedCloseError(err) {
				fmt.Println(err)
			}
			break
		}

		if output.ExitStatus != nil {
			exitStatus = *output.ExitStatus
		} else if len(output.Error) > 0 {
			fmt.Fprintf(os.Stderr, "%s\n", ansi.Color(output.Error, "red+b"))
			exitStatus = 255
		} else if len(output.Stdout) > 0 {
			pio.Out.Write(output.Stdout)
		} else if len(output.Stderr) > 0 {
			pio.Err.Write(output.Stderr)
		}
	}

	return exitStatus
}

func (h *Hijacker) handleInput(conn *websocket.Conn, inputs <-chan atc.HijackInput, finished chan struct{}) {
	ticker := time.NewTicker(h.interval)
	defer ticker.Stop()

	for {
		select {
		case input := <-inputs:
			err := conn.WriteJSON(input)
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to send input: %s", err.Error())
				return
			}
		case t := <-ticker.C:
			err := conn.WriteControl(websocket.PingMessage, []byte(t.String()), time.Now().Add(time.Second))
			if err != nil {
				fmt.Fprintf(os.Stderr, "failed to send heartbeat: %s", err.Error())
			}
		case <-finished:
			return
		}
	}
}

func (h *Hijacker) monitorTTYSize(inputs chan<- atc.HijackInput, finished chan struct{}) {
	resized := pty.ResizeNotifier()

	for {
		select {
		case <-resized:
			rows, cols, err := pty.Getsize(os.Stdin)
			if err == nil {
				inputs <- atc.HijackInput{
					TTYSpec: &atc.HijackTTYSpec{
						WindowSize: atc.HijackWindowSize{
							Columns: cols,
							Rows:    rows,
						},
					},
				}
			}
		case <-finished:
			return
		}
	}
}

type stdinWriter struct {
	inputs chan<- atc.HijackInput
}

func (w *stdinWriter) Write(d []byte) (int, error) {
	w.inputs <- atc.HijackInput{
		Stdin: d,
	}

	return len(d), nil
}

var websocketSchemeMap = map[string]string{
	"http":  "ws",
	"https": "wss",
}
<|endoftext|>


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

package mat64

import "github.com/gonum/blas/blas64"

const (
	// regionOverlap is the panic string used for the general case
	// of a matrix region overlap between a source and destination.
	regionOverlap = "mat64: bad region: overlap"

	// regionIdentity is the panic string used for the specific
	// case of complete agreement between a source and a destination.
	regionIdentity = "mat64: bad region: identical"

	// mismatchedStrides is the panic string used for overlapping
	// data slices with differing strides.
	mismatchedStrides = "mat64: bad region: different strides"
)

// checkOverlap returns false if the receiver does not overlap data elements
// referenced by the parameter and panics otherwise.
//
// checkOverlap methods return a boolean to allow the check call to be added to a
// boolean expression, making use of short-circuit operators.

func (m *Dense) checkOverlap(a blas64.General) bool {
	mat := m.RawMatrix()
	if cap(mat.Data) == 0 || cap(a.Data) == 0 {
		return false
	}

	off := offset(mat.Data[:1], a.Data[:1])

	if off == 0 {
		// At least one element overlaps.
		if mat.Cols == a.Cols && mat.Rows == a.Rows && mat.Stride == a.Stride {
			panic(regionIdentity)
		}
		panic(regionOverlap)
	}

	if off > 0 && len(mat.Data) <= off {
		// We know m is completely before a.
		return false
	}
	if off < 0 && len(a.Data) <= -off {
		// We know m is completely after a.
		return false
	}

	if mat.Stride != a.Stride {
		// Too hard, so assume the worst.
		panic(mismatchedStrides)
	}

	if off < 0 {
		off = -off
		mat.Cols, a.Cols = a.Cols, mat.Cols
	}
	if rectanglesOverlap(off, mat.Cols, a.Cols, mat.Stride) {
		panic(regionOverlap)
	}
	return false
}

func (s *SymDense) checkOverlap(a blas64.Symmetric) bool {
	mat := s.RawSymmetric()
	if cap(mat.Data) == 0 || cap(a.Data) == 0 {
		return false
	}

	off := offset(mat.Data[:1], a.Data[:1])

	if off == 0 {
		// At least one element overlaps.
		if mat.N == a.N && mat.Stride == a.Stride {
			panic(regionIdentity)
		}
		panic(regionOverlap)
	}

	if off > 0 && len(mat.Data) <= off {
		// We know s is completely before a.
		return false
	}
	if off < 0 && len(a.Data) <= -off {
		// We know s is completely after a.
		return false
	}

	if mat.Stride != a.Stride {
		// Too hard, so assume the worst.
		panic(mismatchedStrides)
	}

	// TODO(kortschak) Make this analysis more precise.
	if off > 0 {
		off = -off
		mat.N, a.N = a.N, mat.N
	}
	if rectanglesOverlap(off, mat.N, a.N, mat.Stride) {
		panic(regionOverlap)
	}
	return false
}

func (t *TriDense) checkOverlap(a blas64.Triangular) bool {
	mat := t.RawTriangular()
	if cap(mat.Data) == 0 || cap(a.Data) == 0 {
		return false
	}

	off := offset(mat.Data[:1], a.Data[:1])

	if off == 0 {
		// At least one element overlaps.
		if mat.N == a.N && mat.Stride == a.Stride {
			panic(regionIdentity)
		}
		panic(regionOverlap)
	}

	if off > 0 && len(mat.Data) <= off {
		// We know t is completely before a.
		return false
	}
	if off < 0 && len(a.Data) <= -off {
		// We know t is completely after a.
		return false
	}

	if mat.Stride != a.Stride {
		// Too hard, so assume the worst.
		panic(mismatchedStrides)
	}

	// TODO(kortschak) Make this analysis more precise.
	if off > 0 {
		off = -off
		mat.N, a.N = a.N, mat.N
	}
	if rectanglesOverlap(off, mat.N, a.N, mat.Stride) {
		panic(regionOverlap)
	}
	return false
}

func (v *Vector) checkOverlap(a blas64.Vector) bool {
	mat := v.mat
	if cap(mat.Data) == 0 || cap(a.Data) == 0 {
		return false
	}

	off := offset(mat.Data[:1], a.Data[:1])

	if off == 0 {
		// At least one element overlaps.
		if mat.Inc == a.Inc && len(mat.Data) == len(a.Data) {
			panic(regionIdentity)
		}
		panic(regionOverlap)
	}

	if off > 0 && len(mat.Data) <= off {
		// We know v is completely before a.
		return false
	}
	if off < 0 && len(a.Data) <= -off {
		// We know v is completely after a.
		return false
	}

	if mat.Inc != a.Inc {
		// Too hard, so assume the worst.
		panic(mismatchedStrides)
	}

	if mat.Inc == 1 || off&mat.Inc == 0 {
		panic(regionOverlap)
	}
	return false
}

// rectanglesOverlap returns whether the strided rectangles a and b overlap
// when b is offset by off elements after a but has at least one element before
// the end of a. a and b have aCols and bCols respectively.
func rectanglesOverlap(off, aCols, bCols, stride int) bool {
	if stride == 1 {
		return true
	}
	aTo := aCols
	bFrom := off % stride
	bTo := (bFrom + bCols) % stride
	if bFrom < bTo {
		return aTo > bFrom
	}
	return bTo > 0
}
<commit_msg>mat64: note lack of shadow precision for triangles<commit_after>// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mat64

import "github.com/gonum/blas/blas64"

const (
	// regionOverlap is the panic string used for the general case
	// of a matrix region overlap between a source and destination.
	regionOverlap = "mat64: bad region: overlap"

	// regionIdentity is the panic string used for the specific
	// case of complete agreement between a source and a destination.
	regionIdentity = "mat64: bad region: identical"

	// mismatchedStrides is the panic string used for overlapping
	// data slices with differing strides.
	mismatchedStrides = "mat64: bad region: different strides"
)

// checkOverlap returns false if the receiver does not overlap data elements
// referenced by the parameter and panics otherwise.
//
// checkOverlap methods return a boolean to allow the check call to be added to a
// boolean expression, making use of short-circuit operators.

func (m *Dense) checkOverlap(a blas64.General) bool {
	mat := m.RawMatrix()
	if cap(mat.Data) == 0 || cap(a.Data) == 0 {
		return false
	}

	off := offset(mat.Data[:1], a.Data[:1])

	if off == 0 {
		// At least one element overlaps.
		if mat.Cols == a.Cols && mat.Rows == a.Rows && mat.Stride == a.Stride {
			panic(regionIdentity)
		}
		panic(regionOverlap)
	}

	if off > 0 && len(mat.Data) <= off {
		// We know m is completely before a.
		return false
	}
	if off < 0 && len(a.Data) <= -off {
		// We know m is completely after a.
		return false
	}

	if mat.Stride != a.Stride {
		// Too hard, so assume the worst.
		panic(mismatchedStrides)
	}

	if off < 0 {
		off = -off
		mat.Cols, a.Cols = a.Cols, mat.Cols
	}
	if rectanglesOverlap(off, mat.Cols, a.Cols, mat.Stride) {
		panic(regionOverlap)
	}
	return false
}

// BUG(kortschak): Overlap detection for symmetric and triangular matrices is not
// precise; currently overlap is detected if the bounding rectangles overlap rather
// than exact overlap between visible elements.

func (s *SymDense) checkOverlap(a blas64.Symmetric) bool {
	mat := s.RawSymmetric()
	if cap(mat.Data) == 0 || cap(a.Data) == 0 {
		return false
	}

	off := offset(mat.Data[:1], a.Data[:1])

	if off == 0 {
		// At least one element overlaps.
		if mat.N == a.N && mat.Stride == a.Stride {
			panic(regionIdentity)
		}
		panic(regionOverlap)
	}

	if off > 0 && len(mat.Data) <= off {
		// We know s is completely before a.
		return false
	}
	if off < 0 && len(a.Data) <= -off {
		// We know s is completely after a.
		return false
	}

	if mat.Stride != a.Stride {
		// Too hard, so assume the worst.
		panic(mismatchedStrides)
	}

	// TODO(kortschak) Make this analysis more precise.
	if off > 0 {
		off = -off
		mat.N, a.N = a.N, mat.N
	}
	if rectanglesOverlap(off, mat.N, a.N, mat.Stride) {
		panic(regionOverlap)
	}
	return false
}

func (t *TriDense) checkOverlap(a blas64.Triangular) bool {
	mat := t.RawTriangular()
	if cap(mat.Data) == 0 || cap(a.Data) == 0 {
		return false
	}

	off := offset(mat.Data[:1], a.Data[:1])

	if off == 0 {
		// At least one element overlaps.
		if mat.N == a.N && mat.Stride == a.Stride {
			panic(regionIdentity)
		}
		panic(regionOverlap)
	}

	if off > 0 && len(mat.Data) <= off {
		// We know t is completely before a.
		return false
	}
	if off < 0 && len(a.Data) <= -off {
		// We know t is completely after a.
		return false
	}

	if mat.Stride != a.Stride {
		// Too hard, so assume the worst.
		panic(mismatchedStrides)
	}

	// TODO(kortschak) Make this analysis more precise.
	if off > 0 {
		off = -off
		mat.N, a.N = a.N, mat.N
	}
	if rectanglesOverlap(off, mat.N, a.N, mat.Stride) {
		panic(regionOverlap)
	}
	return false
}

func (v *Vector) checkOverlap(a blas64.Vector) bool {
	mat := v.mat
	if cap(mat.Data) == 0 || cap(a.Data) == 0 {
		return false
	}

	off := offset(mat.Data[:1], a.Data[:1])

	if off == 0 {
		// At least one element overlaps.
		if mat.Inc == a.Inc && len(mat.Data) == len(a.Data) {
			panic(regionIdentity)
		}
		panic(regionOverlap)
	}

	if off > 0 && len(mat.Data) <= off {
		// We know v is completely before a.
		return false
	}
	if off < 0 && len(a.Data) <= -off {
		// We know v is completely after a.
		return false
	}

	if mat.Inc != a.Inc {
		// Too hard, so assume the worst.
		panic(mismatchedStrides)
	}

	if mat.Inc == 1 || off&mat.Inc == 0 {
		panic(regionOverlap)
	}
	return false
}

// rectanglesOverlap returns whether the strided rectangles a and b overlap
// when b is offset by off elements after a but has at least one element before
// the end of a. a and b have aCols and bCols respectively.
func rectanglesOverlap(off, aCols, bCols, stride int) bool {
	if stride == 1 {
		return true
	}
	aTo := aCols
	bFrom := off % stride
	bTo := (bFrom + bCols) % stride
	if bFrom < bTo {
		return aTo > bFrom
	}
	return bTo > 0
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"regexp"
	"strings"

	"github.com/bullettrain-sh/bullettrain-go-core/ansi"
	"github.com/bullettrain-sh/bullettrain-go-core/car_context"
	"github.com/bullettrain-sh/bullettrain-go-core/car_date"
	"github.com/bullettrain-sh/bullettrain-go-core/car_directory"
	"github.com/bullettrain-sh/bullettrain-go-core/car_os"
	"github.com/bullettrain-sh/bullettrain-go-core/car_status"
	"github.com/bullettrain-sh/bullettrain-go-core/car_time"
	"github.com/bullettrain-sh/bullettrain-go-git"
	"github.com/bullettrain-sh/bullettrain-go-golang"
	"github.com/bullettrain-sh/bullettrain-go-nodejs"
	"github.com/bullettrain-sh/bullettrain-go-php"
	"github.com/bullettrain-sh/bullettrain-go-python"
	"github.com/bullettrain-sh/bullettrain-go-ruby"
)

const defaultCarOrder = "os time date context dir python go ruby nodejs php git status"

func main() {
	if d := os.Getenv("BULLETTRAIN_NO_PAINT"); d == "true" {
		ansi.DisableColors(true)
	}

	buildAndPrintCars()
}

func buildAndPrintCars() {
	// List of cars available for use.
	trailers := carsToRender()

	// Create a channel for each car.
	noOfCars := len(trailers) * 2
	chans := make([]chan string, noOfCars)
	for i := range chans {
		chans[i] = make(chan string)
	}

	// Spin off a goroutine for each car.
	var lastSeparator bool
	paintFlipper := flipPaint()
	for j, k := 0, 0; j < noOfCars; j, k = j+2, k+1 {
		// Render car.
		go trailers[k].Render(chans[j])

		// Render separator.
		var newPaint string
		if newPaint = trailers[k].GetSeparatorPaint(); newPaint == "" {
			lastSeparator = j+2 == noOfCars
			if lastSeparator {
				newPaint = paintFlipper(
					trailers[k].GetPaint(),
					"default:default")
			} else {
				newPaint = paintFlipper(
					trailers[k].GetPaint(),
					trailers[k+1].GetPaint())
			}
		}

		sep := new(separator)
		go sep.Render(chans[j+1], newPaint, trailers[k].GetSeparatorSymbol())
	}

	var n bytes.Buffer
	n.WriteString(ansi.Reset)
	// Gather each goroutine's response through their channels,
	// keeping their order.
	for _, c := range chans {
		n.WriteString(<-c)
	}

	if l := os.Getenv("BULLETTRAIN_CARS_SEPARATE_LINE"); l != "false" {
		n.WriteRune('\n')
	}

	n.WriteString(lineEnding())
	n.WriteRune(' ')

	if d := os.Getenv("BULLETTRAIN_DEBUG"); d == "true" {
		fmt.Printf("%+ x", n.String())
		fmt.Println("")
		fmt.Printf("%+q", n.String())
	} else {
		fmt.Print(n.String())
	}
}

func pwd() string {
	cmd := exec.Command("pwd", "-P")
	pwd, err := cmd.Output()
	var d string
	if err == nil {
		d = strings.Trim(string(pwd), "\n")
	}

	return d
}

func carsOrder() (o []string) {
	if envOrder := os.Getenv("BULLETTRAIN_CARS"); envOrder == "" {
		o = strings.Split(strings.TrimSpace(defaultCarOrder), " ")
	} else {
		o = strings.Split(strings.TrimSpace(envOrder), " ")
	}

	return
}

func carsToRender() []carRenderer {
	d := pwd()
	// List of cars to be available for use.
	trailers := map[string]carRenderer{
		"context": &carContext.Car{},
		"date":    &carDate.Car{},
		"dir":     &carDirectory.Car{Pwd: d},
		"git":     &carGit.Car{Pwd: d},
		"go":      &carGo.Car{Pwd: d},
		"nodejs":  &carNodejs.Car{Pwd: d},
		"os":      &carOs.Car{},
		"php":     &carPhp.Car{Pwd: d},
		"python":  &carPython.Car{Pwd: d},
		"ruby":    &carRuby.Car{Pwd: d},
		"status":  &carStatus.Car{},
		"time":    &carTime.Car{},
	}

	var carsToRender []carRenderer
	for _, car := range carsOrder() {
		if trailers[car].CanShow() {
			carsToRender = append(carsToRender, trailers[car])
		}
	}

	return carsToRender
}

func lineEnding() string {
	u, e := user.Current()
	if e != nil {
		panic("Can't figure out current username.")
	}

	// var c string
	var l string
	if u.Username == "root" {
		if l = os.Getenv("BULLETTRAIN_PROMPT_CHAR_ROOT"); l == "" {
			l = "#"
		}

		// if c = os.Getenv("BULLETTRAIN_PROMPT_CHAR_ROOT_PAINT"); c == "" {
		// 	c = "red"
		// }
	} else {
		if l = os.Getenv("BULLETTRAIN_PROMPT_CHAR"); l == "" {
			l = "$"
		}

		// if c = os.Getenv("BULLETTRAIN_PROMPT_CHAR_PAINT"); c == "" {
		// 	c = "green"
		// }
	}

	// FIXME disabled to test no colour in the last line
	//return ansi.Color(l, c)
	return l
}

// flipPaint flips the FG and BG setup in colour strings of cars for a separator.
// Use it as a closure.
func flipPaint() func(string, string) string {
	// foregroundColor+attributes:backgroundColor+attributes
	colourExp := regexp.MustCompile(`\w*\+?\w*:?(\w*)\+?\w?`)

	flipped := func(currentPaint, nextPaint string) string {
		currentParts := colourExp.FindStringSubmatch(currentPaint)
		nextParts := colourExp.FindStringSubmatch(nextPaint)

		newFg := "default"
		if len(currentParts) == 2 && currentParts[1] != "" {
			newFg = currentParts[1]
		}

		newBg := "default"
		if len(nextParts) == 2 && nextParts[1] != "" {
			newBg = nextParts[1]
		}

		return fmt.Sprintf("%s:%s", newFg, newBg)
	}

	return flipped
}

type carRenderer interface {
	// Render builds and passes the end product of a completely composed car onto
	// the channel.
	Render(out chan<- string)

	// GetPaint returns the calculated end paint string for the car.
	GetPaint() string

	// CanShow decides if this car needs to be displayed.
	CanShow() bool

	// GetSeparatorPaint overrides the Fg/Bg colours of the right hand side
	// separator through ENV variables.
	GetSeparatorPaint() string

	// GetSeparatorSymbol overrides the symbol of the right hand side
	// separator through ENV variables.
	GetSeparatorSymbol() string
}

type separator string

func (s *separator) Render(out chan<- string, paint, symbolOverride string) {
	defer close(out)

	var symbol string
	if symbolOverride != "" {
		symbol = symbolOverride
	} else if symbol = os.Getenv("BULLETTRAIN_SEPARATOR_ICON"); symbol == "" {
		symbol = ""
	}

	out <- ansi.Color(symbol, paint)
}
<commit_msg>Introducing max cpu core usage to see if ARM processors see any benefit.<commit_after>package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"regexp"
	"runtime"
	"strings"

	"github.com/bullettrain-sh/bullettrain-go-core/ansi"
	"github.com/bullettrain-sh/bullettrain-go-core/car_context"
	"github.com/bullettrain-sh/bullettrain-go-core/car_date"
	"github.com/bullettrain-sh/bullettrain-go-core/car_directory"
	"github.com/bullettrain-sh/bullettrain-go-core/car_os"
	"github.com/bullettrain-sh/bullettrain-go-core/car_status"
	"github.com/bullettrain-sh/bullettrain-go-core/car_time"
	"github.com/bullettrain-sh/bullettrain-go-git"
	"github.com/bullettrain-sh/bullettrain-go-golang"
	"github.com/bullettrain-sh/bullettrain-go-nodejs"
	"github.com/bullettrain-sh/bullettrain-go-php"
	"github.com/bullettrain-sh/bullettrain-go-python"
	"github.com/bullettrain-sh/bullettrain-go-ruby"
)

const defaultCarOrder = "os time date context dir python go ruby nodejs php git status"

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func main() {
	if d := os.Getenv("BULLETTRAIN_NO_PAINT"); d == "true" {
		ansi.DisableColors(true)
	}

	buildAndPrintCars()
}

func buildAndPrintCars() {
	// List of cars available for use.
	trailers := carsToRender()

	// Create a channel for each car.
	noOfCars := len(trailers) * 2
	chans := make([]chan string, noOfCars)
	for i := range chans {
		chans[i] = make(chan string)
	}

	// Spin off a goroutine for each car.
	var lastSeparator bool
	paintFlipper := flipPaint()
	for j, k := 0, 0; j < noOfCars; j, k = j+2, k+1 {
		// Render car.
		go trailers[k].Render(chans[j])

		// Render separator.
		var newPaint string
		if newPaint = trailers[k].GetSeparatorPaint(); newPaint == "" {
			lastSeparator = j+2 == noOfCars
			if lastSeparator {
				newPaint = paintFlipper(
					trailers[k].GetPaint(),
					"default:default")
			} else {
				newPaint = paintFlipper(
					trailers[k].GetPaint(),
					trailers[k+1].GetPaint())
			}
		}

		sep := new(separator)
		go sep.Render(chans[j+1], newPaint, trailers[k].GetSeparatorSymbol())
	}

	var n bytes.Buffer
	n.WriteString(ansi.Reset)
	// Gather each goroutine's response through their channels,
	// keeping their order.
	for _, c := range chans {
		n.WriteString(<-c)
	}

	if l := os.Getenv("BULLETTRAIN_CARS_SEPARATE_LINE"); l != "false" {
		n.WriteRune('\n')
	}

	n.WriteString(lineEnding())
	n.WriteRune(' ')

	if d := os.Getenv("BULLETTRAIN_DEBUG"); d == "true" {
		fmt.Printf("%+ x", n.String())
		fmt.Println("")
		fmt.Printf("%+q", n.String())
	} else {
		fmt.Print(n.String())
	}
}

func pwd() string {
	cmd := exec.Command("pwd", "-P")
	pwd, err := cmd.Output()
	var d string
	if err == nil {
		d = strings.Trim(string(pwd), "\n")
	}

	return d
}

func carsOrder() (o []string) {
	if envOrder := os.Getenv("BULLETTRAIN_CARS"); envOrder == "" {
		o = strings.Split(strings.TrimSpace(defaultCarOrder), " ")
	} else {
		o = strings.Split(strings.TrimSpace(envOrder), " ")
	}

	return
}

func carsToRender() []carRenderer {
	d := pwd()
	// List of cars to be available for use.
	trailers := map[string]carRenderer{
		"context": &carContext.Car{},
		"date":    &carDate.Car{},
		"dir":     &carDirectory.Car{Pwd: d},
		"git":     &carGit.Car{Pwd: d},
		"go":      &carGo.Car{Pwd: d},
		"nodejs":  &carNodejs.Car{Pwd: d},
		"os":      &carOs.Car{},
		"php":     &carPhp.Car{Pwd: d},
		"python":  &carPython.Car{Pwd: d},
		"ruby":    &carRuby.Car{Pwd: d},
		"status":  &carStatus.Car{},
		"time":    &carTime.Car{},
	}

	var carsToRender []carRenderer
	for _, car := range carsOrder() {
		if trailers[car].CanShow() {
			carsToRender = append(carsToRender, trailers[car])
		}
	}

	return carsToRender
}

func lineEnding() string {
	u, e := user.Current()
	if e != nil {
		panic("Can't figure out current username.")
	}

	// var c string
	var l string
	if u.Username == "root" {
		if l = os.Getenv("BULLETTRAIN_PROMPT_CHAR_ROOT"); l == "" {
			l = "#"
		}

		// if c = os.Getenv("BULLETTRAIN_PROMPT_CHAR_ROOT_PAINT"); c == "" {
		// 	c = "red"
		// }
	} else {
		if l = os.Getenv("BULLETTRAIN_PROMPT_CHAR"); l == "" {
			l = "$"
		}

		// if c = os.Getenv("BULLETTRAIN_PROMPT_CHAR_PAINT"); c == "" {
		// 	c = "green"
		// }
	}

	// FIXME disabled to test no colour in the last line
	//return ansi.Color(l, c)
	return l
}

// flipPaint flips the FG and BG setup in colour strings of cars for a separator.
// Use it as a closure.
func flipPaint() func(string, string) string {
	// foregroundColor+attributes:backgroundColor+attributes
	colourExp := regexp.MustCompile(`\w*\+?\w*:?(\w*)\+?\w?`)

	flipped := func(currentPaint, nextPaint string) string {
		currentParts := colourExp.FindStringSubmatch(currentPaint)
		nextParts := colourExp.FindStringSubmatch(nextPaint)

		newFg := "default"
		if len(currentParts) == 2 && currentParts[1] != "" {
			newFg = currentParts[1]
		}

		newBg := "default"
		if len(nextParts) == 2 && nextParts[1] != "" {
			newBg = nextParts[1]
		}

		return fmt.Sprintf("%s:%s", newFg, newBg)
	}

	return flipped
}

type carRenderer interface {
	// Render builds and passes the end product of a completely composed car onto
	// the channel.
	Render(out chan<- string)

	// GetPaint returns the calculated end paint string for the car.
	GetPaint() string

	// CanShow decides if this car needs to be displayed.
	CanShow() bool

	// GetSeparatorPaint overrides the Fg/Bg colours of the right hand side
	// separator through ENV variables.
	GetSeparatorPaint() string

	// GetSeparatorSymbol overrides the symbol of the right hand side
	// separator through ENV variables.
	GetSeparatorSymbol() string
}

type separator string

func (s *separator) Render(out chan<- string, paint, symbolOverride string) {
	defer close(out)

	var symbol string
	if symbolOverride != "" {
		symbol = symbolOverride
	} else if symbol = os.Getenv("BULLETTRAIN_SEPARATOR_ICON"); symbol == "" {
		symbol = ""
	}

	out <- ansi.Color(symbol, paint)
}
<|endoftext|>


<commit_before>package autotag

import (
	"testing"

	"github.com/hashicorp/go-version"
)

// whitebox testing for autotag bump interface
func TestMinorBumper(t *testing.T) {
	for k, v := range map[string]string{
		"1":      "1.1.0",
		"1.0":    "1.1.0",
		"1.0.0":  "1.1.0",
		"1.0.12": "1.1.0",
		// TODO:(jnelson) support tagging patch/build semver stuff Mon Sep 14 13:27:50 2015
		"1.0.0-patch":    "1.1.0",
		"1.0.0+build123": "1.1.0",
	} {
		tv, err := version.NewVersion(k)
		checkFatal(t, err)

		nv, err := minorBumper.bump(tv)
		checkFatal(t, err)

		if nv.String() != v {
			t.Fatalf("Expected '%s' got '%s'", v, nv.String())
		}
	}
}

func TestPatchBumper(t *testing.T) {
	// in retro this didn't have to be a map, but w/e
	for k, v := range map[string]string{
		"1":              "1.0.1",
		"1.0":            "1.0.1",
		"1.0.0":          "1.0.1",
		"1.0.0-patch":    "1.0.1",
		"1.0.0+build123": "1.0.1",
	} {
		tv, err := version.NewVersion(k)
		checkFatal(t, err)

		nv, err := patchBumper.bump(tv)
		checkFatal(t, err)

		if nv.String() != v {
			t.Fatalf("Expected '%s' got '%s'", v, nv.String())
		}
	}
}

func TestMajorBumper(t *testing.T) {
	for k, v := range map[string]string{
		"1":              "2.0.0",
		"1.0":            "2.0.0",
		"1.1":            "2.0.0",
		"1.0.0":          "2.0.0",
		"1.1.0":          "2.0.0",
		"1.0.0-patch":    "2.0.0",
		"1.0.0+build123": "2.0.0",
		"1.0.12":         "2.0.0",
	} {
		tv, err := version.NewVersion(k)
		checkFatal(t, err)

		nv, err := majorBumper.bump(tv)
		checkFatal(t, err)

		if nv.String() != v {
			t.Fatalf("Expected '%s' got '%s'", v, nv.String())
		}
	}
}
<commit_msg>increase test coverage of bumper.go<commit_after>package autotag

import (
	"testing"

	"github.com/hashicorp/go-version"
)

// whitebox testing for autotag bump interface
func TestMinorBumper(t *testing.T) {
	for k, v := range map[string]string{
		"1":                  "1.1.0",
		"1.0":                "1.1.0",
		"1.0.0":              "1.1.0",
		"1.0.12":             "1.1.0",
		"1.0.0-patch":        "1.1.0",
		"1.0.0+build123":     "1.1.0",
		"1.0.0+build123.foo": "1.1.0",
		"1.0.0.0":            "1.1.0",
	} {
		tv, err := version.NewVersion(k)
		checkFatal(t, err)

		nv, err := minorBumper.bump(tv)
		checkFatal(t, err)

		if nv.String() != v {
			t.Fatalf("Expected '%s' got '%s'", v, nv.String())
		}
	}
}

func TestPatchBumper(t *testing.T) {
	// in retro this didn't have to be a map, but w/e
	for k, v := range map[string]string{
		"1":                      "1.0.1",
		"1.0":                    "1.0.1",
		"1.0.0":                  "1.0.1",
		"1.0.0-patch":            "1.0.1",
		"1.0.0+build123":         "1.0.1",
		"1.0.0+build123.foo.bar": "1.0.1",
		"1.0.0.0":                "1.0.1.0", // XXX: this passes tests but is it correct? SemVer doesn't specify behavior 4 for digit versions
	} {
		tv, err := version.NewVersion(k)
		checkFatal(t, err)

		nv, err := patchBumper.bump(tv)
		checkFatal(t, err)

		if nv.String() != v {
			t.Fatalf("Expected '%s' got '%s'", v, nv.String())
		}
	}
}

func TestMajorBumper(t *testing.T) {
	for k, v := range map[string]string{
		"1":                  "2.0.0",
		"1.0":                "2.0.0",
		"1.1":                "2.0.0",
		"1.0.0":              "2.0.0",
		"1.1.0":              "2.0.0",
		"1.0.0-patch":        "2.0.0",
		"1.0.0+build123":     "2.0.0",
		"1.0.0+build123.foo": "2.0.0",
		"1.0.12":             "2.0.0",
	} {
		tv, err := version.NewVersion(k)
		checkFatal(t, err)

		nv, err := majorBumper.bump(tv)
		checkFatal(t, err)

		if nv.String() != v {
			t.Fatalf("Expected '%s' got '%s'", v, nv.String())
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Krister Svanlund
//
//    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 matrix

import (
    "bytes"
    "crypto/rand"
    "encoding/base64"
    c "github.com/KoFish/pallium/config"
    "strings"
    "time"
)

type (
    UserID    struct{ DomainSpecificString }
    RoomID    struct{ DomainSpecificString }
    RoomAlias struct{ DomainSpecificString }
    EventID   struct{ DomainSpecificString }
)

var (
    // Default invalid Room ID
    NoRoomID RoomID = RoomID{}
    // Default invalid User ID
    NoUserID UserID = UserID{}
    // Default invalid Event ID
    NoEventID EventID = EventID{}
    // Default invalid Room Alias
    NoRoomAlias RoomAlias = RoomAlias{}
)

func NewUserID(localpart, domain string) (UserID, error) {
    dss, err := makeDSS("@", localpart, domain)
    return UserID{dss}, err
}

func ParseUserID(s string) (UserID, error) {
    dss, err := parseDSS("@", s)
    return UserID{dss}, err
}

func NewRoomID(localpart, domain string) (RoomID, error) {
    dss, err := makeDSS("!", localpart, domain)
    return RoomID{dss}, err
}

func GenerateRoomID() (RoomID, error) {
    rstr := make([]byte, 10)
    if _, err := rand.Read(rstr); err != nil {
        return NoRoomID, err
    }
    localpart := strings.Replace(base64.URLEncoding.EncodeToString(rstr), "=", "", -1)
    return NewRoomID(localpart, c.Hostname)
}

func ParseRoomID(s string) (RoomID, error) {
    dss, err := parseDSS("!", s)
    return RoomID{dss}, err
}

func NewRoomAlias(localpart, domain string) (RoomAlias, error) {
    dss, err := makeDSS("#", localpart, domain)
    return RoomAlias{dss}, err
}

func ParseRoomAlias(s string) (RoomAlias, error) {
    dss, err := parseDSS("#", s)
    return RoomAlias{dss}, err
}

func NewEventID(localpart, domain string) (EventID, error) {
    dss, err := makeDSS("$", localpart, domain)
    return EventID{dss}, err
}

func ParseEventID(s string) (EventID, error) {
    dss, err := parseDSS("$", s)
    return EventID{dss}, err
}

func toBytes(nr int64) []byte {
    var b [8]byte
    for i := 0; i < 8; i++ {
        b[i] = byte((nr >> uint(8*(7-i))) & 0xff)
    }
    return b[:]
}

func GenerateEventID() (ev EventID, err error) {
    idb := bytes.TrimLeft(toBytes(event_id_counter), "\x00")
    event_id_counter += 1
    nowb := bytes.TrimLeft(toBytes(time.Now().Unix()), "\x00")
    rstr := make([]byte, 5)
    if _, err := rand.Read(rstr); err != nil {
        return NoEventID
    }
    evid := bytes.Join([][]byte{rstr, nowb, idb}, []byte{})
    encstr := strings.Replace(base64.URLEncoding.EncodeToString(evid), "=", "", -1)
    encstr = strings.Replace(encstr, "$", ".", -1)
    encstr = strings.Replace(encstr, ":", ".", -1)
    return NewEventID(encstr, c.Hostname)
}
<commit_msg>Fixed bug with missing return value<commit_after>// Copyright 2014 Krister Svanlund
//
//    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 matrix

import (
    "bytes"
    "crypto/rand"
    "encoding/base64"
    c "github.com/KoFish/pallium/config"
    "strings"
    "time"
)

type (
    UserID    struct{ DomainSpecificString }
    RoomID    struct{ DomainSpecificString }
    RoomAlias struct{ DomainSpecificString }
    EventID   struct{ DomainSpecificString }
)

var (
    // Default invalid Room ID
    NoRoomID RoomID = RoomID{}
    // Default invalid User ID
    NoUserID UserID = UserID{}
    // Default invalid Event ID
    NoEventID EventID = EventID{}
    // Default invalid Room Alias
    NoRoomAlias RoomAlias = RoomAlias{}
)

func NewUserID(localpart, domain string) (UserID, error) {
    dss, err := makeDSS("@", localpart, domain)
    return UserID{dss}, err
}

func ParseUserID(s string) (UserID, error) {
    dss, err := parseDSS("@", s)
    return UserID{dss}, err
}

func NewRoomID(localpart, domain string) (RoomID, error) {
    dss, err := makeDSS("!", localpart, domain)
    return RoomID{dss}, err
}

func GenerateRoomID() (RoomID, error) {
    rstr := make([]byte, 10)
    if _, err := rand.Read(rstr); err != nil {
        return NoRoomID, err
    }
    localpart := strings.Replace(base64.URLEncoding.EncodeToString(rstr), "=", "", -1)
    return NewRoomID(localpart, c.Hostname)
}

func ParseRoomID(s string) (RoomID, error) {
    dss, err := parseDSS("!", s)
    return RoomID{dss}, err
}

func NewRoomAlias(localpart, domain string) (RoomAlias, error) {
    dss, err := makeDSS("#", localpart, domain)
    return RoomAlias{dss}, err
}

func ParseRoomAlias(s string) (RoomAlias, error) {
    dss, err := parseDSS("#", s)
    return RoomAlias{dss}, err
}

func NewEventID(localpart, domain string) (EventID, error) {
    dss, err := makeDSS("$", localpart, domain)
    return EventID{dss}, err
}

func ParseEventID(s string) (EventID, error) {
    dss, err := parseDSS("$", s)
    return EventID{dss}, err
}

func toBytes(nr int64) []byte {
    var b [8]byte
    for i := 0; i < 8; i++ {
        b[i] = byte((nr >> uint(8*(7-i))) & 0xff)
    }
    return b[:]
}

func GenerateEventID() (ev EventID, err error) {
    idb := bytes.TrimLeft(toBytes(event_id_counter), "\x00")
    event_id_counter += 1
    nowb := bytes.TrimLeft(toBytes(time.Now().Unix()), "\x00")
    rstr := make([]byte, 5)
    if _, err := rand.Read(rstr); err != nil {
        return NoEventID, err
    }
    evid := bytes.Join([][]byte{rstr, nowb, idb}, []byte{})
    encstr := strings.Replace(base64.URLEncoding.EncodeToString(evid), "=", "", -1)
    encstr = strings.Replace(encstr, "$", ".", -1)
    encstr = strings.Replace(encstr, ":", ".", -1)
    return NewEventID(encstr, c.Hostname)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/mat/besticon/besticon"
	"github.com/mat/besticon/besticon/iconserver/assets"
	"github.com/mat/besticon/lettericon"

	"github.com/prometheus/client_golang/prometheus/promhttp"

	// Enable runtime profiling at /debug/pprof
	_ "net/http/pprof"
)

func indexHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "" || r.URL.Path == "/" {
		renderHTMLTemplate(w, 200, indexHTML, nil)
	} else {
		renderHTMLTemplate(w, 404, notFoundHTML, nil)
	}
}

func iconsHandler(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue(urlParam)
	if len(url) == 0 {
		http.Redirect(w, r, "/", 302)
		return
	}

	finder := newIconFinder()

	formats := r.FormValue("formats")
	if formats != "" {
		finder.FormatsAllowed = strings.Split(r.FormValue("formats"), ",")
	}

	icons, e := finder.FetchIcons(url)
	switch {
	case e != nil:
		renderHTMLTemplate(w, 404, iconsHTML, pageInfo{URL: url, Error: e})
	case len(icons) == 0:
		errNoIcons := errors.New("this poor site has no icons at all :-(")
		renderHTMLTemplate(w, 404, iconsHTML, pageInfo{URL: url, Error: errNoIcons})
	default:
		addCacheControl(w, cacheDurationSeconds)
		renderHTMLTemplate(w, 200, iconsHTML, pageInfo{Icons: icons, URL: url})
	}
}

func iconHandler(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue("url")
	if len(url) == 0 {
		writeAPIError(w, 400, errors.New("need url parameter"))
		return
	}

	sizeRange, err := besticon.ParseSizeRange(r.FormValue("size"))
	if err != nil {
		writeAPIError(w, 400, errors.New("bad size parameter"))
		return
	}

	finder := newIconFinder()
	formats := r.FormValue("formats")
	if formats != "" {
		finder.FormatsAllowed = strings.Split(r.FormValue("formats"), ",")
	}

	finder.FetchIcons(url)

	icon := finder.IconInSizeRange(*sizeRange)
	if icon != nil {
		proxyWithCacheControl(w, r, icon.URL)
		return
	}

	fallbackIconURL := r.FormValue("fallback_icon_url")
	if fallbackIconURL != "" {
		proxyWithCacheControl(w, r, fallbackIconURL)
		return
	}

	iconColor := finder.MainColorForIcons()
	letter := lettericon.MainLetterFromURL(url)

	fallbackColorHex := r.FormValue("fallback_icon_color")
	if iconColor == nil && fallbackColorHex != "" {
		color, err := lettericon.ColorFromHex(fallbackColorHex)
		if err == nil {
			iconColor = color
		}
	}

	redirectPath := lettericon.IconPath(letter, fmt.Sprintf("%d", sizeRange.Perfect), iconColor)
	redirectWithCacheControl(w, r, redirectPath)
}

func popularHandler(w http.ResponseWriter, r *http.Request) {
	iconSize, err := strconv.Atoi(r.FormValue("iconsize"))
	if iconSize > besticon.MaxIconSize || iconSize < besticon.MinIconSize || err != nil {
		iconSize = 120
	}

	pageInfo := struct {
		URLs        []string
		IconSize    int
		DisplaySize int
	}{
		besticon.PopularSites,
		iconSize,
		iconSize / 2,
	}
	renderHTMLTemplate(w, 200, popularHTML, pageInfo)
}

const (
	urlParam = "url"
)

func alliconsHandler(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue(urlParam)
	if len(url) == 0 {
		errMissingURL := errors.New("need url query parameter")
		writeAPIError(w, 400, errMissingURL)
		return
	}

	finder := newIconFinder()
	formats := r.FormValue("formats")
	if formats != "" {
		finder.FormatsAllowed = strings.Split(r.FormValue("formats"), ",")
	}

	icons, e := finder.FetchIcons(url)
	if e != nil {
		writeAPIError(w, 404, e)
		return
	}

	addCacheControl(w, cacheDurationSeconds)
	writeAPIIcons(w, url, icons)
}

func lettericonHandler(w http.ResponseWriter, r *http.Request) {
	charParam, col, size := lettericon.ParseIconPath(r.URL.Path)
	if charParam == "" || col == nil || size <= 0 {
		writeAPIError(w, 400, errors.New("wrong format for lettericons/ path, must look like lettericons/M-144-EFC25D.png"))
		return
	}

	w.Header().Add(contentType, imagePNG)
	addCacheControl(w, oneYear)
	lettericon.Render(charParam, col, size, w)
}

func writeAPIError(w http.ResponseWriter, httpStatus int, e error) {
	data := struct {
		Error string `json:"error"`
	}{
		e.Error(),
	}
	renderJSONResponse(w, httpStatus, data)
}

func writeAPIIcons(w http.ResponseWriter, url string, icons []besticon.Icon) {
	// Don't return whole image data
	newIcons := []besticon.Icon{}
	for _, ico := range icons {
		newIcon := ico
		newIcon.ImageData = nil
		newIcons = append(newIcons, newIcon)
	}

	data := &struct {
		URL   string          `json:"url"`
		Icons []besticon.Icon `json:"icons"`
	}{
		url,
		newIcons,
	}
	renderJSONResponse(w, 200, data)
}

const (
	contentType     = "Content-Type"
	applicationJSON = "application/json"
	imagePNG        = "image/png"
)

func renderJSONResponse(w http.ResponseWriter, httpStatus int, data interface{}) {
	w.Header().Add(contentType, applicationJSON)
	w.WriteHeader(httpStatus)
	enc := json.NewEncoder(w)
	enc.Encode(data)
}

type pageInfo struct {
	URL   string
	Icons []besticon.Icon
	Error error
}

func (pi pageInfo) Host() string {
	u := pi.URL
	url, _ := url.Parse(u)
	if url != nil && url.Host != "" {
		return url.Host
	}
	return pi.URL
}

func (pi pageInfo) Best() string {
	if len(pi.Icons) > 0 {
		best := pi.Icons[0]
		return best.URL
	}
	return ""
}

func renderHTMLTemplate(w http.ResponseWriter, httpStatus int, templ *template.Template, data interface{}) {
	w.Header().Add(contentType, "text/html; charset=utf-8")
	w.WriteHeader(httpStatus)

	err := templ.Execute(w, data)
	if err != nil {
		err = fmt.Errorf("server: could not generate output: %s", err)
		logger.Print(err)
		w.Write([]byte(err.Error()))
	}
}

func startServer(port string) {
	registerHandler("/", indexHandler)
	registerHandler("/icons", iconsHandler)
	registerHandler("/icon", iconHandler)
	registerHandler("/popular", popularHandler)
	registerHandler("/allicons.json", alliconsHandler)
	registerHandler("/lettericons/", lettericonHandler)

	serveAsset("/pure-0.5.0-min.css", "besticon/iconserver/assets/pure-0.5.0-min.css", oneYear)
	serveAsset("/grids-responsive-0.5.0-min.css", "besticon/iconserver/assets/grids-responsive-0.5.0-min.css", oneYear)
	serveAsset("/main-min.css", "besticon/iconserver/assets/main-min.css", oneYear)

	serveAsset("/icon.svg", "besticon/iconserver/assets/icon.svg", oneYear)
	serveAsset("/favicon.ico", "besticon/iconserver/assets/favicon.ico", oneYear)
	serveAsset("/apple-touch-icon.png", "besticon/iconserver/assets/apple-touch-icon.png", oneYear)

	http.Handle("/metrics", promhttp.Handler())

	addr := "0.0.0.0:" + port
	logger.Print("Starting server on ", addr, "...")
	e := http.ListenAndServe(addr, newLoggingMux())
	if e != nil {
		logger.Fatalf("cannot start server: %s\n", e)
	}
}

const (
	cacheControl = "Cache-Control"
	oneYear      = 365 * 24 * 3600
	maxResponseBodySize = 10485760 // 10MB
)

func proxyWithCacheControl(w http.ResponseWriter, r *http.Request, redirectURL string) {
	if (os.Getenv("SERVER_MODE") == "download") {
		response, e := besticon.Get(redirectURL)
		if e != nil {
			redirectWithCacheControl(w, r, redirectURL)
			return
		}

		b, e := besticon.GetBodyBytes(response)
		if e != nil {
			redirectWithCacheControl(w, r, redirectURL)
			return
		}
		addCacheControl(w, cacheDurationSeconds)
		w.Write([]byte(b))
	} else {
		redirectWithCacheControl(w, r, redirectURL)
	}
}

func redirectWithCacheControl(w http.ResponseWriter, r *http.Request, redirectURL string) {
	addCacheControl(w, cacheDurationSeconds)
	http.Redirect(w, r, redirectURL, 302)
}

func addCacheControl(w http.ResponseWriter, maxAge int) {
	w.Header().Add(cacheControl, fmt.Sprintf("max-age=%d", maxAge))
}

func serveAsset(path string, assetPath string, maxAgeSeconds int) {
	registerHandler(path, func(w http.ResponseWriter, r *http.Request) {
		assetInfo, err := assets.AssetInfo(assetPath)
		if err != nil {
			panic(err)
		}

		addCacheControl(w, maxAgeSeconds)

		http.ServeContent(w, r, assetInfo.Name(), assetInfo.ModTime(),
			bytes.NewReader(assets.MustAsset(assetPath)))
	})
}

func registerHandler(path string, f http.HandlerFunc) {
	http.Handle(path, newPrometheusHandler(path, f))
}

func main() {
	fmt.Printf("iconserver %s (%s) (%s) - https://github.com/mat/besticon\n", besticon.VersionString, besticon.BuildDate, runtime.Version())
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	startServer(port)
}

func init() {
	indexHTML = templateFromAsset("besticon/iconserver/assets/index.html", "index.html")
	iconsHTML = templateFromAsset("besticon/iconserver/assets/icons.html", "icons.html")
	popularHTML = templateFromAsset("besticon/iconserver/assets/popular.html", "popular.html")
	notFoundHTML = templateFromAsset("besticon/iconserver/assets/not_found.html", "not_found.html")
}

func templateFromAsset(assetPath, templateName string) *template.Template {
	bytes := assets.MustAsset(assetPath)
	return template.Must(template.New(templateName).Funcs(funcMap).Parse(string(bytes)))
}

var indexHTML *template.Template
var iconsHTML *template.Template
var popularHTML *template.Template
var notFoundHTML *template.Template

var funcMap = template.FuncMap{
	"ImgWidth": imgWidth,
}

func imgWidth(i *besticon.Icon) int {
	return i.Width / 2.0
}

func newIconFinder() *besticon.IconFinder {
	finder := besticon.IconFinder{}
	if len(hostOnlyDomains) > 0 {
		finder.HostOnlyDomains = hostOnlyDomains
	}

	return &finder
}

var hostOnlyDomains []string
var cacheDurationSeconds int

func init() {
	cacheSize := os.Getenv("CACHE_SIZE_MB")
	if cacheSize == "" {
		besticon.SetCacheMaxSize(32)
	} else {
		n, _ := strconv.Atoi(cacheSize)
		besticon.SetCacheMaxSize(int64(n))
	}

	duration, e := time.ParseDuration(getenvOrFallback("HTTP_MAX_AGE_DURATION", "720h"))
	if e != nil {
		panic(e)
	}
	cacheDurationSeconds = (int)(duration.Seconds())

	hostOnlyDomains = strings.Split(os.Getenv("HOST_ONLY_DOMAINS"), ",")
}

func getenvOrFallback(key string, fallbackValue string) string {
	value := os.Getenv(key)
	if len(strings.TrimSpace(value)) != 0 {
		return value
	}
	return fallbackValue
}
<commit_msg>Delete unused constant<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/mat/besticon/besticon"
	"github.com/mat/besticon/besticon/iconserver/assets"
	"github.com/mat/besticon/lettericon"

	"github.com/prometheus/client_golang/prometheus/promhttp"

	// Enable runtime profiling at /debug/pprof
	_ "net/http/pprof"
)

func indexHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "" || r.URL.Path == "/" {
		renderHTMLTemplate(w, 200, indexHTML, nil)
	} else {
		renderHTMLTemplate(w, 404, notFoundHTML, nil)
	}
}

func iconsHandler(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue(urlParam)
	if len(url) == 0 {
		http.Redirect(w, r, "/", 302)
		return
	}

	finder := newIconFinder()

	formats := r.FormValue("formats")
	if formats != "" {
		finder.FormatsAllowed = strings.Split(r.FormValue("formats"), ",")
	}

	icons, e := finder.FetchIcons(url)
	switch {
	case e != nil:
		renderHTMLTemplate(w, 404, iconsHTML, pageInfo{URL: url, Error: e})
	case len(icons) == 0:
		errNoIcons := errors.New("this poor site has no icons at all :-(")
		renderHTMLTemplate(w, 404, iconsHTML, pageInfo{URL: url, Error: errNoIcons})
	default:
		addCacheControl(w, cacheDurationSeconds)
		renderHTMLTemplate(w, 200, iconsHTML, pageInfo{Icons: icons, URL: url})
	}
}

func iconHandler(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue("url")
	if len(url) == 0 {
		writeAPIError(w, 400, errors.New("need url parameter"))
		return
	}

	sizeRange, err := besticon.ParseSizeRange(r.FormValue("size"))
	if err != nil {
		writeAPIError(w, 400, errors.New("bad size parameter"))
		return
	}

	finder := newIconFinder()
	formats := r.FormValue("formats")
	if formats != "" {
		finder.FormatsAllowed = strings.Split(r.FormValue("formats"), ",")
	}

	finder.FetchIcons(url)

	icon := finder.IconInSizeRange(*sizeRange)
	if icon != nil {
		proxyWithCacheControl(w, r, icon.URL)
		return
	}

	fallbackIconURL := r.FormValue("fallback_icon_url")
	if fallbackIconURL != "" {
		proxyWithCacheControl(w, r, fallbackIconURL)
		return
	}

	iconColor := finder.MainColorForIcons()
	letter := lettericon.MainLetterFromURL(url)

	fallbackColorHex := r.FormValue("fallback_icon_color")
	if iconColor == nil && fallbackColorHex != "" {
		color, err := lettericon.ColorFromHex(fallbackColorHex)
		if err == nil {
			iconColor = color
		}
	}

	redirectPath := lettericon.IconPath(letter, fmt.Sprintf("%d", sizeRange.Perfect), iconColor)
	redirectWithCacheControl(w, r, redirectPath)
}

func popularHandler(w http.ResponseWriter, r *http.Request) {
	iconSize, err := strconv.Atoi(r.FormValue("iconsize"))
	if iconSize > besticon.MaxIconSize || iconSize < besticon.MinIconSize || err != nil {
		iconSize = 120
	}

	pageInfo := struct {
		URLs        []string
		IconSize    int
		DisplaySize int
	}{
		besticon.PopularSites,
		iconSize,
		iconSize / 2,
	}
	renderHTMLTemplate(w, 200, popularHTML, pageInfo)
}

const (
	urlParam = "url"
)

func alliconsHandler(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue(urlParam)
	if len(url) == 0 {
		errMissingURL := errors.New("need url query parameter")
		writeAPIError(w, 400, errMissingURL)
		return
	}

	finder := newIconFinder()
	formats := r.FormValue("formats")
	if formats != "" {
		finder.FormatsAllowed = strings.Split(r.FormValue("formats"), ",")
	}

	icons, e := finder.FetchIcons(url)
	if e != nil {
		writeAPIError(w, 404, e)
		return
	}

	addCacheControl(w, cacheDurationSeconds)
	writeAPIIcons(w, url, icons)
}

func lettericonHandler(w http.ResponseWriter, r *http.Request) {
	charParam, col, size := lettericon.ParseIconPath(r.URL.Path)
	if charParam == "" || col == nil || size <= 0 {
		writeAPIError(w, 400, errors.New("wrong format for lettericons/ path, must look like lettericons/M-144-EFC25D.png"))
		return
	}

	w.Header().Add(contentType, imagePNG)
	addCacheControl(w, oneYear)
	lettericon.Render(charParam, col, size, w)
}

func writeAPIError(w http.ResponseWriter, httpStatus int, e error) {
	data := struct {
		Error string `json:"error"`
	}{
		e.Error(),
	}
	renderJSONResponse(w, httpStatus, data)
}

func writeAPIIcons(w http.ResponseWriter, url string, icons []besticon.Icon) {
	// Don't return whole image data
	newIcons := []besticon.Icon{}
	for _, ico := range icons {
		newIcon := ico
		newIcon.ImageData = nil
		newIcons = append(newIcons, newIcon)
	}

	data := &struct {
		URL   string          `json:"url"`
		Icons []besticon.Icon `json:"icons"`
	}{
		url,
		newIcons,
	}
	renderJSONResponse(w, 200, data)
}

const (
	contentType     = "Content-Type"
	applicationJSON = "application/json"
	imagePNG        = "image/png"
)

func renderJSONResponse(w http.ResponseWriter, httpStatus int, data interface{}) {
	w.Header().Add(contentType, applicationJSON)
	w.WriteHeader(httpStatus)
	enc := json.NewEncoder(w)
	enc.Encode(data)
}

type pageInfo struct {
	URL   string
	Icons []besticon.Icon
	Error error
}

func (pi pageInfo) Host() string {
	u := pi.URL
	url, _ := url.Parse(u)
	if url != nil && url.Host != "" {
		return url.Host
	}
	return pi.URL
}

func (pi pageInfo) Best() string {
	if len(pi.Icons) > 0 {
		best := pi.Icons[0]
		return best.URL
	}
	return ""
}

func renderHTMLTemplate(w http.ResponseWriter, httpStatus int, templ *template.Template, data interface{}) {
	w.Header().Add(contentType, "text/html; charset=utf-8")
	w.WriteHeader(httpStatus)

	err := templ.Execute(w, data)
	if err != nil {
		err = fmt.Errorf("server: could not generate output: %s", err)
		logger.Print(err)
		w.Write([]byte(err.Error()))
	}
}

func startServer(port string) {
	registerHandler("/", indexHandler)
	registerHandler("/icons", iconsHandler)
	registerHandler("/icon", iconHandler)
	registerHandler("/popular", popularHandler)
	registerHandler("/allicons.json", alliconsHandler)
	registerHandler("/lettericons/", lettericonHandler)

	serveAsset("/pure-0.5.0-min.css", "besticon/iconserver/assets/pure-0.5.0-min.css", oneYear)
	serveAsset("/grids-responsive-0.5.0-min.css", "besticon/iconserver/assets/grids-responsive-0.5.0-min.css", oneYear)
	serveAsset("/main-min.css", "besticon/iconserver/assets/main-min.css", oneYear)

	serveAsset("/icon.svg", "besticon/iconserver/assets/icon.svg", oneYear)
	serveAsset("/favicon.ico", "besticon/iconserver/assets/favicon.ico", oneYear)
	serveAsset("/apple-touch-icon.png", "besticon/iconserver/assets/apple-touch-icon.png", oneYear)

	http.Handle("/metrics", promhttp.Handler())

	addr := "0.0.0.0:" + port
	logger.Print("Starting server on ", addr, "...")
	e := http.ListenAndServe(addr, newLoggingMux())
	if e != nil {
		logger.Fatalf("cannot start server: %s\n", e)
	}
}

const (
	cacheControl = "Cache-Control"
	oneYear      = 365 * 24 * 3600
)

func proxyWithCacheControl(w http.ResponseWriter, r *http.Request, redirectURL string) {
	if (os.Getenv("SERVER_MODE") == "download") {
		response, e := besticon.Get(redirectURL)
		if e != nil {
			redirectWithCacheControl(w, r, redirectURL)
			return
		}

		b, e := besticon.GetBodyBytes(response)
		if e != nil {
			redirectWithCacheControl(w, r, redirectURL)
			return
		}
		addCacheControl(w, cacheDurationSeconds)
		w.Write([]byte(b))
	} else {
		redirectWithCacheControl(w, r, redirectURL)
	}
}

func redirectWithCacheControl(w http.ResponseWriter, r *http.Request, redirectURL string) {
	addCacheControl(w, cacheDurationSeconds)
	http.Redirect(w, r, redirectURL, 302)
}

func addCacheControl(w http.ResponseWriter, maxAge int) {
	w.Header().Add(cacheControl, fmt.Sprintf("max-age=%d", maxAge))
}

func serveAsset(path string, assetPath string, maxAgeSeconds int) {
	registerHandler(path, func(w http.ResponseWriter, r *http.Request) {
		assetInfo, err := assets.AssetInfo(assetPath)
		if err != nil {
			panic(err)
		}

		addCacheControl(w, maxAgeSeconds)

		http.ServeContent(w, r, assetInfo.Name(), assetInfo.ModTime(),
			bytes.NewReader(assets.MustAsset(assetPath)))
	})
}

func registerHandler(path string, f http.HandlerFunc) {
	http.Handle(path, newPrometheusHandler(path, f))
}

func main() {
	fmt.Printf("iconserver %s (%s) (%s) - https://github.com/mat/besticon\n", besticon.VersionString, besticon.BuildDate, runtime.Version())
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	startServer(port)
}

func init() {
	indexHTML = templateFromAsset("besticon/iconserver/assets/index.html", "index.html")
	iconsHTML = templateFromAsset("besticon/iconserver/assets/icons.html", "icons.html")
	popularHTML = templateFromAsset("besticon/iconserver/assets/popular.html", "popular.html")
	notFoundHTML = templateFromAsset("besticon/iconserver/assets/not_found.html", "not_found.html")
}

func templateFromAsset(assetPath, templateName string) *template.Template {
	bytes := assets.MustAsset(assetPath)
	return template.Must(template.New(templateName).Funcs(funcMap).Parse(string(bytes)))
}

var indexHTML *template.Template
var iconsHTML *template.Template
var popularHTML *template.Template
var notFoundHTML *template.Template

var funcMap = template.FuncMap{
	"ImgWidth": imgWidth,
}

func imgWidth(i *besticon.Icon) int {
	return i.Width / 2.0
}

func newIconFinder() *besticon.IconFinder {
	finder := besticon.IconFinder{}
	if len(hostOnlyDomains) > 0 {
		finder.HostOnlyDomains = hostOnlyDomains
	}

	return &finder
}

var hostOnlyDomains []string
var cacheDurationSeconds int

func init() {
	cacheSize := os.Getenv("CACHE_SIZE_MB")
	if cacheSize == "" {
		besticon.SetCacheMaxSize(32)
	} else {
		n, _ := strconv.Atoi(cacheSize)
		besticon.SetCacheMaxSize(int64(n))
	}

	duration, e := time.ParseDuration(getenvOrFallback("HTTP_MAX_AGE_DURATION", "720h"))
	if e != nil {
		panic(e)
	}
	cacheDurationSeconds = (int)(duration.Seconds())

	hostOnlyDomains = strings.Split(os.Getenv("HOST_ONLY_DOMAINS"), ",")
}

func getenvOrFallback(key string, fallbackValue string) string {
	value := os.Getenv(key)
	if len(strings.TrimSpace(value)) != 0 {
		return value
	}
	return fallbackValue
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/sendgrid/sendgrid-go"
	"io/ioutil"
	"os"
	"strings"
)

type flagSlice []string

func (f *flagSlice) String() string {
	return fmt.Sprint(*f)
}

func (f *flagSlice) Set(value string) error {
	if *f == nil {
		*f = make(flagSlice, 0)
	}
	*f = append(*f, value)
	return nil
}

func main() {
	username := os.Getenv("SENDGRID_USERNAME")
	password := os.Getenv("SENDGRID_PASSWORD")

	if username == "" {
		panic("SENDGRID_USERNAME environment variable not set")
	}

	if password == "" {
		panic("SENDGRID_PASSWORD environment variable not set")
	}

	subject := flag.String("s", "", "Subject")
	from := flag.String("f", "", "From")
	var attachments flagSlice
	flag.Var(&attachments, "a", "Attachments")
	flag.Parse()

	if *subject == "" || *from == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	sg := sendgrid.NewSendGridClient(username, password)
	message := sendgrid.NewMail()
	recipients := strings.Split(flag.Args()[len(flag.Args())-1], ",")
	for _, recipient := range recipients {
		message.AddTo(recipient)
	}

	message.SetSubject(*subject)

	b, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		panic(err)
	}
	message.SetText(string(b))
	message.SetFrom(*from)

	for _, attachment := range attachments {
		r, err := os.Open(attachment)
		if err != nil {
			panic(err)
		}
		message.AddAttachment(attachment, r)
	}

	/* Send Message */
	if r := sg.Send(message); r == nil {
		fmt.Println("Email sent!")
	} else {
		panic(r)
	}
}
<commit_msg>support for cc and bcc<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/sendgrid/sendgrid-go"
	"io/ioutil"
	"os"
	"strings"
)

type flagSlice []string

func (f *flagSlice) String() string {
	return fmt.Sprint(*f)
}

func (f *flagSlice) Set(value string) error {
	if *f == nil {
		*f = make(flagSlice, 0)
	}
	*f = append(*f, value)
	return nil
}

func main() {
	username := os.Getenv("SENDGRID_USERNAME")
	password := os.Getenv("SENDGRID_PASSWORD")

	if username == "" {
		panic("SENDGRID_USERNAME environment variable not set")
	}

	if password == "" {
		panic("SENDGRID_PASSWORD environment variable not set")
	}

	subject := flag.String("s", "", "Subject")
	from := flag.String("f", "", "From")
	ccs := flag.String("cc", "", "CC (comma delimited)")
	bccs := flag.String("bcc", "", "BCC (comma delimited)")
	var attachments flagSlice
	flag.Var(&attachments, "a", "Attachment")
	flag.Parse()

	if *subject == "" || *from == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	sg := sendgrid.NewSendGridClient(username, password)
	message := sendgrid.NewMail()
	recipients := strings.Split(flag.Args()[len(flag.Args())-1], ",")
	message.AddTos(recipients)
	message.AddCcs(strings.Split(*ccs, ","))
	message.AddBccs(strings.Split(*bccs, ","))

	message.SetSubject(*subject)

	b, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		panic(err)
	}
	message.SetText(string(b))
	message.SetFrom(*from)

	for _, attachment := range attachments {
		r, err := os.Open(attachment)
		if err != nil {
			panic(err)
		}
		message.AddAttachment(attachment, r)
	}

	/* Send Message */
	if r := sg.Send(message); r == nil {
		fmt.Println("Email sent!")
	} else {
		panic(r)
	}
}
<|endoftext|>


<commit_before>package zfs

// #include <stdlib.h>
// #include <libzfs.h>
// #include "common.h"
// #include "zpool.h"
// #include "zfs.h"
// #include <memory.h>
// #include <string.h>
import "C"
import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unsafe"
)

type SendFlags struct {
	Verbose    bool
	Replicate  bool
	DoAll      bool
	FromOrigin bool
	Dedup      bool
	Props      bool
	DryRun     bool
	Parsable   bool
	Progress   bool
	LargeBlock bool
	EmbedData  bool
	Compress   bool
}

type RecvFlags struct {
	Verbose     bool
	IsPrefix    bool
	IsTail      bool
	DryRun      bool
	Force       bool
	CanmountOff bool
	Resumable   bool
	ByteSwap    bool
	NoMount     bool
}

func to_boolean_t(a bool) C.boolean_t {
	if a {
		return 1
	}
	return 0
}

func to_sendflags_t(flags *SendFlags) (cflags *C.sendflags_t) {
	cflags = C.alloc_sendflags()
	cflags.verbose = to_boolean_t(flags.Verbose)
	cflags.replicate = to_boolean_t(flags.Replicate)
	cflags.doall = to_boolean_t(flags.DoAll)
	cflags.fromorigin = to_boolean_t(flags.FromOrigin)
	cflags.dedup = to_boolean_t(flags.Dedup)
	cflags.props = to_boolean_t(flags.Props)
	cflags.dryrun = to_boolean_t(flags.DryRun)
	cflags.parsable = to_boolean_t(flags.Parsable)
	cflags.progress = to_boolean_t(flags.Progress)
	cflags.largeblock = to_boolean_t(flags.LargeBlock)
	cflags.embed_data = to_boolean_t(flags.EmbedData)
	cflags.compress = to_boolean_t(flags.Compress)
	return
}

func to_recvflags_t(flags *RecvFlags) (cflags *C.recvflags_t) {
	cflags = C.alloc_recvflags()
	cflags.verbose = to_boolean_t(flags.Verbose)
	cflags.isprefix = to_boolean_t(flags.IsPrefix)
	cflags.istail = to_boolean_t(flags.IsTail)
	cflags.dryrun = to_boolean_t(flags.DryRun)
	cflags.force = to_boolean_t(flags.Force)
	cflags.canmountoff = to_boolean_t(flags.CanmountOff)
	// cflags.resumable = to_boolean_t(flags.Resumable)
	cflags.byteswap = to_boolean_t(flags.ByteSwap)
	cflags.nomount = to_boolean_t(flags.NoMount)
	return
}

func (d *Dataset) send(FromName string, outf *os.File, flags *SendFlags) (err error) {
	var cfromname, ctoname *C.char
	var dpath string
	var pd Dataset

	if d.Type != DatasetTypeSnapshot || (len(FromName) > 0 && strings.Contains(FromName, "#")) {
		err = fmt.Errorf(
			"Unsupported method on filesystem or bookmark. Use func SendOne() for that purpose.")
		return
	}

	cflags := to_sendflags_t(flags)
	defer C.free(unsafe.Pointer(cflags))
	if dpath, err = d.Path(); err != nil {
		return
	}
	if len(FromName) > 0 {
		if FromName[0] == '#' || FromName[0] == '@' {
			FromName = dpath + FromName
		}
		cfromname = C.CString(FromName)
		defer C.free(unsafe.Pointer(cfromname))
	}
	sendparams := strings.Split(dpath, "@")
	parent := sendparams[0]
	ctoname = C.CString(sendparams[1])
	defer C.free(unsafe.Pointer(ctoname))
	if pd, err = DatasetOpen(parent); err != nil {
		return
	}
	defer pd.Close()
	cerr := C.zfs_send(pd.list.zh, cfromname, ctoname, cflags, C.int(outf.Fd()), nil, nil, nil)
	if cerr != 0 {
		err = LastError()
	}
	return
}

func (d *Dataset) SendOne(FromName string, outf *os.File, flags *SendFlags) (err error) {
	var cfromname, ctoname *C.char
	var dpath string
	var lzc_send_flags uint32

	if d.Type == DatasetTypeSnapshot || (len(FromName) > 0 && !strings.Contains(FromName, "#")) {
		err = fmt.Errorf(
			"Unsupported with snapshot. Use func Send() for that purpose.")
		return
	}
	if flags.Replicate || flags.DoAll || flags.Props || flags.Dedup || flags.DryRun {
		err = fmt.Errorf("Unsupported flag with filesystem or bookmark.")
		return
	}

	if flags.LargeBlock {
		lzc_send_flags |= C.LZC_SEND_FLAG_LARGE_BLOCK
	}
	if flags.EmbedData {
		lzc_send_flags |= C.LZC_SEND_FLAG_EMBED_DATA
	}
	// if (flags.Compress)
	// 	lzc_send_flags |= LZC_SEND_FLAG_COMPRESS;
	if dpath, err = d.Path(); err != nil {
		return
	}
	if len(FromName) > 0 {
		if FromName[0] == '#' || FromName[0] == '@' {
			FromName = dpath + FromName
		}
		cfromname = C.CString(FromName)
		defer C.free(unsafe.Pointer(cfromname))
	}
	ctoname = C.CString(path.Base(dpath))
	defer C.free(unsafe.Pointer(ctoname))
	cerr := C.zfs_send_one(d.list.zh, cfromname, C.int(outf.Fd()), lzc_send_flags)
	if cerr != 0 {
		err = LastError()
	}
	return
}

func (d *Dataset) Send(outf *os.File, flags SendFlags) (err error) {
	if flags.Replicate {
		flags.DoAll = true
	}
	err = d.send("", outf, &flags)
	return
}

func (d *Dataset) SendFrom(FromName string, outf *os.File, flags SendFlags) (err error) {
	var porigin Property
	var from, dest []string
	if err = d.ReloadProperties(); err != nil {
		return
	}
	porigin, _ = d.GetProperty(DatasetPropOrigin)
	if len(porigin.Value) > 0 && porigin.Value == FromName {
		FromName = ""
		flags.FromOrigin = true
	} else {
		var dpath string
		if dpath, err = d.Path(); err != nil {
			return
		}
		dest = strings.Split(dpath, "@")
		from = strings.Split(FromName, "@")

		if len(from[0]) > 0 && from[0] != dest[0] {
			err = fmt.Errorf("Incremental source must be in same filesystem.")
			return
		}
		if len(from) < 2 || strings.Contains(from[1], "@") || strings.Contains(from[1], "/") {
			err = fmt.Errorf("Invalid incremental source.")
			return
		}
	}
	err = d.send(from[1], outf, &flags)
	return
}

// SendSize - estimate snapshot size to transfer
func (d *Dataset) SendSize(FromName string, flags SendFlags) (size int64, err error) {
	var r, w *os.File
	errch := make(chan error)
	defer func() {
		select {
		case <-errch:
		default:
		}
		close(errch)
	}()
	flags.DryRun = true
	flags.Verbose = true
	flags.Progress = true
	flags.Parsable = true
	if r, w, err = os.Pipe(); err != nil {
		return
	}
	defer r.Close()
	go func() {
		var tmpe error
		saveOut := C.dup(C.fileno(C.stdout))
		if res := C.dup2(C.int(w.Fd()), C.fileno(C.stdout)); res < 0 {
			tmpe = fmt.Errorf("Redirection of zfslib stdout failed %d", res)
		} else {
			tmpe = d.send(FromName, w, &flags)
			C.fflush(C.stdout)
			C.dup2(saveOut, C.fileno(C.stdout))
		}
		w.Close()
		errch <- tmpe
	}()

	r.SetReadDeadline(time.Now().Add(15 * time.Second))
	var data []byte
	if data, err = ioutil.ReadAll(r); err != nil {
		return
	}
	// parse size
	var sizeRe *regexp.Regexp
	if sizeRe, err = regexp.Compile("size[ \t]*([0-9]+)"); err != nil {
		return
	}
	matches := sizeRe.FindAllSubmatch(data, 3)
	if len(matches) > 0 && len(matches[0]) > 1 {
		if size, err = strconv.ParseInt(
			string(matches[0][1]), 10, 64); err != nil {
			return
		}
	}
	err = <-errch
	return
}

// Receive - receive snapshot stream
func (d *Dataset) Receive(inf *os.File, flags RecvFlags) (err error) {
	var dpath string
	if dpath, err = d.Path(); err != nil {
		return
	}
	props := C.new_property_nvlist()
	if props == nil {
		err = fmt.Errorf("Out of memory func (d *Dataset) Recv()")
		return
	}
	defer C.nvlist_free(props)
	cflags := to_recvflags_t(&flags)
	defer C.free(unsafe.Pointer(cflags))
	dest := C.CString(dpath)
	defer C.free(unsafe.Pointer(dest))
	ec := C.zfs_receive(C.libzfsHandle, dest, nil, cflags, C.int(inf.Fd()), nil)
	if ec != 0 {
		err = fmt.Errorf("ZFS receive of %s failed. %s", C.GoString(dest), LastError().Error())
	}
	return
}
<commit_msg>Fix problem with from snapshot name interpretation on incremental syncs<commit_after>package zfs

// #include <stdlib.h>
// #include <libzfs.h>
// #include "common.h"
// #include "zpool.h"
// #include "zfs.h"
// #include <memory.h>
// #include <string.h>
import "C"
import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unsafe"
)

type SendFlags struct {
	Verbose    bool
	Replicate  bool
	DoAll      bool
	FromOrigin bool
	Dedup      bool
	Props      bool
	DryRun     bool
	Parsable   bool
	Progress   bool
	LargeBlock bool
	EmbedData  bool
	Compress   bool
}

type RecvFlags struct {
	Verbose     bool
	IsPrefix    bool
	IsTail      bool
	DryRun      bool
	Force       bool
	CanmountOff bool
	Resumable   bool
	ByteSwap    bool
	NoMount     bool
}

func to_boolean_t(a bool) C.boolean_t {
	if a {
		return 1
	}
	return 0
}

func to_sendflags_t(flags *SendFlags) (cflags *C.sendflags_t) {
	cflags = C.alloc_sendflags()
	cflags.verbose = to_boolean_t(flags.Verbose)
	cflags.replicate = to_boolean_t(flags.Replicate)
	cflags.doall = to_boolean_t(flags.DoAll)
	cflags.fromorigin = to_boolean_t(flags.FromOrigin)
	cflags.dedup = to_boolean_t(flags.Dedup)
	cflags.props = to_boolean_t(flags.Props)
	cflags.dryrun = to_boolean_t(flags.DryRun)
	cflags.parsable = to_boolean_t(flags.Parsable)
	cflags.progress = to_boolean_t(flags.Progress)
	cflags.largeblock = to_boolean_t(flags.LargeBlock)
	cflags.embed_data = to_boolean_t(flags.EmbedData)
	cflags.compress = to_boolean_t(flags.Compress)
	return
}

func to_recvflags_t(flags *RecvFlags) (cflags *C.recvflags_t) {
	cflags = C.alloc_recvflags()
	cflags.verbose = to_boolean_t(flags.Verbose)
	cflags.isprefix = to_boolean_t(flags.IsPrefix)
	cflags.istail = to_boolean_t(flags.IsTail)
	cflags.dryrun = to_boolean_t(flags.DryRun)
	cflags.force = to_boolean_t(flags.Force)
	cflags.canmountoff = to_boolean_t(flags.CanmountOff)
	// cflags.resumable = to_boolean_t(flags.Resumable)
	cflags.byteswap = to_boolean_t(flags.ByteSwap)
	cflags.nomount = to_boolean_t(flags.NoMount)
	return
}

func (d *Dataset) send(FromName string, outf *os.File, flags *SendFlags) (err error) {
	var cfromname, ctoname *C.char
	var dpath string
	var pd Dataset

	if d.Type != DatasetTypeSnapshot || (len(FromName) > 0 && strings.Contains(FromName, "#")) {
		err = fmt.Errorf(
			"Unsupported method on filesystem or bookmark. Use func SendOne() for that purpose.")
		return
	}

	cflags := to_sendflags_t(flags)
	defer C.free(unsafe.Pointer(cflags))
	if dpath, err = d.Path(); err != nil {
		return
	}
	sendparams := strings.Split(dpath, "@")
	parent := sendparams[0]
	if len(FromName) > 0 {
		if FromName[0] == '@' {
			FromName = FromName[1:]
		} else if strings.Contains(FromName, "/") {
			from := strings.Split(FromName, "@")
			if len(from) > 0 {
				FromName = from[1]
			}
		}
		cfromname = C.CString(FromName)
		defer C.free(unsafe.Pointer(cfromname))
	}
	ctoname = C.CString(sendparams[1])
	defer C.free(unsafe.Pointer(ctoname))
	if pd, err = DatasetOpen(parent); err != nil {
		return
	}
	defer pd.Close()
	cerr := C.zfs_send(pd.list.zh, cfromname, ctoname, cflags, C.int(outf.Fd()), nil, nil, nil)
	if cerr != 0 {
		err = LastError()
	}
	return
}

func (d *Dataset) SendOne(FromName string, outf *os.File, flags *SendFlags) (err error) {
	var cfromname, ctoname *C.char
	var dpath string
	var lzc_send_flags uint32

	if d.Type == DatasetTypeSnapshot || (len(FromName) > 0 && !strings.Contains(FromName, "#")) {
		err = fmt.Errorf(
			"Unsupported with snapshot. Use func Send() for that purpose.")
		return
	}
	if flags.Replicate || flags.DoAll || flags.Props || flags.Dedup || flags.DryRun {
		err = fmt.Errorf("Unsupported flag with filesystem or bookmark.")
		return
	}

	if flags.LargeBlock {
		lzc_send_flags |= C.LZC_SEND_FLAG_LARGE_BLOCK
	}
	if flags.EmbedData {
		lzc_send_flags |= C.LZC_SEND_FLAG_EMBED_DATA
	}
	// if (flags.Compress)
	// 	lzc_send_flags |= LZC_SEND_FLAG_COMPRESS;
	if dpath, err = d.Path(); err != nil {
		return
	}
	if len(FromName) > 0 {
		if FromName[0] == '#' || FromName[0] == '@' {
			FromName = dpath + FromName
		}
		cfromname = C.CString(FromName)
		defer C.free(unsafe.Pointer(cfromname))
	}
	ctoname = C.CString(path.Base(dpath))
	defer C.free(unsafe.Pointer(ctoname))
	cerr := C.zfs_send_one(d.list.zh, cfromname, C.int(outf.Fd()), lzc_send_flags)
	if cerr != 0 {
		err = LastError()
	}
	return
}

func (d *Dataset) Send(outf *os.File, flags SendFlags) (err error) {
	if flags.Replicate {
		flags.DoAll = true
	}
	err = d.send("", outf, &flags)
	return
}

func (d *Dataset) SendFrom(FromName string, outf *os.File, flags SendFlags) (err error) {
	var porigin Property
	var from, dest []string
	if err = d.ReloadProperties(); err != nil {
		return
	}
	porigin, _ = d.GetProperty(DatasetPropOrigin)
	if len(porigin.Value) > 0 && porigin.Value == FromName {
		FromName = ""
		flags.FromOrigin = true
	} else {
		var dpath string
		if dpath, err = d.Path(); err != nil {
			return
		}
		dest = strings.Split(dpath, "@")
		from = strings.Split(FromName, "@")

		if len(from[0]) > 0 && from[0] != dest[0] {
			err = fmt.Errorf("Incremental source must be in same filesystem.")
			return
		}
		if len(from) < 2 || strings.Contains(from[1], "@") || strings.Contains(from[1], "/") {
			err = fmt.Errorf("Invalid incremental source.")
			return
		}
	}
	err = d.send("@"+from[1], outf, &flags)
	return
}

// SendSize - estimate snapshot size to transfer
func (d *Dataset) SendSize(FromName string, flags SendFlags) (size int64, err error) {
	var r, w *os.File
	errch := make(chan error)
	defer func() {
		select {
		case <-errch:
		default:
		}
		close(errch)
	}()
	flags.DryRun = true
	flags.Verbose = true
	flags.Progress = true
	flags.Parsable = true
	if r, w, err = os.Pipe(); err != nil {
		return
	}
	defer r.Close()
	go func() {
		var tmpe error
		saveOut := C.dup(C.fileno(C.stdout))
		if res := C.dup2(C.int(w.Fd()), C.fileno(C.stdout)); res < 0 {
			tmpe = fmt.Errorf("Redirection of zfslib stdout failed %d", res)
		} else {
			tmpe = d.send(FromName, w, &flags)
			C.fflush(C.stdout)
			C.dup2(saveOut, C.fileno(C.stdout))
		}
		w.Close()
		errch <- tmpe
	}()

	r.SetReadDeadline(time.Now().Add(15 * time.Second))
	var data []byte
	if data, err = ioutil.ReadAll(r); err != nil {
		return
	}
	// parse size
	var sizeRe *regexp.Regexp
	if sizeRe, err = regexp.Compile("size[ \t]*([0-9]+)"); err != nil {
		return
	}
	matches := sizeRe.FindAllSubmatch(data, 3)
	if len(matches) > 0 && len(matches[0]) > 1 {
		if size, err = strconv.ParseInt(
			string(matches[0][1]), 10, 64); err != nil {
			return
		}
	}
	err = <-errch
	return
}

// Receive - receive snapshot stream
func (d *Dataset) Receive(inf *os.File, flags RecvFlags) (err error) {
	var dpath string
	if dpath, err = d.Path(); err != nil {
		return
	}
	props := C.new_property_nvlist()
	if props == nil {
		err = fmt.Errorf("Out of memory func (d *Dataset) Recv()")
		return
	}
	defer C.nvlist_free(props)
	cflags := to_recvflags_t(&flags)
	defer C.free(unsafe.Pointer(cflags))
	dest := C.CString(dpath)
	defer C.free(unsafe.Pointer(dest))
	ec := C.zfs_receive(C.libzfsHandle, dest, nil, cflags, C.int(inf.Fd()), nil)
	if ec != 0 {
		err = fmt.Errorf("ZFS receive of %s failed. %s", C.GoString(dest), LastError().Error())
	}
	return
}
<|endoftext|>


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

package sessions

import (
	"encoding/gob"
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/context"
)

// Default flashes key.
const flashesKey = "_flash"

// Options --------------------------------------------------------------------

// Options stores configuration for a session or session store.
//
// Fields are a subset of http.Cookie fields.
type Options struct {
	Path   string
	Domain string
	// MaxAge=0 means no 'Max-Age' attribute specified.
	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'.
	// MaxAge>0 means Max-Age attribute present and given in seconds.
	MaxAge   int
	Secure   bool
	HttpOnly bool
}

// Session --------------------------------------------------------------------

// NewSession is called by session stores to create a new session instance.
func NewSession(store Store, name string) *Session {
	return &Session{
		Values:  make(map[interface{}]interface{}),
		store:   store,
		name:    name,
		Options: new(Options),
	}
}

// Session stores the values and optional configuration for a session.
type Session struct {
	// The ID of the session, generated by stores. It should not be used for
	// user data.
	ID string
	// Values contains the user-data for the session.
	Values  map[interface{}]interface{}
	Options *Options
	IsNew   bool
	store   Store
	name    string
}

// Flashes returns a slice of flash messages from the session.
//
// A single variadic argument is accepted, and it is optional: it defines
// the flash key. If not defined "_flash" is used by default.
func (s *Session) Flashes(vars ...string) []interface{} {
	var flashes []interface{}
	key := flashesKey
	if len(vars) > 0 {
		key = vars[0]
	}
	if v, ok := s.Values[key]; ok {
		// Drop the flashes and return it.
		delete(s.Values, key)
		flashes = v.([]interface{})
	}
	return flashes
}

// AddFlash adds a flash message to the session.
//
// A single variadic argument is accepted, and it is optional: it defines
// the flash key. If not defined "_flash" is used by default.
func (s *Session) AddFlash(value interface{}, vars ...string) {
	key := flashesKey
	if len(vars) > 0 {
		key = vars[0]
	}
	var flashes []interface{}
	if v, ok := s.Values[key]; ok {
		flashes = v.([]interface{})
	}
	s.Values[key] = append(flashes, value)
}

// Save is a convenience method to save this session. It is the same as calling
// store.Save(request, response, session). You should call Save before writing to
// the response or returning from the handler.
func (s *Session) Save(r *http.Request, w http.ResponseWriter) error {
	return s.store.Save(r, w, s)
}

// Name returns the name used to register the session.
func (s *Session) Name() string {
	return s.name
}

// Store returns the session store used to register the session.
func (s *Session) Store() Store {
	return s.store
}

// Registry -------------------------------------------------------------------

// sessionInfo stores a session tracked by the registry.
type sessionInfo struct {
	s *Session
	e error
}

// contextKey is the type used to store the registry in the context.
type contextKey int

// registryKey is the key used to store the registry in the context.
const registryKey contextKey = 0

// GetRegistry returns a registry instance for the current request.
func GetRegistry(r *http.Request) *Registry {
	registry := context.Get(r, registryKey)
	if registry != nil {
		return registry.(*Registry)
	}
	newRegistry := &Registry{
		request:  r,
		sessions: make(map[string]sessionInfo),
	}
	context.Set(r, registryKey, newRegistry)
	return newRegistry
}

// Registry stores sessions used during a request.
type Registry struct {
	request  *http.Request
	sessions map[string]sessionInfo
}

// Get registers and returns a session for the given name and session store.
//
// It returns a new session if there are no sessions registered for the name.
func (s *Registry) Get(store Store, name string) (session *Session, err error) {
	if !isCookieNameValid(name) {
		return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name)
	}
	if info, ok := s.sessions[name]; ok {
		session, err = info.s, info.e
	} else {
		session, err = store.New(s.request, name)
		session.name = name
		s.sessions[name] = sessionInfo{s: session, e: err}
	}
	session.store = store
	return
}

// Save saves all sessions registered for the current request.
func (s *Registry) Save(w http.ResponseWriter) error {
	var errMulti MultiError
	for name, info := range s.sessions {
		session := info.s
		if session.store == nil {
			errMulti = append(errMulti, fmt.Errorf(
				"sessions: missing store for session %q", name))
		} else if err := session.store.Save(s.request, w, session); err != nil {
			errMulti = append(errMulti, fmt.Errorf(
				"sessions: error saving session %q -- %v", name, err))
		}
	}
	if errMulti != nil {
		return errMulti
	}
	return nil
}

// Helpers --------------------------------------------------------------------

func init() {
	gob.Register([]interface{}{})
}

// Save saves all sessions used during the current request.
func Save(r *http.Request, w http.ResponseWriter) error {
	return GetRegistry(r).Save(w)
}

// NewCookie returns an http.Cookie with the options set. It also sets
// the Expires field calculated based on the MaxAge value, for Internet
// Explorer compatibility.
func NewCookie(name, value string, options *Options) *http.Cookie {
	cookie := &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     options.Path,
		Domain:   options.Domain,
		MaxAge:   options.MaxAge,
		Secure:   options.Secure,
		HttpOnly: options.HttpOnly,
	}
	if options.MaxAge > 0 {
		d := time.Duration(options.MaxAge) * time.Second
		cookie.Expires = time.Now().Add(d)
	} else if options.MaxAge < 0 {
		// Set it to the past to expire now.
		cookie.Expires = time.Unix(1, 0)
	}
	return cookie
}

// Error ----------------------------------------------------------------------

// MultiError stores multiple errors.
//
// Borrowed from the App Engine SDK.
type MultiError []error

func (m MultiError) Error() string {
	s, n := "", 0
	for _, e := range m {
		if e != nil {
			if n == 0 {
				s = e.Error()
			}
			n++
		}
	}
	switch n {
	case 0:
		return "(0 errors)"
	case 1:
		return s
	case 2:
		return s + " (and 1 other error)"
	}
	return fmt.Sprintf("%s (and %d other errors)", s, n-1)
}
<commit_msg>Added description about Max-Age field in Options (#148)<commit_after>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sessions

import (
	"encoding/gob"
	"fmt"
	"net/http"
	"time"

	"github.com/gorilla/context"
)

// Default flashes key.
const flashesKey = "_flash"

// Options --------------------------------------------------------------------

// Options stores configuration for a session or session store.
//
// Fields are a subset of http.Cookie fields.
type Options struct {
	Path   string
	Domain string
	// MaxAge=0 means no Max-Age attribute specified and the cookie will be
	// deleted after the browser session ends.
	// MaxAge<0 means delete cookie immediately.
	// MaxAge>0 means Max-Age attribute present and given in seconds.
	MaxAge   int
	Secure   bool
	HttpOnly bool
}

// Session --------------------------------------------------------------------

// NewSession is called by session stores to create a new session instance.
func NewSession(store Store, name string) *Session {
	return &Session{
		Values:  make(map[interface{}]interface{}),
		store:   store,
		name:    name,
		Options: new(Options),
	}
}

// Session stores the values and optional configuration for a session.
type Session struct {
	// The ID of the session, generated by stores. It should not be used for
	// user data.
	ID string
	// Values contains the user-data for the session.
	Values  map[interface{}]interface{}
	Options *Options
	IsNew   bool
	store   Store
	name    string
}

// Flashes returns a slice of flash messages from the session.
//
// A single variadic argument is accepted, and it is optional: it defines
// the flash key. If not defined "_flash" is used by default.
func (s *Session) Flashes(vars ...string) []interface{} {
	var flashes []interface{}
	key := flashesKey
	if len(vars) > 0 {
		key = vars[0]
	}
	if v, ok := s.Values[key]; ok {
		// Drop the flashes and return it.
		delete(s.Values, key)
		flashes = v.([]interface{})
	}
	return flashes
}

// AddFlash adds a flash message to the session.
//
// A single variadic argument is accepted, and it is optional: it defines
// the flash key. If not defined "_flash" is used by default.
func (s *Session) AddFlash(value interface{}, vars ...string) {
	key := flashesKey
	if len(vars) > 0 {
		key = vars[0]
	}
	var flashes []interface{}
	if v, ok := s.Values[key]; ok {
		flashes = v.([]interface{})
	}
	s.Values[key] = append(flashes, value)
}

// Save is a convenience method to save this session. It is the same as calling
// store.Save(request, response, session). You should call Save before writing to
// the response or returning from the handler.
func (s *Session) Save(r *http.Request, w http.ResponseWriter) error {
	return s.store.Save(r, w, s)
}

// Name returns the name used to register the session.
func (s *Session) Name() string {
	return s.name
}

// Store returns the session store used to register the session.
func (s *Session) Store() Store {
	return s.store
}

// Registry -------------------------------------------------------------------

// sessionInfo stores a session tracked by the registry.
type sessionInfo struct {
	s *Session
	e error
}

// contextKey is the type used to store the registry in the context.
type contextKey int

// registryKey is the key used to store the registry in the context.
const registryKey contextKey = 0

// GetRegistry returns a registry instance for the current request.
func GetRegistry(r *http.Request) *Registry {
	registry := context.Get(r, registryKey)
	if registry != nil {
		return registry.(*Registry)
	}
	newRegistry := &Registry{
		request:  r,
		sessions: make(map[string]sessionInfo),
	}
	context.Set(r, registryKey, newRegistry)
	return newRegistry
}

// Registry stores sessions used during a request.
type Registry struct {
	request  *http.Request
	sessions map[string]sessionInfo
}

// Get registers and returns a session for the given name and session store.
//
// It returns a new session if there are no sessions registered for the name.
func (s *Registry) Get(store Store, name string) (session *Session, err error) {
	if !isCookieNameValid(name) {
		return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name)
	}
	if info, ok := s.sessions[name]; ok {
		session, err = info.s, info.e
	} else {
		session, err = store.New(s.request, name)
		session.name = name
		s.sessions[name] = sessionInfo{s: session, e: err}
	}
	session.store = store
	return
}

// Save saves all sessions registered for the current request.
func (s *Registry) Save(w http.ResponseWriter) error {
	var errMulti MultiError
	for name, info := range s.sessions {
		session := info.s
		if session.store == nil {
			errMulti = append(errMulti, fmt.Errorf(
				"sessions: missing store for session %q", name))
		} else if err := session.store.Save(s.request, w, session); err != nil {
			errMulti = append(errMulti, fmt.Errorf(
				"sessions: error saving session %q -- %v", name, err))
		}
	}
	if errMulti != nil {
		return errMulti
	}
	return nil
}

// Helpers --------------------------------------------------------------------

func init() {
	gob.Register([]interface{}{})
}

// Save saves all sessions used during the current request.
func Save(r *http.Request, w http.ResponseWriter) error {
	return GetRegistry(r).Save(w)
}

// NewCookie returns an http.Cookie with the options set. It also sets
// the Expires field calculated based on the MaxAge value, for Internet
// Explorer compatibility.
func NewCookie(name, value string, options *Options) *http.Cookie {
	cookie := &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     options.Path,
		Domain:   options.Domain,
		MaxAge:   options.MaxAge,
		Secure:   options.Secure,
		HttpOnly: options.HttpOnly,
	}
	if options.MaxAge > 0 {
		d := time.Duration(options.MaxAge) * time.Second
		cookie.Expires = time.Now().Add(d)
	} else if options.MaxAge < 0 {
		// Set it to the past to expire now.
		cookie.Expires = time.Unix(1, 0)
	}
	return cookie
}

// Error ----------------------------------------------------------------------

// MultiError stores multiple errors.
//
// Borrowed from the App Engine SDK.
type MultiError []error

func (m MultiError) Error() string {
	s, n := "", 0
	for _, e := range m {
		if e != nil {
			if n == 0 {
				s = e.Error()
			}
			n++
		}
	}
	switch n {
	case 0:
		return "(0 errors)"
	case 1:
		return s
	case 2:
		return s + " (and 1 other error)"
	}
	return fmt.Sprintf("%s (and %d other errors)", s, n-1)
}
<|endoftext|>


<commit_before>package fingerprint

import (
	"fmt"
	"log"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/nomad/structs"
)

// BuiltinFingerprints contains the built in registered fingerprints
// which are available
var BuiltinFingerprints = map[string]Factory{
	"arch":   NewArchFingerprint,
	"cpu":    NewCPUFingerprint,
	"host":   NewHostFingerprint,
	"os":     NewOSFingerprint,
	"memory": NewMemoryFingerprint,
}

// NewFingerprint is used to instantiate and return a new fingerprint
// given the name and a logger
func NewFingerprint(name string, logger *log.Logger) (Fingerprint, error) {
	// Lookup the factory function
	factory, ok := BuiltinFingerprints[name]
	if !ok {
		return nil, fmt.Errorf("unknown fingerprint '%s'", name)
	}

	// Instantiate the fingerprint
	f := factory(logger)
	return f, nil
}

// Factory is used to instantiate a new Fingerprint
type Factory func(*log.Logger) Fingerprint

// Fingerprint is used for doing "fingerprinting" of the
// host to automatically determine attributes, resources,
// and metadata about it. Each of these is a heuristic, and
// many of them can be applied on a particular host.
type Fingerprint interface {
	// Fingerprint is used to update properties of the Node,
	// and returns if the fingerprint was applicable and a potential error.
	Fingerprint(*config.Config, *structs.Node) (bool, error)
}
<commit_msg>Add the storage fingerprinter to the list of built-ins<commit_after>package fingerprint

import (
	"fmt"
	"log"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/nomad/structs"
)

// BuiltinFingerprints contains the built in registered fingerprints
// which are available
var BuiltinFingerprints = map[string]Factory{
	"arch":    NewArchFingerprint,
	"cpu":     NewCPUFingerprint,
	"host":    NewHostFingerprint,
	"os":      NewOSFingerprint,
	"memory":  NewMemoryFingerprint,
	"storage": NewStorageFingerprint,
}

// NewFingerprint is used to instantiate and return a new fingerprint
// given the name and a logger
func NewFingerprint(name string, logger *log.Logger) (Fingerprint, error) {
	// Lookup the factory function
	factory, ok := BuiltinFingerprints[name]
	if !ok {
		return nil, fmt.Errorf("unknown fingerprint '%s'", name)
	}

	// Instantiate the fingerprint
	f := factory(logger)
	return f, nil
}

// Factory is used to instantiate a new Fingerprint
type Factory func(*log.Logger) Fingerprint

// Fingerprint is used for doing "fingerprinting" of the
// host to automatically determine attributes, resources,
// and metadata about it. Each of these is a heuristic, and
// many of them can be applied on a particular host.
type Fingerprint interface {
	// Fingerprint is used to update properties of the Node,
	// and returns if the fingerprint was applicable and a potential error.
	Fingerprint(*config.Config, *structs.Node) (bool, error)
}
<|endoftext|>