<commit_before>package psql

import (
	"fmt"
	"strings"

	"github.com/lib/pq"
)

// Select creates a new SelectQuery using the expressions exprs to populate
// the SELECT list, that is the portion of the query between the key words
// "SELECT" and "FROM".
func Select(exprs ...Expression) SelectQuery {
	return SelectQuery{
		sel:    selectClause{exprs},
		params: newParams(),
	}
}

// A SelectQuery represents a SELECT query with all its clauses.
type SelectQuery struct {
	sel     selectClause
	orderBy orderByClause
	groupBy groupByClause

	params *Params
}

// OrderBy returns a copy of the SelectQuery s with an additional ORDER BY
// clause containing the order expressions provided. If an ORDER BY clause
// was already present, this operation will overwrite it.
func (s SelectQuery) OrderBy(exprs ...OrderExpression) SelectQuery {
	s.orderBy = orderByClause{exprs}
	return s
}

// GroupBy returns a copy of the SelectQuery s with an additional GROUP BY
// clause containing the Expressions provided. If a GROUP BY clause was
// already present, this operation will overwrite it.
func (s SelectQuery) GroupBy(exprs ...Expression) SelectQuery {
	s.groupBy = groupByClause{exprs}
	return s
}

// ToSQL returns a string containing the full SQL query version of the
// SelectQuery. If the query is empty, an empty string is returned.
func (s SelectQuery) ToSQL() string {
	var parts []string

	for _, clause := range s.clauses() {
		if part := clause.ToSQLClause(s.params); part != "" {
			parts = append(parts, part)
		}
	}

	if len(parts) == 0 {
		return ""
	}

	return strings.Join(parts, " ")
}

func (s SelectQuery) clauses() []Clause {
	return []Clause{
		s.sel,
		s.from(),
		s.groupBy,
		s.orderBy,
	}
}

func (s SelectQuery) from() Clause {
	rels := make([]string, 0)
	set := make(map[string]struct{})

	for _, rel := range s.relations() {
		// Have we seen this relation before?
		if _, ok := set[rel]; !ok {
			set[rel] = struct{}{}
			rels = append(rels, rel)
		}
	}

	return fromClause{rels}
}

func (s SelectQuery) relations() []string {
	var rels []string
	rels = append(rels, s.sel.Relations()...)
	rels = append(rels, s.groupBy.Relations()...)
	rels = append(rels, s.orderBy.Relations()...)
	return rels
}

// Bindings returns a slice of arguments that can be unpacked and passed
// into the Query and QueryRow methods of the database/sql package.
//
// Any variadic arguments passed into Bindings will be used to replace
// user-supplied parameters in the SELECT query, in the same order as
// they appear in the query.
func (s SelectQuery) Bindings(inputs ...interface{}) []interface{} {
	return s.params.Values(inputs)
}

// Clause is the interface that represents the individual components of
// an SQL query.
//
// ToSQLClause converts the clause to a string representation. If an empty
// string is returned, the clause will be omitted from the final SQL query.
type Clause interface {
	ToSQLClause(p *Params) string
}

type selectClause struct {
	exprs []Expression
}

func (s selectClause) ToSQLClause(p *Params) string {
	if len(s.exprs) == 0 {
		return ""
	}

	args := make([]string, len(s.exprs))
	for i, expr := range s.exprs {
		args[i] = expr.ToSQLExpr(p)
	}

	return fmt.Sprintf("SELECT %s", strings.Join(args, ", "))
}

func (s selectClause) Relations() []string {
	var rels []string
	for _, expr := range s.exprs {
		rels = append(rels, expr.Relations()...)
	}
	return rels
}

type fromClause struct {
	rels []string
}

func (f fromClause) ToSQLClause(*Params) string {
	if len(f.rels) == 0 {
		return ""
	}

	return fmt.Sprintf("FROM %s", strings.Join(f.rels, ", "))
}

type groupByClause struct {
	exprs []Expression
}

func (g groupByClause) ToSQLClause(p *Params) string {
	if len(g.exprs) == 0 {
		return ""
	}

	parts := make([]string, len(g.exprs))
	for i, expr := range g.exprs {
		parts[i] = expr.ToSQLExpr(p)
	}

	return fmt.Sprintf("GROUP BY %s", strings.Join(parts, ", "))
}

func (g groupByClause) Relations() []string {
	var rels []string
	for _, expr := range g.exprs {
		rels = append(rels, expr.Relations()...)
	}
	return rels
}

type orderByClause struct {
	exprs []OrderExpression
}

func (o orderByClause) ToSQLClause(p *Params) string {
	if len(o.exprs) == 0 {
		return ""
	}

	parts := make([]string, len(o.exprs))
	for i, expr := range o.exprs {
		parts[i] = expr.ToSQLOrder(p)
	}

	return fmt.Sprintf("ORDER BY %s", strings.Join(parts, ", "))
}

func (o orderByClause) Relations() []string {
	var rels []string
	for _, expr := range o.exprs {
		rels = append(rels, expr.Relations()...)
	}
	return rels
}

// Ascending returns a new OrderExpression specifying that the results
// of the query must be ordered by the given Expression in ascending order.
func Ascending(expr Expression) OrderExpression {
	return OrderExpression{expr, asc}
}

// Descending returns a new OrderExpression specifying that the results
// of the query must be ordered by the given Expression in descending order.
func Descending(expr Expression) OrderExpression {
	return OrderExpression{expr, desc}
}

type orderDirection string

const (
	asc  orderDirection = "ASC"
	desc                = "DESC"
)

// An OrderExpression is each individual component of a SELECT query's
// ORDER BY clause, specifying that the results of the query must be
// sorted by a given SQL expression.
type OrderExpression struct {
	expr      Expression
	direction orderDirection
}

func (o OrderExpression) ToSQLOrder(p *Params) string {
	return fmt.Sprintf("%s %s", o.expr.ToSQLExpr(p), o.direction)
}

func (o OrderExpression) Relations() []string {
	return o.expr.Relations()
}

// Expression is the interface that represents any SQL expression that
// can be used in the SELECT list of an SQL query.
//
// ToSQLExpr converts the expression into a snippet of SQL that may be
// safely embedded in a query. If the expression needs to be quoted or
// otherwise escaped, ToSQLExpr must return the quoted version.
//
// Relations returns a slice of strings corresponding to the (quoted)
// names of all relations used by the Expression.
type Expression interface {
	ToSQLExpr(*Params) string
	Relations() []string
}

// AllColumns returns an Expression representing all columns in the table.
func AllColumns(table string) allColumns {
	return allColumns{table}
}

type allColumns struct {
	table string
}

func (ac allColumns) ToSQLExpr(*Params) string {
	return fmt.Sprintf("%s.*", pq.QuoteIdentifier(ac.table))
}

func (ac allColumns) Relations() []string {
	return []string{
		pq.QuoteIdentifier(ac.table),
	}
}

// TableColumn returns an Expression representing the column col of the
// database table with the given name.
func TableColumn(table, col string) tableColumn {
	return tableColumn{table, col}
}

type tableColumn struct {
	table, column string
}

func (tc tableColumn) ToSQLExpr(*Params) string {
	return pq.QuoteIdentifier(tc.column)
}

func (tc tableColumn) Relations() []string {
	return []string{
		pq.QuoteIdentifier(tc.table),
	}
}

func newParams() *Params {
	return &Params{
		values: make(map[int]interface{}),
	}
}

type Params struct {
	counter int
	values  map[int]interface{}
}

func (p *Params) Add(value interface{}) string {
	marker := p.next()
	p.values[p.counter] = value
	return marker
}

func (p *Params) New() string {
	return p.next()
}

func (p *Params) next() string {
	p.counter++
	return fmt.Sprintf("$%d", p.counter)
}

func (p *Params) Values(inputs []interface{}) []interface{} {
	var values []interface{}

	for i := 1; i <= p.counter; i++ {
		if val, ok := p.values[i]; ok {
			values = append(values, val)
		} else if len(inputs) > 0 {
			values = append(values, inputs[0])
			inputs = inputs[1:]
		}
	}

	return values
}

// StringLiteral returns a text literal that will be replaced with str
// when the query is executed. For security reasons, the contents of str
// are not directly interpolated into the query's SQL representation.
// Instead, a marker such as $1 is used, and the value of str is injected
// into the appropriate position when the Bindings() method is called.
func StringLiteral(str string) stringLiteral {
	return stringLiteral(str)
}

type stringLiteral string

func (s stringLiteral) ToSQLExpr(params *Params) string {
	marker := params.Add(string(s))
	return fmt.Sprintf("%s::%s", marker, TextDataType)
}

func (stringLiteral) Relations() []string {
	return nil
}

type DataType string

const (
	TextDataType DataType = "text"
)

// StringParam returns a free (unbound) parameter using the "text" type.
func StringParam() freeParam {
	return freeParam{TextDataType}
}

type freeParam struct {
	Type DataType
}

func (p freeParam) ToSQLExpr(params *Params) string {
	return fmt.Sprintf("%s::%s", params.New(), p.Type)
}

func (p freeParam) Relations() []string {
	return nil
}
<commit_msg>Rename DataType to dataType since we don't need to export it<commit_after>package psql

import (
	"fmt"
	"strings"

	"github.com/lib/pq"
)

// Select creates a new SelectQuery using the expressions exprs to populate
// the SELECT list, that is the portion of the query between the key words
// "SELECT" and "FROM".
func Select(exprs ...Expression) SelectQuery {
	return SelectQuery{
		sel:    selectClause{exprs},
		params: newParams(),
	}
}

// A SelectQuery represents a SELECT query with all its clauses.
type SelectQuery struct {
	sel     selectClause
	orderBy orderByClause
	groupBy groupByClause

	params *Params
}

// OrderBy returns a copy of the SelectQuery s with an additional ORDER BY
// clause containing the order expressions provided. If an ORDER BY clause
// was already present, this operation will overwrite it.
func (s SelectQuery) OrderBy(exprs ...OrderExpression) SelectQuery {
	s.orderBy = orderByClause{exprs}
	return s
}

// GroupBy returns a copy of the SelectQuery s with an additional GROUP BY
// clause containing the Expressions provided. If a GROUP BY clause was
// already present, this operation will overwrite it.
func (s SelectQuery) GroupBy(exprs ...Expression) SelectQuery {
	s.groupBy = groupByClause{exprs}
	return s
}

// ToSQL returns a string containing the full SQL query version of the
// SelectQuery. If the query is empty, an empty string is returned.
func (s SelectQuery) ToSQL() string {
	var parts []string

	for _, clause := range s.clauses() {
		if part := clause.ToSQLClause(s.params); part != "" {
			parts = append(parts, part)
		}
	}

	if len(parts) == 0 {
		return ""
	}

	return strings.Join(parts, " ")
}

func (s SelectQuery) clauses() []Clause {
	return []Clause{
		s.sel,
		s.from(),
		s.groupBy,
		s.orderBy,
	}
}

func (s SelectQuery) from() Clause {
	rels := make([]string, 0)
	set := make(map[string]struct{})

	for _, rel := range s.relations() {
		// Have we seen this relation before?
		if _, ok := set[rel]; !ok {
			set[rel] = struct{}{}
			rels = append(rels, rel)
		}
	}

	return fromClause{rels}
}

func (s SelectQuery) relations() []string {
	var rels []string
	rels = append(rels, s.sel.Relations()...)
	rels = append(rels, s.groupBy.Relations()...)
	rels = append(rels, s.orderBy.Relations()...)
	return rels
}

// Bindings returns a slice of arguments that can be unpacked and passed
// into the Query and QueryRow methods of the database/sql package.
//
// Any variadic arguments passed into Bindings will be used to replace
// user-supplied parameters in the SELECT query, in the same order as
// they appear in the query.
func (s SelectQuery) Bindings(inputs ...interface{}) []interface{} {
	return s.params.Values(inputs)
}

// Clause is the interface that represents the individual components of
// an SQL query.
//
// ToSQLClause converts the clause to a string representation. If an empty
// string is returned, the clause will be omitted from the final SQL query.
type Clause interface {
	ToSQLClause(p *Params) string
}

type selectClause struct {
	exprs []Expression
}

func (s selectClause) ToSQLClause(p *Params) string {
	if len(s.exprs) == 0 {
		return ""
	}

	args := make([]string, len(s.exprs))
	for i, expr := range s.exprs {
		args[i] = expr.ToSQLExpr(p)
	}

	return fmt.Sprintf("SELECT %s", strings.Join(args, ", "))
}

func (s selectClause) Relations() []string {
	var rels []string
	for _, expr := range s.exprs {
		rels = append(rels, expr.Relations()...)
	}
	return rels
}

type fromClause struct {
	rels []string
}

func (f fromClause) ToSQLClause(*Params) string {
	if len(f.rels) == 0 {
		return ""
	}

	return fmt.Sprintf("FROM %s", strings.Join(f.rels, ", "))
}

type groupByClause struct {
	exprs []Expression
}

func (g groupByClause) ToSQLClause(p *Params) string {
	if len(g.exprs) == 0 {
		return ""
	}

	parts := make([]string, len(g.exprs))
	for i, expr := range g.exprs {
		parts[i] = expr.ToSQLExpr(p)
	}

	return fmt.Sprintf("GROUP BY %s", strings.Join(parts, ", "))
}

func (g groupByClause) Relations() []string {
	var rels []string
	for _, expr := range g.exprs {
		rels = append(rels, expr.Relations()...)
	}
	return rels
}

type orderByClause struct {
	exprs []OrderExpression
}

func (o orderByClause) ToSQLClause(p *Params) string {
	if len(o.exprs) == 0 {
		return ""
	}

	parts := make([]string, len(o.exprs))
	for i, expr := range o.exprs {
		parts[i] = expr.ToSQLOrder(p)
	}

	return fmt.Sprintf("ORDER BY %s", strings.Join(parts, ", "))
}

func (o orderByClause) Relations() []string {
	var rels []string
	for _, expr := range o.exprs {
		rels = append(rels, expr.Relations()...)
	}
	return rels
}

// Ascending returns a new OrderExpression specifying that the results
// of the query must be ordered by the given Expression in ascending order.
func Ascending(expr Expression) OrderExpression {
	return OrderExpression{expr, asc}
}

// Descending returns a new OrderExpression specifying that the results
// of the query must be ordered by the given Expression in descending order.
func Descending(expr Expression) OrderExpression {
	return OrderExpression{expr, desc}
}

type orderDirection string

const (
	asc  orderDirection = "ASC"
	desc                = "DESC"
)

// An OrderExpression is each individual component of a SELECT query's
// ORDER BY clause, specifying that the results of the query must be
// sorted by a given SQL expression.
type OrderExpression struct {
	expr      Expression
	direction orderDirection
}

func (o OrderExpression) ToSQLOrder(p *Params) string {
	return fmt.Sprintf("%s %s", o.expr.ToSQLExpr(p), o.direction)
}

func (o OrderExpression) Relations() []string {
	return o.expr.Relations()
}

// Expression is the interface that represents any SQL expression that
// can be used in the SELECT list of an SQL query.
//
// ToSQLExpr converts the expression into a snippet of SQL that may be
// safely embedded in a query. If the expression needs to be quoted or
// otherwise escaped, ToSQLExpr must return the quoted version.
//
// Relations returns a slice of strings corresponding to the (quoted)
// names of all relations used by the Expression.
type Expression interface {
	ToSQLExpr(*Params) string
	Relations() []string
}

// AllColumns returns an Expression representing all columns in the table.
func AllColumns(table string) allColumns {
	return allColumns{table}
}

type allColumns struct {
	table string
}

func (ac allColumns) ToSQLExpr(*Params) string {
	return fmt.Sprintf("%s.*", pq.QuoteIdentifier(ac.table))
}

func (ac allColumns) Relations() []string {
	return []string{
		pq.QuoteIdentifier(ac.table),
	}
}

// TableColumn returns an Expression representing the column col of the
// database table with the given name.
func TableColumn(table, col string) tableColumn {
	return tableColumn{table, col}
}

type tableColumn struct {
	table, column string
}

func (tc tableColumn) ToSQLExpr(*Params) string {
	return pq.QuoteIdentifier(tc.column)
}

func (tc tableColumn) Relations() []string {
	return []string{
		pq.QuoteIdentifier(tc.table),
	}
}

func newParams() *Params {
	return &Params{
		values: make(map[int]interface{}),
	}
}

type Params struct {
	counter int
	values  map[int]interface{}
}

func (p *Params) Add(value interface{}) string {
	marker := p.next()
	p.values[p.counter] = value
	return marker
}

func (p *Params) New() string {
	return p.next()
}

func (p *Params) next() string {
	p.counter++
	return fmt.Sprintf("$%d", p.counter)
}

func (p *Params) Values(inputs []interface{}) []interface{} {
	var values []interface{}

	for i := 1; i <= p.counter; i++ {
		if val, ok := p.values[i]; ok {
			values = append(values, val)
		} else if len(inputs) > 0 {
			values = append(values, inputs[0])
			inputs = inputs[1:]
		}
	}

	return values
}

// StringLiteral returns a text literal that will be replaced with str
// when the query is executed. For security reasons, the contents of str
// are not directly interpolated into the query's SQL representation.
// Instead, a marker such as $1 is used, and the value of str is injected
// into the appropriate position when the Bindings() method is called.
func StringLiteral(str string) stringLiteral {
	return stringLiteral(str)
}

type stringLiteral string

func (s stringLiteral) ToSQLExpr(params *Params) string {
	marker := params.Add(string(s))
	return fmt.Sprintf("%s::%s", marker, textType)
}

func (stringLiteral) Relations() []string {
	return nil
}

type dataType string

const (
	textType dataType = "text"
)

// StringParam returns a free (unbound) parameter using the "text" type.
func StringParam() freeParam {
	return freeParam{textType}
}

type freeParam struct {
	dataType dataType
}

func (p freeParam) ToSQLExpr(params *Params) string {
	return fmt.Sprintf("%s::%s", params.New(), p.dataType)
}

func (p freeParam) Relations() []string {
	return nil
}
<|endoftext|>


<commit_before>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package lorawan

import (
	"github.com/TheThingsNetwork/ttn/api"
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/TheThingsNetwork/ttn/utils/errors"
)

// Validate implements the api.Validator interface
func (m *DeviceIdentifier) Validate() error {
	if m.AppEui == nil || m.AppEui.IsEmpty() {
		return errors.NewErrInvalidArgument("AppEui", "can not be empty")
	}
	if m.DevEui == nil || m.DevEui.IsEmpty() {
		return errors.NewErrInvalidArgument("DevEui", "can not be empty")
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *Device) Validate() error {
	if m.AppEui == nil || m.AppEui.IsEmpty() {
		return errors.NewErrInvalidArgument("AppEui", "can not be empty")
	}
	if m.DevEui == nil || m.DevEui.IsEmpty() {
		return errors.NewErrInvalidArgument("DevEui", "can not be empty")
	}
	if err := api.NotEmptyAndValidID(m.AppId, "AppId"); err != nil {
		return err
	}
	if err := api.NotEmptyAndValidID(m.DevId, "DevId"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *Metadata) Validate() error {
	switch m.Modulation {
	case Modulation_LORA:
		if m.DataRate == "" {
			return errors.NewErrInvalidArgument("DataRate", "can not be empty")
		}
		if _, err := types.ParseDataRate(m.DataRate); err != nil {
			return errors.NewErrInvalidArgument("DataRate", err.Error())
		}
	case Modulation_FSK:
		if m.BitRate == 0 {
			return errors.NewErrInvalidArgument("BitRate", "can not be empty")
		}
	}
	if m.CodingRate == "" {
		return errors.NewErrInvalidArgument("CodingRate", "can not be empty")
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *TxConfiguration) Validate() error {
	switch m.Modulation {
	case Modulation_LORA:
		if m.DataRate == "" {
			return errors.NewErrInvalidArgument("DataRate", "can not be empty")
		}
		if _, err := types.ParseDataRate(m.DataRate); err != nil {
			return errors.NewErrInvalidArgument("DataRate", err.Error())
		}
	case Modulation_FSK:
		if m.BitRate == 0 {
			return errors.NewErrInvalidArgument("BitRate", "can not be empty")
		}
	}
	if m.CodingRate == "" {
		return errors.NewErrInvalidArgument("CodingRate", "can not be empty")
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *ActivationMetadata) Validate() error {
	if m.AppEui == nil || m.AppEui.IsEmpty() {
		return errors.NewErrInvalidArgument("AppEui", "can not be empty")
	}
	if m.DevEui == nil || m.DevEui.IsEmpty() {
		return errors.NewErrInvalidArgument("DevEui", "can not be empty")
	}
	if m.DevAddr != nil && m.DevAddr.IsEmpty() {
		return errors.NewErrInvalidArgument("DevAddr", "can not be empty")
	}
	if m.NwkSKey != nil && m.NwkSKey.IsEmpty() {
		return errors.NewErrInvalidArgument("NwkSKey", "can not be empty")
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *Message) Validate() error {
	if m.Major != Major_LORAWAN_R1 {
		return errors.NewErrInvalidArgument("Major", "invalid value "+m.Major.String())
	}
	switch m.MType {
	case MType_JOIN_REQUEST:
		if m.GetJoinRequestPayload() == nil {
			return errors.NewErrInvalidArgument("JoinRequestPayload", "can not be empty")
		}
		if err := m.GetJoinRequestPayload().Validate(); err != nil {
			return errors.NewErrInvalidArgument("JoinRequestPayload", err.Error())
		}
	case MType_JOIN_ACCEPT:
		if m.GetJoinAcceptPayload() == nil {
			return errors.NewErrInvalidArgument("JoinAcceptPayload", "can not be empty")
		}
		if err := m.GetJoinAcceptPayload().Validate(); err != nil {
			return errors.NewErrInvalidArgument("JoinAcceptPayload", err.Error())
		}
	case MType_UNCONFIRMED_UP, MType_UNCONFIRMED_DOWN, MType_CONFIRMED_UP, MType_CONFIRMED_DOWN:
		if m.GetMacPayload() == nil {
			return errors.NewErrInvalidArgument("MacPayload", "can not be empty")
		}
		if err := m.GetMacPayload().Validate(); err != nil {
			return errors.NewErrInvalidArgument("MacPayload", err.Error())
		}
	default:
		return errors.NewErrInvalidArgument("MType", "unknown type "+m.MType.String())
	}

	return nil
}

// Validate implements the api.Validator interface
func (m *JoinRequestPayload) Validate() error {
	if m.AppEui.IsEmpty() {
		return errors.NewErrInvalidArgument("AppEui", "can not be empty")
	}
	if m.DevEui.IsEmpty() {
		return errors.NewErrInvalidArgument("DevEui", "can not be empty")
	}

	return nil
}

// Validate implements the api.Validator interface
func (m *JoinAcceptPayload) Validate() error {
	if len(m.Encrypted) != 0 {
		return nil
	}

	if m.CfList != nil && len(m.CfList.Freq) != 5 {
		return errors.NewErrInvalidArgument("CfList.Freq", "length must be 5")
	}

	if m.DevAddr.IsEmpty() {
		return errors.NewErrInvalidArgument("DevAddr", "can not be empty")
	}
	if m.NetId.IsEmpty() {
		return errors.NewErrInvalidArgument("NetId", "can not be empty")
	}

	return nil
}

// Validate implements the api.Validator interface
func (m *MACPayload) Validate() error {
	if m.DevAddr.IsEmpty() {
		return errors.NewErrInvalidArgument("DevAddr", "can not be empty")
	}
	return nil
}
<commit_msg>Update validation to allow on-join device registration<commit_after>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package lorawan

import (
	"github.com/TheThingsNetwork/ttn/api"
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/TheThingsNetwork/ttn/utils/errors"
)

// Validate implements the api.Validator interface
func (m *DeviceIdentifier) Validate() error {
	if m.AppEui == nil || m.AppEui.IsEmpty() {
		return errors.NewErrInvalidArgument("AppEui", "can not be empty")
	}
	if m.DevEui == nil {
		return errors.NewErrInvalidArgument("DevEui", "can not be empty")
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *Device) Validate() error {
	if m.AppEui == nil || m.AppEui.IsEmpty() {
		return errors.NewErrInvalidArgument("AppEui", "can not be empty")
	}
	if m.DevEui == nil {
		return errors.NewErrInvalidArgument("DevEui", "can not be empty")
	}
	if err := api.NotEmptyAndValidID(m.AppId, "AppId"); err != nil {
		return err
	}
	if err := api.NotEmptyAndValidID(m.DevId, "DevId"); err != nil {
		return err
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *Metadata) Validate() error {
	switch m.Modulation {
	case Modulation_LORA:
		if m.DataRate == "" {
			return errors.NewErrInvalidArgument("DataRate", "can not be empty")
		}
		if _, err := types.ParseDataRate(m.DataRate); err != nil {
			return errors.NewErrInvalidArgument("DataRate", err.Error())
		}
	case Modulation_FSK:
		if m.BitRate == 0 {
			return errors.NewErrInvalidArgument("BitRate", "can not be empty")
		}
	}
	if m.CodingRate == "" {
		return errors.NewErrInvalidArgument("CodingRate", "can not be empty")
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *TxConfiguration) Validate() error {
	switch m.Modulation {
	case Modulation_LORA:
		if m.DataRate == "" {
			return errors.NewErrInvalidArgument("DataRate", "can not be empty")
		}
		if _, err := types.ParseDataRate(m.DataRate); err != nil {
			return errors.NewErrInvalidArgument("DataRate", err.Error())
		}
	case Modulation_FSK:
		if m.BitRate == 0 {
			return errors.NewErrInvalidArgument("BitRate", "can not be empty")
		}
	}
	if m.CodingRate == "" {
		return errors.NewErrInvalidArgument("CodingRate", "can not be empty")
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *ActivationMetadata) Validate() error {
	if m.AppEui == nil || m.AppEui.IsEmpty() {
		return errors.NewErrInvalidArgument("AppEui", "can not be empty")
	}
	if m.DevEui == nil || m.DevEui.IsEmpty() {
		return errors.NewErrInvalidArgument("DevEui", "can not be empty")
	}
	if m.DevAddr != nil && m.DevAddr.IsEmpty() {
		return errors.NewErrInvalidArgument("DevAddr", "can not be empty")
	}
	if m.NwkSKey != nil && m.NwkSKey.IsEmpty() {
		return errors.NewErrInvalidArgument("NwkSKey", "can not be empty")
	}
	return nil
}

// Validate implements the api.Validator interface
func (m *Message) Validate() error {
	if m.Major != Major_LORAWAN_R1 {
		return errors.NewErrInvalidArgument("Major", "invalid value "+m.Major.String())
	}
	switch m.MType {
	case MType_JOIN_REQUEST:
		if m.GetJoinRequestPayload() == nil {
			return errors.NewErrInvalidArgument("JoinRequestPayload", "can not be empty")
		}
		if err := m.GetJoinRequestPayload().Validate(); err != nil {
			return errors.NewErrInvalidArgument("JoinRequestPayload", err.Error())
		}
	case MType_JOIN_ACCEPT:
		if m.GetJoinAcceptPayload() == nil {
			return errors.NewErrInvalidArgument("JoinAcceptPayload", "can not be empty")
		}
		if err := m.GetJoinAcceptPayload().Validate(); err != nil {
			return errors.NewErrInvalidArgument("JoinAcceptPayload", err.Error())
		}
	case MType_UNCONFIRMED_UP, MType_UNCONFIRMED_DOWN, MType_CONFIRMED_UP, MType_CONFIRMED_DOWN:
		if m.GetMacPayload() == nil {
			return errors.NewErrInvalidArgument("MacPayload", "can not be empty")
		}
		if err := m.GetMacPayload().Validate(); err != nil {
			return errors.NewErrInvalidArgument("MacPayload", err.Error())
		}
	default:
		return errors.NewErrInvalidArgument("MType", "unknown type "+m.MType.String())
	}

	return nil
}

// Validate implements the api.Validator interface
func (m *JoinRequestPayload) Validate() error {
	if m.AppEui.IsEmpty() {
		return errors.NewErrInvalidArgument("AppEui", "can not be empty")
	}
	if m.DevEui.IsEmpty() {
		return errors.NewErrInvalidArgument("DevEui", "can not be empty")
	}

	return nil
}

// Validate implements the api.Validator interface
func (m *JoinAcceptPayload) Validate() error {
	if len(m.Encrypted) != 0 {
		return nil
	}

	if m.CfList != nil && len(m.CfList.Freq) != 5 {
		return errors.NewErrInvalidArgument("CfList.Freq", "length must be 5")
	}

	if m.DevAddr.IsEmpty() {
		return errors.NewErrInvalidArgument("DevAddr", "can not be empty")
	}
	if m.NetId.IsEmpty() {
		return errors.NewErrInvalidArgument("NetId", "can not be empty")
	}

	return nil
}

// Validate implements the api.Validator interface
func (m *MACPayload) Validate() error {
	if m.DevAddr.IsEmpty() {
		return errors.NewErrInvalidArgument("DevAddr", "can not be empty")
	}
	return nil
}
<|endoftext|>


<commit_before>// 4 march 2014
package main

import (
	"fmt"
	"os"
	"os/exec"
	"time"

	"github.com/andlabs/ui"
)

const (
	defCmdLine = "mpv -loop inf ~/ring.wav"
	defTime    = "10:30 AM"
	timeFmt    = "3:04 PM"
)

// If later hasn't happened yet, make it happen on the day of now; if not, the day after.
func bestTime(now time.Time, later time.Time) time.Time {
	now = now.Local() // use local time to make things make sense
	nowh, nowm, nows := now.Clock()
	laterh, laterm, laters := later.Clock()
	add := false
	if nowh > laterh {
		add = true
	} else if (nowh == laterh) && (nowm > laterm) {
		add = true
	} else if (nowh == laterh) && (nowm == laterm) && (nows >= laters) {
		// >= in the case we're on the exact second; add a day because the alarm should have gone off by now otherwise!
		add = true
	}
	if add {
		now = now.AddDate(0, 0, 1)
	}
	return time.Date(now.Year(), now.Month(), now.Day(),
		laterh, laterm, laters, 0,
		now.Location())
}

func myMain() {
	var cmd *exec.Cmd
	var timer *time.Timer
	var timerChan <-chan time.Time
	var w *ui.Window

	status := ui.NewLabel("")

	stop := func() {
		if cmd != nil { // stop the command if it's running
			err := cmd.Process.Kill()
			if err != nil {
				ui.MsgBoxError(w,
					fmt.Sprintf("Error killing process: %v", err),
					"You may need to kill it manually.")
			}
			err = cmd.Process.Release()
			if err != nil {
				ui.MsgBoxError(w,
					fmt.Sprintf("Error releasing process: %v", err),
					"")
			}
			cmd = nil
		}
		if timer != nil { // stop the timer if we started it
			timer.Stop()
			timer = nil
			timerChan = nil
		}
		status.SetText("")
	}

	w = ui.NewWindow("wakeup", 400, 100)
	ui.AppQuit = w.Closing // treat application close as main window close
	cmdbox := ui.NewLineEdit(defCmdLine)
	timebox := ui.NewLineEdit(defTime)
	bStart := ui.NewButton("Start")
	bStop := ui.NewButton("Stop")

	// a Stack to keep both buttons at the same size
	btnbox := ui.NewHorizontalStack(bStart, bStop)
	btnbox.SetStretchy(0)
	btnbox.SetStretchy(1)
	// and a Stack around that Stack to keep them at a reasonable size, with space to their right
	btnbox = ui.NewHorizontalStack(btnbox, status)

	// the main layout
	grid := ui.NewGrid(2,
		ui.NewLabel("Command"), cmdbox,
		ui.NewLabel("Time"), timebox,
		ui.Space(), ui.Space(), // the Space on the right will consume the window blank space
		ui.Space(), btnbox)
	grid.SetStretchy(2, 1) // make the Space noted above consume
	grid.SetFilling(0, 1)  // make the two textboxes grow horizontally
	grid.SetFilling(1, 1)

	w.Open(grid)

mainloop:
	for {
		select {
		case <-w.Closing:
			break mainloop
		case <-bStart.Clicked:
			stop() // only one alarm at a time
			alarmTime, err := time.Parse(timeFmt, timebox.Text())
			if err != nil {
				ui.MsgBoxError(w,
					fmt.Sprintf("Error parsing time %q: %v", timebox.Text(), err),
					fmt.Sprintf("Make sure your time is in the form %q (without quotes).", timeFmt))
				continue
			}
			now := time.Now()
			later := bestTime(now, alarmTime)
			timer = time.NewTimer(later.Sub(now))
			timerChan = timer.C
			status.SetText("Started")
		case <-timerChan:
			cmd = exec.Command("/bin/sh", "-c", "exec "+cmdbox.Text())
			// keep stdin /dev/null in case user wants to run multiple alarms on one instance (TODO should I allow this program to act as a pipe?)
			// keep stdout /dev/null to avoid stty mucking
			cmd.Stderr = os.Stderr
			err := cmd.Start()
			status.SetText("Firing")
			if err != nil {
				ui.MsgBoxError(w,
					fmt.Sprintf("Error running program: %v", err),
					"")
				cmd = nil
				status.SetText("")
			}
			timer = nil
			timerChan = nil
		case <-bStop.Clicked:
			stop()
		}
	}

	// clean up
	stop()
}

func main() {
	err := ui.Go(myMain)
	if err != nil {
		panic(fmt.Errorf("error initializing UI library: %v", err))
	}
}
<commit_msg>Another MsgBox() API update.<commit_after>// 4 march 2014
package main

import (
	"fmt"
	"os"
	"os/exec"
	"time"

	"github.com/andlabs/ui"
)

const (
	defCmdLine = "mpv -loop inf ~/ring.wav"
	defTime    = "10:30 AM"
	timeFmt    = "3:04 PM"
)

// If later hasn't happened yet, make it happen on the day of now; if not, the day after.
func bestTime(now time.Time, later time.Time) time.Time {
	now = now.Local() // use local time to make things make sense
	nowh, nowm, nows := now.Clock()
	laterh, laterm, laters := later.Clock()
	add := false
	if nowh > laterh {
		add = true
	} else if (nowh == laterh) && (nowm > laterm) {
		add = true
	} else if (nowh == laterh) && (nowm == laterm) && (nows >= laters) {
		// >= in the case we're on the exact second; add a day because the alarm should have gone off by now otherwise!
		add = true
	}
	if add {
		now = now.AddDate(0, 0, 1)
	}
	return time.Date(now.Year(), now.Month(), now.Day(),
		laterh, laterm, laters, 0,
		now.Location())
}

func myMain() {
	var cmd *exec.Cmd
	var timer *time.Timer
	var timerChan <-chan time.Time
	var w *ui.Window

	status := ui.NewLabel("")

	stop := func() {
		if cmd != nil { // stop the command if it's running
			err := cmd.Process.Kill()
			if err != nil {
				<-w.MsgBoxError(
					fmt.Sprintf("Error killing process: %v", err),
					"You may need to kill it manually.")
			}
			err = cmd.Process.Release()
			if err != nil {
				<-w.MsgBoxError(
					fmt.Sprintf("Error releasing process: %v", err),
					"")
			}
			cmd = nil
		}
		if timer != nil { // stop the timer if we started it
			timer.Stop()
			timer = nil
			timerChan = nil
		}
		status.SetText("")
	}

	w = ui.NewWindow("wakeup", 400, 100)
	ui.AppQuit = w.Closing // treat application close as main window close
	cmdbox := ui.NewLineEdit(defCmdLine)
	timebox := ui.NewLineEdit(defTime)
	bStart := ui.NewButton("Start")
	bStop := ui.NewButton("Stop")

	// a Stack to keep both buttons at the same size
	btnbox := ui.NewHorizontalStack(bStart, bStop)
	btnbox.SetStretchy(0)
	btnbox.SetStretchy(1)
	// and a Stack around that Stack to keep them at a reasonable size, with space to their right
	btnbox = ui.NewHorizontalStack(btnbox, status)

	// the main layout
	grid := ui.NewGrid(2,
		ui.NewLabel("Command"), cmdbox,
		ui.NewLabel("Time"), timebox,
		ui.Space(), ui.Space(), // the Space on the right will consume the window blank space
		ui.Space(), btnbox)
	grid.SetStretchy(2, 1) // make the Space noted above consume
	grid.SetFilling(0, 1)  // make the two textboxes grow horizontally
	grid.SetFilling(1, 1)

	w.Open(grid)

mainloop:
	for {
		select {
		case <-w.Closing:
			break mainloop
		case <-bStart.Clicked:
			stop() // only one alarm at a time
			alarmTime, err := time.Parse(timeFmt, timebox.Text())
			if err != nil {
				<-w.MsgBoxError(
					fmt.Sprintf("Error parsing time %q: %v", timebox.Text(), err),
					fmt.Sprintf("Make sure your time is in the form %q (without quotes).", timeFmt))
				continue
			}
			now := time.Now()
			later := bestTime(now, alarmTime)
			timer = time.NewTimer(later.Sub(now))
			timerChan = timer.C
			status.SetText("Started")
		case <-timerChan:
			cmd = exec.Command("/bin/sh", "-c", "exec "+cmdbox.Text())
			// keep stdin /dev/null in case user wants to run multiple alarms on one instance (TODO should I allow this program to act as a pipe?)
			// keep stdout /dev/null to avoid stty mucking
			cmd.Stderr = os.Stderr
			err := cmd.Start()
			status.SetText("Firing")
			if err != nil {
				<-w.MsgBoxError(
					fmt.Sprintf("Error running program: %v", err),
					"")
				cmd = nil
				status.SetText("")
			}
			timer = nil
			timerChan = nil
		case <-bStop.Clicked:
			stop()
		}
	}

	// clean up
	stop()
}

func main() {
	err := ui.Go(myMain)
	if err != nil {
		panic(fmt.Errorf("error initializing UI library: %v", err))
	}
}
<|endoftext|>


<commit_before>package main

func main() {
	flags := ParseCmdArgs()
	if flags.Help {
		ExitWithUsage()
	} else if flags.Version {
		ExitWithVersion()
	}

	g := NewGenerator(
		TemplateDir(),
		NewRepositoryFromURL(RemoteURL("origin")),
	)
	if flags.IssueOnly {
		g.GenerateIssueTemplate()
	} else if flags.PROnly {
		g.GeneratePRTemplate()
	} else if flags.ContributingOnly {
		g.GenerateContributingTemplate()
	} else {
		g.GenerateAllTemplates()
	}
}
<commit_msg>allow multiple flags<commit_after>package main

func main() {
	flags := ParseCmdArgs()
	if flags.Help {
		ExitWithUsage()
	} else if flags.Version {
		ExitWithVersion()
	}

	g := NewGenerator(
		TemplateDir(),
		NewRepositoryFromURL(RemoteURL("origin")),
	)
	if flags.IssueOnly {
		g.GenerateIssueTemplate()
	}
	if flags.PROnly {
		g.GeneratePRTemplate()
	}
	if flags.ContributingOnly {
		g.GenerateContributingTemplate()
	}
	if !flags.IssueOnly && !flags.PROnly && !flags.ContributingOnly {
		g.GenerateAllTemplates()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"os"
)

var Cfg Config
var utl util.SomaUtil

func main() {
	app := cli.NewApp()
	app.Name = "somaadm"
	app.Usage = "SOMA Administrative Interface"
	app.Version = "0.2.0"

	app = registerCommands(*app)
	app = registerFlags(*app)

	app.Run(os.Args)
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>0.3.0: merged serviceprop branch<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"os"
)

var Cfg Config
var utl util.SomaUtil

func main() {
	app := cli.NewApp()
	app.Name = "somaadm"
	app.Usage = "SOMA Administrative Interface"
	app.Version = "0.3.0"

	app = registerCommands(*app)
	app = registerFlags(*app)

	app.Run(os.Args)
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>// HttpDebugProxy is a HTTP proxy which dumps out the requests and responses.
package main

import (
	"flag"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

// CustomTransport replaces DefaultTransport for overriding RoundTrip.
type CustomTransport struct{}

func main() {
	// TODO: improve flags.
	var src, dst string
	flag.Parse()
	args := flag.Args()
	// First arg is destination.
	if len(args) >= 1 {
		dst = args[0]
	} else {
		dst = "http://127.0.0.1:8080"
	}
	// Second arg is source.
	if len(args) == 2 {
		src = args[1]
	} else {
		src = ":80"
	}

	// Create destination url.
	u, err := url.Parse(dst)
	if err != nil {
		log.Fatal("Error parsing destination url:", err)
	}

	// Create reverse proxy.
	proxy := httputil.NewSingleHostReverseProxy(u)
	proxy.Transport = &CustomTransport{}

	// Create server.
	s := &http.Server{
		Addr:           src,
		Handler:        proxy,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	log.Fatal(s.ListenAndServe())
}

// RoundTrip replaces DefaultTransport RoundTrip, contains the Request and Response dumps.
func (t *CustomTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	// Read the Request, including body.
	reqBody, err := httputil.DumpRequest(req, true)
	if err != nil {
		log.Println("Error calling DumpRequest:", err)
	}
	log.Println(">>>>> Request >>>>>")
	log.Println("\n\n" + string(reqBody))

	// Send request and receive response.
	response, err := http.DefaultTransport.RoundTrip(req)
	if err != nil {
		log.Println("Error calling RoundTrip:", err)
		return nil, err
	}

	// Read the Response, including body.
	respBody, err := httputil.DumpResponse(response, true)
	if err != nil {
		log.Println("Error calling DumpResponse:", err)
		return nil, err
	}
	log.Println("<<<<< Response <<<<<")
	log.Println("\n\n" + string(respBody))

	return response, err
}
<commit_msg>Refactored.<commit_after>// HttpDebugProxy is a HTTP proxy which dumps out the requests and responses.
package main

import (
	"flag"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

// CustomTransport replaces DefaultTransport for overriding RoundTrip.
type CustomTransport struct{}

func main() {
	// TODO: improve flags.
	var src, dst string
	flag.Parse()
	args := flag.Args()
	// First arg is destination.
	if len(args) >= 1 {
		dst = args[0]
	} else {
		dst = "http://127.0.0.1:8080"
	}
	// Second arg is source.
	if len(args) == 2 {
		src = args[1]
	} else {
		src = ":80"
	}

	s, err := buildServer(src, dst)
	if err != nil {
		log.Fatal("Error building server:", err)
	}
	log.Fatal(s.ListenAndServe())
}
func buildServer(src string, dst string) (*http.Server, error) {
	// Create destination url.
	u, err := url.Parse(dst)
	if err != nil {
		log.Fatal("Error parsing destination url:", err)
		return nil, err
	}

	// Create reverse proxy.
	proxy := httputil.NewSingleHostReverseProxy(u)
	proxy.Transport = &CustomTransport{}

	// Create server.
	s := &http.Server{
		Addr:           src,
		Handler:        proxy,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	return s, nil
}

// RoundTrip replaces DefaultTransport RoundTrip, contains the Request and Response dumps.
func (t *CustomTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	// Read the Request, including body.
	reqBody, err := httputil.DumpRequest(req, true)
	if err != nil {
		log.Println("Error calling DumpRequest:", err)
	}
	log.Println(">>>>> Request >>>>>")
	log.Println("\n\n" + string(reqBody))

	// Send request and receive response.
	response, err := http.DefaultTransport.RoundTrip(req)
	if err != nil {
		log.Println("Error calling RoundTrip:", err)
		return nil, err
	}

	// Read the Response, including body.
	respBody, err := httputil.DumpResponse(response, true)
	if err != nil {
		log.Println("Error calling DumpResponse:", err)
		return nil, err
	}
	log.Println("<<<<< Response <<<<<")
	log.Println("\n\n" + string(respBody))

	return response, err
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"time"

	"github.com/codegangsta/cli"
)

func main() {
	app := cli.NewApp()
	app.Name = "swan"
	app.Usage = "terminal migrations"
	app.Commands = []cli.Command{
		{
			Name:        "run",
			Aliases:     []string{"r"},
			Usage:       "run migrations",
			Description: "run all migrations since the last migration",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "dir",
					Value: "./migrations",
					Usage: "migration directory",
				},
				cli.StringFlag{
					Name:  "last",
					Value: ".swan",
					Usage: "last migration file",
				},
			},
			Action: func(c *cli.Context) {
				dir := c.String("dir")
				last := c.String("last")
				if err := Run(last, dir); err != nil {
					os.Exit(1)
				}
			},
		},
		{
			Name:        "create",
			Aliases:     []string{"c"},
			Usage:       "create a new migration: create [name] [ext]",
			Description: "create a new migration in the provided migrations directory",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "dir",
					Value: "./migrations",
					Usage: "migration directory",
				},
			},
			Action: func(c *cli.Context) {
				name := c.Args().First()
				ext := c.Args().Get(1)
				if ext == "" {
					ext = "sh"
				}
				dir := c.String("dir")

				if err := Create(name, dir, ext, time.Now()); err != nil {
					os.Exit(1)
				}
			},
		},
	}

	app.Run(os.Args)
}
<commit_msg>Use constants for defaults<commit_after>package main

import (
	"os"
	"time"

	"github.com/codegangsta/cli"
)

const (
	defaultDir = "migrations"
	lastFile   = ".swan"
)

func main() {
	app := cli.NewApp()
	app.Name = "swan"
	app.Usage = "terminal migrations"
	app.Commands = []cli.Command{
		{
			Name:        "run",
			Aliases:     []string{"r"},
			Usage:       "run migrations",
			Description: "run all migrations since the last migration",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "dir",
					Value: defaultDir,
					Usage: "migration directory",
				},
				cli.StringFlag{
					Name:  "last",
					Value: lastFile,
					Usage: "last migration file",
				},
			},
			Action: func(c *cli.Context) {
				dir := c.String("dir")
				last := c.String("last")
				if err := Run(last, dir); err != nil {
					os.Exit(1)
				}
			},
		},
		{
			Name:        "create",
			Aliases:     []string{"c"},
			Usage:       "create a new migration: create [name] [ext]",
			Description: "create a new migration in the provided migrations directory",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "dir",
					Value: defaultDir,
					Usage: "migration directory",
				},
			},
			Action: func(c *cli.Context) {
				name := c.Args().First()
				ext := c.Args().Get(1)
				if ext == "" {
					ext = "sh"
				}
				dir := c.String("dir")

				if err := Create(name, dir, ext, time.Now()); err != nil {
					os.Exit(1)
				}
			},
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/json"
	"flag"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os/exec"
	"time"

	"github.com/artyom/autoflags"
	yaml "gopkg.in/yaml.v2"
)

func main() {
	config := struct {
		Addr     string `flag:"listen,address to listen at"`
		Qsize    int    `flag:"qsize,job queue size"`
		Config   string `flag:"config,path to config (yaml)"`
		CertFile string `flag:"cert,path to ssl certificate"`
		KeyFile  string `flag:"key,path to ssl certificate key"`
	}{
		Addr:  "127.0.0.1:8080",
		Qsize: 10,
	}
	autoflags.Define(&config)
	flag.Parse()
	cfg, err := readConfig(config.Config)
	if err != nil {
		log.Fatal(err)
	}
	if config.Qsize < 1 {
		config.Qsize = 1
	}
	h := hookHandler{cmds: make(chan execEnv, config.Qsize)}
	for k, v := range cfg {
		http.HandleFunc(k, h.endpointHandler(v))
	}
	go h.run()
	server := &http.Server{
		Addr:           config.Addr,
		MaxHeaderBytes: 1 << 20,
		ReadTimeout:    15 * time.Second,
		WriteTimeout:   15 * time.Second,
	}
	if len(config.CertFile) > 0 && len(config.KeyFile) > 0 {
		log.Fatal(server.ListenAndServeTLS(config.CertFile, config.KeyFile))
	}
	log.Fatal(server.ListenAndServe())
}

// hookHandler manages receiving/dispatching hook requests and running
// corresponding commands
type hookHandler struct {
	cmds chan execEnv
}

// run receives commands to run on channel and executes them
func (hh hookHandler) run() {
	for item := range hh.cmds {
		var cmd *exec.Cmd
		c, ok := item.endpoint.Refs[item.payload.Ref]
		switch {
		case ok:
			log.Print("found per-ref command")
			cmd = exec.Command(c.Command, c.Args...)
		case !ok && len(item.endpoint.Command) > 0:
			log.Print("found global per-repo command")
			cmd = exec.Command(
				item.endpoint.Command,
				item.endpoint.Args...)
		default:
			log.Printf("no matching command for ref %q found, skipping",
				item.payload.Ref)
			continue
		}
		log.Printf("repo: %q, ref: %q, command: %v",
			item.endpoint.RepoName, item.payload.Ref, cmd.Args)
		if err := cmd.Start(); err != nil {
			log.Print("failed to start command:", err)
			continue
		}
		go func(cmd *exec.Cmd) {
			if err := cmd.Wait(); err != nil {
				log.Print("command finished with error:", err)
				return
			}
			log.Print("command finished ok")
		}(cmd)
	}
}

// endpointHandler constructs http.HandlerFunc for particular endpoint
func (hh hookHandler) endpointHandler(ep endpoint) http.HandlerFunc {
	secret := []byte(ep.Secret)
	withSecret := len(ep.Secret) > 0
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.Error(w, "unsupported method",
				http.StatusMethodNotAllowed)
			return
		}
		switch r.Header.Get("X-Github-Event") {
		case "push":
		case "ping":
			return // accept with code 200
		default:
			http.Error(w, "unsupported event type",
				http.StatusBadRequest)
			return
		}
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "unsupported content type",
				http.StatusUnsupportedMediaType)
			return
		}
		var sig string
		if n, err := fmt.Sscanf(
			r.Header.Get("X-Hub-Signature"),
			"sha1=%s", &sig); n != 1 || err != nil {
			http.Error(w, "malformed signature", http.StatusForbidden)
			return
		}
		var (
			tr      io.Reader = r.Body
			mac     hash.Hash
			payload pushPayload
		)
		if withSecret {
			mac = hmac.New(sha1.New, secret)
			tr = io.TeeReader(r.Body, mac)
		}
		if err := json.NewDecoder(tr).Decode(&payload); err != nil {
			log.Print(err)
			http.Error(w, "malformed json",
				http.StatusInternalServerError)
			return
		}
		if withSecret {
			sig2 := fmt.Sprintf("%x", mac.Sum(nil))
			if sig != sig2 {
				log.Printf("signature mismatch, got %q, want %q", sig, sig2)
				http.Error(w, "signature mismatch",
					http.StatusPreconditionFailed)
				return
			}
		}
		if payload.Repository.Name != ep.RepoName {
			log.Printf("repository names mismatch: got %q, want %q",
				payload.Repository.Name, ep.RepoName)
			http.Error(w, "repository mismatch",
				http.StatusPreconditionFailed)
			return
		}
		select {
		case hh.cmds <- execEnv{payload, ep}:
		default: // spillover
			log.Print("buffer spillover")
			http.Error(w, "spillover", http.StatusServiceUnavailable)
			return
		}
	}
}

// execEnv used to pass both payload and endpoint info via channel
type execEnv struct {
	payload  pushPayload
	endpoint endpoint
}

type pushPayload struct {
	Ref        string `json:"ref"`
	Repository struct {
		Name     string `json:"name"`
		FullName string `json:"full_name"`
		HttpUrl  string `json:"html_url"`
		SshUrl   string `json:"ssh_url"`
		GitUrl   string `json:"git_url"`
		CloneUrl string `json:"clone_url"`
	} `json:"repository"`
}

// endpoint represents config for one repository, handled by particular url
type endpoint struct {
	RepoName string
	Secret   string
	Command  string // global command used if no per-ref command found
	Args     []string
	Refs     map[string]struct {
		Command string // per-ref commands
		Args    []string
	}
}

// readConfig loads configuration from yaml file
//
// Config should be in form map[string]endpoint, where keys are urls used to set
// up http handlers.
func readConfig(fileName string) (map[string]endpoint, error) {
	b, err := ioutil.ReadFile(fileName)
	if err != nil {
		return nil, err
	}
	out := make(map[string]endpoint)
	if err := yaml.Unmarshal(b, out); err != nil {
		return nil, err
	}
	return out, nil
}
<commit_msg>Add -verbose and -timeout flags<commit_after>package main

import (
	"context"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/json"
	"flag"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"time"

	"github.com/artyom/autoflags"
	yaml "gopkg.in/yaml.v2"
)

func main() {
	config := struct {
		Addr     string        `flag:"listen,address to listen at"`
		Qsize    int           `flag:"qsize,job queue size"`
		Config   string        `flag:"config,path to config (yaml)"`
		CertFile string        `flag:"cert,path to ssl certificate"`
		KeyFile  string        `flag:"key,path to ssl certificate key"`
		Timeout  time.Duration `flag:"timeout,timeout for command run"`
		Verbose  bool          `flag:"verbose,pass stdout/stderr from commands to stderr"`
	}{
		Addr:    "127.0.0.1:8080",
		Qsize:   10,
		Timeout: 3 * time.Minute,
	}
	autoflags.Define(&config)
	flag.Parse()
	cfg, err := readConfig(config.Config)
	if err != nil {
		log.Fatal(err)
	}
	if config.Qsize < 1 {
		config.Qsize = 1
	}
	h := hookHandler{
		cmds:    make(chan execEnv, config.Qsize),
		timeout: config.Timeout,
		verbose: config.Verbose,
	}
	for k, v := range cfg {
		http.HandleFunc(k, h.endpointHandler(v))
	}
	go h.run()
	server := &http.Server{
		Addr:           config.Addr,
		MaxHeaderBytes: 1 << 20,
		ReadTimeout:    15 * time.Second,
		WriteTimeout:   15 * time.Second,
	}
	if len(config.CertFile) > 0 && len(config.KeyFile) > 0 {
		log.Fatal(server.ListenAndServeTLS(config.CertFile, config.KeyFile))
	}
	log.Fatal(server.ListenAndServe())
}

// hookHandler manages receiving/dispatching hook requests and running
// corresponding commands
type hookHandler struct {
	cmds    chan execEnv
	timeout time.Duration
	verbose bool
}

// run receives commands to run on channel and executes them
func (hh hookHandler) run() {
	cmdRun := func(item execEnv) error {
		ctx := context.Background()
		if hh.timeout > 0 {
			var cancel func()
			ctx, cancel = context.WithTimeout(ctx, hh.timeout)
			defer cancel()
		}
		var cmd *exec.Cmd
		c, ok := item.endpoint.Refs[item.payload.Ref]
		switch {
		case ok:
			log.Print("found per-ref command")
			cmd = exec.CommandContext(ctx, c.Command, c.Args...)
		case !ok && len(item.endpoint.Command) > 0:
			log.Print("found global per-repo command")
			cmd = exec.CommandContext(ctx,
				item.endpoint.Command,
				item.endpoint.Args...)
		default:
			log.Printf("no matching command for ref %q found, skipping",
				item.payload.Ref)
			return nil
		}
		log.Printf("repo: %q, ref: %q, command: %v",
			item.endpoint.RepoName, item.payload.Ref, cmd.Args)
		if hh.verbose {
			cmd.Stdout = os.Stderr
			cmd.Stderr = os.Stderr
		}
		return cmd.Run()
	}
	for item := range hh.cmds {
		if err := cmdRun(item); err != nil {
			log.Printf("repo: %q, ref: %q, command run: %v",
				item.endpoint.RepoName, item.payload.Ref, err)
		}
	}
}

// endpointHandler constructs http.HandlerFunc for particular endpoint
func (hh hookHandler) endpointHandler(ep endpoint) http.HandlerFunc {
	secret := []byte(ep.Secret)
	withSecret := len(ep.Secret) > 0
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.Error(w, "unsupported method",
				http.StatusMethodNotAllowed)
			return
		}
		switch r.Header.Get("X-Github-Event") {
		case "push":
		case "ping":
			return // accept with code 200
		default:
			http.Error(w, "unsupported event type",
				http.StatusBadRequest)
			return
		}
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "unsupported content type",
				http.StatusUnsupportedMediaType)
			return
		}
		var sig string
		if n, err := fmt.Sscanf(
			r.Header.Get("X-Hub-Signature"),
			"sha1=%s", &sig); n != 1 || err != nil {
			http.Error(w, "malformed signature", http.StatusForbidden)
			return
		}
		var (
			tr      io.Reader = r.Body
			mac     hash.Hash
			payload pushPayload
		)
		if withSecret {
			mac = hmac.New(sha1.New, secret)
			tr = io.TeeReader(r.Body, mac)
		}
		if err := json.NewDecoder(tr).Decode(&payload); err != nil {
			log.Print(err)
			http.Error(w, "malformed json",
				http.StatusInternalServerError)
			return
		}
		if withSecret {
			sig2 := fmt.Sprintf("%x", mac.Sum(nil))
			if sig != sig2 {
				log.Printf("signature mismatch, got %q, want %q", sig, sig2)
				http.Error(w, "signature mismatch",
					http.StatusPreconditionFailed)
				return
			}
		}
		if payload.Repository.Name != ep.RepoName {
			log.Printf("repository names mismatch: got %q, want %q",
				payload.Repository.Name, ep.RepoName)
			http.Error(w, "repository mismatch",
				http.StatusPreconditionFailed)
			return
		}
		select {
		case hh.cmds <- execEnv{payload, ep}:
		default: // spillover
			log.Print("buffer spillover")
			http.Error(w, "spillover", http.StatusServiceUnavailable)
			return
		}
	}
}

// execEnv used to pass both payload and endpoint info via channel
type execEnv struct {
	payload  pushPayload
	endpoint endpoint
}

type pushPayload struct {
	Ref        string `json:"ref"`
	Repository struct {
		Name     string `json:"name"`
		FullName string `json:"full_name"`
		HttpUrl  string `json:"html_url"`
		SshUrl   string `json:"ssh_url"`
		GitUrl   string `json:"git_url"`
		CloneUrl string `json:"clone_url"`
	} `json:"repository"`
}

// endpoint represents config for one repository, handled by particular url
type endpoint struct {
	RepoName string
	Secret   string
	Command  string // global command used if no per-ref command found
	Args     []string
	Refs     map[string]struct {
		Command string // per-ref commands
		Args    []string
	}
}

// readConfig loads configuration from yaml file
//
// Config should be in form map[string]endpoint, where keys are urls used to set
// up http handlers.
func readConfig(fileName string) (map[string]endpoint, error) {
	b, err := ioutil.ReadFile(fileName)
	if err != nil {
		return nil, err
	}
	out := make(map[string]endpoint)
	if err := yaml.Unmarshal(b, out); err != nil {
		return nil, err
	}
	return out, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"code.google.com/p/gcfg"
	"code.google.com/p/go-uuid/uuid"
	"fmt"
	"github.com/dpapathanasiou/go-recaptcha"
	"github.com/justinas/nosurf"
	"github.com/worr/chrooter"
	"github.com/worr/secstring"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"net/mail"
	"net/smtp"
	"time"
	txttemplate "text/template"
)

type Config struct {
	Mail struct {
		Email    string
		Username string
		Password string
		Hostname string
		password *secstring.SecString
	}

	Recaptcha struct {
		Private string
	}
}

var t = template.Must(template.ParseFiles("template/index.html"))
var emailTemplate = txttemplate.Must(txttemplate.New("email").Parse("Here is your exclusive Vim download link: http://www.vim.org/download.php?code={{.Code}}"))
var c = make(chan string)
var conf Config

// Default handler
func dispatch(w http.ResponseWriter, r *http.Request) {
	context := map[string]string{
		"token": nosurf.Token(r),
	}

	if r.Method == "POST" {
		context["email"] = r.FormValue("email")
		if !recaptcha.Confirm(r.RemoteAddr, r.FormValue("recaptcha_challenge_field"), r.FormValue("recaptcha_response_field")) {
			http.Error(w, "Failed captcha", http.StatusBadRequest)
			return
		}

		if context["email"] == "" {
			http.Error(w, "Empty email address", http.StatusBadRequest)
			return
		}

		c <- context["email"]
	}

	if err := t.Execute(w, context); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// Handler for all bad CSRF requests
func failedCSRF(w http.ResponseWriter, r *http.Request) {
	http.Error(w, nosurf.Reason(r).Error(), http.StatusBadRequest)
}

// Pulls email off of the channel and possibly sends download codes
func email() {
	auth := smtp.PlainAuth("", conf.Mail.Username, string(conf.Mail.password.String), conf.Mail.Hostname)

	for addr := range c {
		// Exclusivity
		if r := rand.Intn(3); r != 0 {
			continue
		}

		var emailAddr *mail.Address
		var err error
		if emailAddr, err = mail.ParseAddress(addr); err != nil {
			log.Printf("Failed to send email to %v: %v", emailAddr.Address, err)
			continue
		}

		fromAddr := mail.Address{Name:"Vim Download Czar", Address: conf.Mail.Email}
		headers := make(map[string]string)
		headers["To"] = emailAddr.String()
		headers["From"] = fromAddr.String()
		headers["Date"] = time.Now().String()
		headers["Subject"] = "Your Vim invite is ready"
		headers["MIME-Version"] = "1.0"
		headers["Content-Type"] = "text/plain; charset=\"utf-8\""

		msg := ""
		for key, val := range(headers) {
			msg += fmt.Sprintf("%v: %v\r\n", key, val)
		}

		buf := bytes.NewBuffer(make([]byte, 0))
		if err := emailTemplate.Execute(buf, struct{ Code string }{uuid.NewUUID().String()}); err != nil {
			log.Printf("Can't execute email template: %v", err)
			continue
		}

		msg += fmt.Sprintf("\r\n%s", buf)

		conf.Mail.password.Decrypt()
		if err = smtp.SendMail(conf.Mail.Hostname, auth, fromAddr.Address, []string{emailAddr.Address}, []byte(msg)); err != nil {
			log.Printf("Failed to send email to %v: %v", emailAddr.Address, err)
			continue
		}
		conf.Mail.password.Encrypt()
	}
}

func main() {
	if err := gcfg.ReadFileInto(&conf, "vim.sexy.ini"); err != nil {
		log.Fatalf("Can't read config file: %v", err)
	}

	if err := chrooter.Chroot("www", "/var/chroot/vim.sexy"); err != nil {
		log.Fatalf("Can't chroot: %v", err)
	}

	var err error
	if conf.Mail.password, err = secstring.FromString(&conf.Mail.Password); err != nil {
		log.Fatal(err)
	}

	recaptcha.Init(conf.Recaptcha.Private)

	go email()

	http.HandleFunc("/", dispatch)
	csrf := nosurf.New(http.DefaultServeMux)
	cookie := http.Cookie{HttpOnly: true}
	csrf.SetBaseCookie(cookie)
	csrf.SetFailureHandler(http.HandlerFunc(failedCSRF))
	if err = http.ListenAndServe("127.0.0.1:8000", csrf); err != nil {
		log.Fatalf("Cannot listen: %v", err)
	}
}
<commit_msg>CSRF dickery<commit_after>package main

import (
	"bytes"
	"code.google.com/p/gcfg"
	"code.google.com/p/go-uuid/uuid"
	"fmt"
	"github.com/dpapathanasiou/go-recaptcha"
	"github.com/justinas/nosurf"
	"github.com/worr/chrooter"
	"github.com/worr/secstring"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"net/mail"
	"net/smtp"
	"time"
	txttemplate "text/template"
)

type Config struct {
	Mail struct {
		Email    string
		Username string
		Password string
		Hostname string
		password *secstring.SecString
	}

	Recaptcha struct {
		Private string
	}
}

var t = template.Must(template.ParseFiles("template/index.html"))
var emailTemplate = txttemplate.Must(txttemplate.New("email").Parse("Here is your exclusive Vim download link: http://www.vim.org/download.php?code={{.Code}}"))
var c = make(chan string)
var conf Config

// Default handler
func dispatch(w http.ResponseWriter, r *http.Request) {
	context := map[string]string{
		"token": nosurf.Token(r),
	}

	if r.Method == "POST" {
		context["email"] = r.FormValue("email")
		if !recaptcha.Confirm(r.RemoteAddr, r.FormValue("recaptcha_challenge_field"), r.FormValue("recaptcha_response_field")) {
			http.Error(w, "Failed captcha", http.StatusBadRequest)
			return
		}

		if context["email"] == "" {
			http.Error(w, "Empty email address", http.StatusBadRequest)
			return
		}

		c <- context["email"]
	}

	if err := t.Execute(w, context); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// Handler for all bad CSRF requests
func failedCSRF(w http.ResponseWriter, r *http.Request) {
	http.Error(w, nosurf.Reason(r).Error(), http.StatusBadRequest)
}

// Pulls email off of the channel and possibly sends download codes
func email() {
	auth := smtp.PlainAuth("", conf.Mail.Username, string(conf.Mail.password.String), conf.Mail.Hostname)

	for addr := range c {
		// Exclusivity
		if r := rand.Intn(3); r != 0 {
			continue
		}

		var emailAddr *mail.Address
		var err error
		if emailAddr, err = mail.ParseAddress(addr); err != nil {
			log.Printf("Failed to send email to %v: %v", emailAddr.Address, err)
			continue
		}

		fromAddr := mail.Address{Name:"Vim Download Czar", Address: conf.Mail.Email}
		headers := make(map[string]string)
		headers["To"] = emailAddr.String()
		headers["From"] = fromAddr.String()
		headers["Date"] = time.Now().String()
		headers["Subject"] = "Your Vim invite is ready"
		headers["MIME-Version"] = "1.0"
		headers["Content-Type"] = "text/plain; charset=\"utf-8\""

		msg := ""
		for key, val := range(headers) {
			msg += fmt.Sprintf("%v: %v\r\n", key, val)
		}

		buf := bytes.NewBuffer(make([]byte, 0))
		if err := emailTemplate.Execute(buf, struct{ Code string }{uuid.NewUUID().String()}); err != nil {
			log.Printf("Can't execute email template: %v", err)
			continue
		}

		msg += fmt.Sprintf("\r\n%s", buf)

		conf.Mail.password.Decrypt()
		if err = smtp.SendMail(conf.Mail.Hostname, auth, fromAddr.Address, []string{emailAddr.Address}, []byte(msg)); err != nil {
			log.Printf("Failed to send email to %v: %v", emailAddr.Address, err)
			continue
		}
		conf.Mail.password.Encrypt()
	}
}

func main() {
	if err := gcfg.ReadFileInto(&conf, "vim.sexy.ini"); err != nil {
		log.Fatalf("Can't read config file: %v", err)
	}

	if err := chrooter.Chroot("www", "/var/chroot/vim.sexy"); err != nil {
		log.Fatalf("Can't chroot: %v", err)
	}

	var err error
	if conf.Mail.password, err = secstring.FromString(&conf.Mail.Password); err != nil {
		log.Fatal(err)
	}

	recaptcha.Init(conf.Recaptcha.Private)

	go email()

	csrf := nosurf.New(http.HandleFunc("/", dispatch))
	cookie := http.Cookie{HttpOnly: true}
	csrf.SetBaseCookie(cookie)
	csrf.SetFailureHandler(http.HandlerFunc(failedCSRF))
	if err = http.ListenAndServe("127.0.0.1:8000", csrf); err != nil {
		log.Fatalf("Cannot listen: %v", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
)

const (
	rootDir        = "/"
	welcomeMessage = "Welcome to the Go FTP Server"
	USER           = "USER"
	PASS           = "PASS"
)

func getMessageFormat(command int) (messageFormat string) {
	switch command {
	case 220:
		messageFormat = "220 %s"
		break
	case 230:
		messageFormat = "230 %s"
		break
	case 331:
		messageFormat = "331 %s"
		break
	case 500:
		messageFormat = "500 %s"
		break
	}
	return messageFormat + "\r\n"
}

type Array struct {
	container []interface{}
}

func (a *Array) Append(object interface{}) {
	if a.container == nil {
		a.container = make([]interface{}, 0)
	}
	newContainer := make([]interface{}, len(a.container)+1)
	copy(newContainer, a.container)
	newContainer[len(newContainer)-1] = object
	a.container = newContainer
}

func (a *Array) Remove(object interface{}) (result bool) {
	result = false
	newContainer := make([]interface{}, len(a.container)-1)
	i := 0
	for _, target := range a.container {
		if target != object {
			newContainer[i] = target
		} else {
			result = true
		}
		i++
	}
	return
}

type FTPServer struct {
	name        string
	listener    *net.TCPListener
	connections *Array
}

func (ftpServer *FTPServer) Listen() (err error) {
	for {
		ftpConn, err := ftpServer.Accept()
		if err != nil {
			break
		}
		ftpServer.connections.Append(ftpConn)
		terminated := make(chan bool)
		go ftpConn.Serve(terminated)
		<-terminated
		ftpServer.connections.Remove(ftpConn)
		ftpConn.Close()
	}
	return
}

func (ftpServer *FTPServer) Accept() (ftpConn *FTPConn, err error) {
	tcpConn, err := ftpServer.listener.AcceptTCP()
	if err == nil {
		controlReader := bufio.NewReader(tcpConn)
		controlWriter := bufio.NewWriter(tcpConn)
		ftpConn = &FTPConn{
			rootDir,
			tcpConn,
			controlReader,
			controlWriter,
			nil,
		}
	}
	return
}

type FTPConn struct {
	cwd           string
	control       *net.TCPConn
	controlReader *bufio.Reader
	controlWriter *bufio.Writer
	data          *net.TCPConn
}

func (ftpConn *FTPConn) WriteMessage(messageFormat string, v ...interface{}) (wrote int, err error) {
	message := fmt.Sprintf(messageFormat, v...)
	wrote, err = ftpConn.controlWriter.WriteString(message)
	ftpConn.controlWriter.Flush()
	log.Print(message)
	return
}

func (ftpConn *FTPConn) Serve(terminated chan bool) {
	log.Print("Connection Established")
	// send welcome
	ftpConn.WriteMessage(getMessageFormat(220), welcomeMessage)
	// read commands
	for {
		line, err := ftpConn.controlReader.ReadString('\n')
		if err != nil {
			break
		}
		log.Print(line)
		params := strings.Split(strings.Trim(line, "\r\n"), " ")
		count := len(params)
		if count > 0 {
			command := params[0]
			switch command {
			case USER:
				ftpConn.WriteMessage(getMessageFormat(331), "User name ok, password required")
				break
			case PASS:
				ftpConn.WriteMessage(getMessageFormat(230), "Password ok, continue")
				break
			default:
				ftpConn.WriteMessage(getMessageFormat(500), "Command not found")
			}
		} else {
			ftpConn.WriteMessage(getMessageFormat(500), "Syntax error, zero parameters")
		}
	}
	terminated <- true
	log.Print("Connection Terminated")
}

func (ftpConn *FTPConn) Close() {
	ftpConn.control.Close()
	if ftpConn.data != nil {
		ftpConn.data.Close()
	}
}

func main() {
	laddr, err := net.ResolveTCPAddr("tcp", "localhost:3000")
	if err != nil {
		log.Fatal(err)
	}
	listener, err := net.ListenTCP("tcp4", laddr)
	if err != nil {
		log.Fatal(err)
	}
	ftpServer := &FTPServer{
		"Go FTP Server",
		listener,
		new(Array),
	}
	log.Fatal(ftpServer.Listen())
}
<commit_msg>remove main function from this package<commit_after>package raval

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
)

const (
	rootDir        = "/"
	welcomeMessage = "Welcome to the Go FTP Server"
	USER           = "USER"
	PASS           = "PASS"
)

func getMessageFormat(command int) (messageFormat string) {
	switch command {
	case 220:
		messageFormat = "220 %s"
		break
	case 230:
		messageFormat = "230 %s"
		break
	case 331:
		messageFormat = "331 %s"
		break
	case 500:
		messageFormat = "500 %s"
		break
	}
	return messageFormat + "\r\n"
}

type Array struct {
	container []interface{}
}

func (a *Array) Append(object interface{}) {
	if a.container == nil {
		a.container = make([]interface{}, 0)
	}
	newContainer := make([]interface{}, len(a.container)+1)
	copy(newContainer, a.container)
	newContainer[len(newContainer)-1] = object
	a.container = newContainer
}

func (a *Array) Remove(object interface{}) (result bool) {
	result = false
	newContainer := make([]interface{}, len(a.container)-1)
	i := 0
	for _, target := range a.container {
		if target != object {
			newContainer[i] = target
		} else {
			result = true
		}
		i++
	}
	return
}

type FTPServer struct {
	name        string
	listener    *net.TCPListener
	connections *Array
}

func (ftpServer *FTPServer) Listen() (err error) {
	for {
		ftpConn, err := ftpServer.Accept()
		if err != nil {
			break
		}
		ftpServer.connections.Append(ftpConn)
		terminated := make(chan bool)
		go ftpConn.Serve(terminated)
		<-terminated
		ftpServer.connections.Remove(ftpConn)
		ftpConn.Close()
	}
	return
}

func (ftpServer *FTPServer) Accept() (ftpConn *FTPConn, err error) {
	tcpConn, err := ftpServer.listener.AcceptTCP()
	if err == nil {
		controlReader := bufio.NewReader(tcpConn)
		controlWriter := bufio.NewWriter(tcpConn)
		ftpConn = &FTPConn{
			rootDir,
			tcpConn,
			controlReader,
			controlWriter,
			nil,
		}
	}
	return
}

type FTPConn struct {
	cwd           string
	control       *net.TCPConn
	controlReader *bufio.Reader
	controlWriter *bufio.Writer
	data          *net.TCPConn
}

func (ftpConn *FTPConn) WriteMessage(messageFormat string, v ...interface{}) (wrote int, err error) {
	message := fmt.Sprintf(messageFormat, v...)
	wrote, err = ftpConn.controlWriter.WriteString(message)
	ftpConn.controlWriter.Flush()
	log.Print(message)
	return
}

func (ftpConn *FTPConn) Serve(terminated chan bool) {
	log.Print("Connection Established")
	// send welcome
	ftpConn.WriteMessage(getMessageFormat(220), welcomeMessage)
	// read commands
	for {
		line, err := ftpConn.controlReader.ReadString('\n')
		if err != nil {
			break
		}
		log.Print(line)
		params := strings.Split(strings.Trim(line, "\r\n"), " ")
		count := len(params)
		if count > 0 {
			command := params[0]
			switch command {
			case USER:
				ftpConn.WriteMessage(getMessageFormat(331), "User name ok, password required")
				break
			case PASS:
				ftpConn.WriteMessage(getMessageFormat(230), "Password ok, continue")
				break
			default:
				ftpConn.WriteMessage(getMessageFormat(500), "Command not found")
			}
		} else {
			ftpConn.WriteMessage(getMessageFormat(500), "Syntax error, zero parameters")
		}
	}
	terminated <- true
	log.Print("Connection Terminated")
}

func (ftpConn *FTPConn) Close() {
	ftpConn.control.Close()
	if ftpConn.data != nil {
		ftpConn.data.Close()
	}
}

<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"runtime"
	"runtime/debug"

	"github.com/eBay/fabio/admin"
	"github.com/eBay/fabio/config"
	"github.com/eBay/fabio/metrics"
	"github.com/eBay/fabio/proxy"
	"github.com/eBay/fabio/registry"
	"github.com/eBay/fabio/registry/consul"
	"github.com/eBay/fabio/registry/file"
	"github.com/eBay/fabio/registry/static"
	"github.com/eBay/fabio/route"
)

// version contains the version number
//
// It is set by build/release.sh for tagged releases
// so that 'go get' just works.
//
// It is also set by the linker when fabio
// is built via the Makefile or the build/docker.sh
// script to ensure the correct version nubmer
var version = "1.1.3rc2"

func main() {
	var filename string
	var v bool
	flag.StringVar(&filename, "cfg", "", "path to config file")
	flag.BoolVar(&v, "v", false, "show version")
	flag.Parse()

	if v {
		fmt.Println(version)
		return
	}

	cfg, err := config.Load(filename)
	if err != nil {
		log.Fatalf("[FATAL] %s. %s", version, err)
	}

	log.Printf("[INFO] Runtime config\n" + toJSON(cfg))
	log.Printf("[INFO] Version %s starting", version)
	log.Printf("[INFO] Go runtime is %s", runtime.Version())

	initRuntime(cfg)
	initMetrics(cfg)
	initBackend(cfg)
	go watchBackend()
	startAdmin(cfg)
	startListeners(cfg.Listen, cfg.Proxy.ShutdownWait, newProxy(cfg))
	registry.Default.Deregister()
}

func newProxy(cfg *config.Config) *proxy.Proxy {
	if err := route.SetPickerStrategy(cfg.Proxy.Strategy); err != nil {
		log.Fatal("[FATAL] ", err)
	}
	log.Printf("[INFO] Using routing strategy %q", cfg.Proxy.Strategy)

	if err := route.SetMatcher(cfg.Proxy.Matcher); err != nil {
		log.Fatal("[FATAL] ", err)
	}
	log.Printf("[INFO] Using routing matching %q", cfg.Proxy.Matcher)

	tr := &http.Transport{
		ResponseHeaderTimeout: cfg.Proxy.ResponseHeaderTimeout,
		MaxIdleConnsPerHost:   cfg.Proxy.MaxConn,
		Dial: (&net.Dialer{
			Timeout:   cfg.Proxy.DialTimeout,
			KeepAlive: cfg.Proxy.KeepAliveTimeout,
		}).Dial,
	}

	return proxy.New(tr, cfg.Proxy)
}

func startAdmin(cfg *config.Config) {
	log.Printf("[INFO] Admin server listening on %q", cfg.UI.Addr)
	go func() {
		if err := admin.ListenAndServe(cfg, version); err != nil {
			log.Fatal("[FATAL] ui: ", err)
		}
	}()
}

func initMetrics(cfg *config.Config) {
	if err := metrics.Init(cfg.Metrics); err != nil {
		log.Fatal("[FATAL] ", err)
	}
}

func initRuntime(cfg *config.Config) {
	if os.Getenv("GOGC") == "" {
		log.Print("[INFO] Setting GOGC=", cfg.Runtime.GOGC)
		debug.SetGCPercent(cfg.Runtime.GOGC)
	} else {
		log.Print("[INFO] Using GOGC=", os.Getenv("GOGC"), " from env")
	}

	if os.Getenv("GOMAXPROCS") == "" {
		log.Print("[INFO] Setting GOMAXPROCS=", cfg.Runtime.GOMAXPROCS)
		runtime.GOMAXPROCS(cfg.Runtime.GOMAXPROCS)
	} else {
		log.Print("[INFO] Using GOMAXPROCS=", os.Getenv("GOMAXPROCS"), " from env")
	}
}

func initBackend(cfg *config.Config) {
	var err error

	switch cfg.Registry.Backend {
	case "file":
		registry.Default, err = file.NewBackend(cfg.Registry.File.Path)
	case "static":
		registry.Default, err = static.NewBackend(cfg.Registry.Static.Routes)
	case "consul":
		registry.Default, err = consul.NewBackend(&cfg.Registry.Consul)
	default:
		log.Fatal("[FATAL] Unknown registry backend ", cfg.Registry.Backend)
	}

	if err != nil {
		log.Fatal("[FATAL] Error initializing backend. ", err)
	}
	if err := registry.Default.Register(); err != nil {
		log.Fatal("[FATAL] Error registering backend. ", err)
	}
}

func watchBackend() {
	var (
		last   string
		svccfg string
		mancfg string
	)

	svc := registry.Default.WatchServices()
	man := registry.Default.WatchManual()

	for {
		select {
		case svccfg = <-svc:
		case mancfg = <-man:
		}

		// manual config overrides service config
		// order matters
		next := svccfg + "\n" + mancfg
		if next == last {
			continue
		}

		t, err := route.ParseString(next)
		if err != nil {
			log.Printf("[WARN] %s", err)
			continue
		}
		route.SetTable(t)

		last = next
	}
}

func toJSON(v interface{}) string {
	data, err := json.MarshalIndent(v, "", "    ")
	if err != nil {
		panic("json: " + err.Error())
	}
	return string(data)
}
<commit_msg>Release v1.1.3<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"runtime"
	"runtime/debug"

	"github.com/eBay/fabio/admin"
	"github.com/eBay/fabio/config"
	"github.com/eBay/fabio/metrics"
	"github.com/eBay/fabio/proxy"
	"github.com/eBay/fabio/registry"
	"github.com/eBay/fabio/registry/consul"
	"github.com/eBay/fabio/registry/file"
	"github.com/eBay/fabio/registry/static"
	"github.com/eBay/fabio/route"
)

// version contains the version number
//
// It is set by build/release.sh for tagged releases
// so that 'go get' just works.
//
// It is also set by the linker when fabio
// is built via the Makefile or the build/docker.sh
// script to ensure the correct version nubmer
var version = "1.1.3"

func main() {
	var filename string
	var v bool
	flag.StringVar(&filename, "cfg", "", "path to config file")
	flag.BoolVar(&v, "v", false, "show version")
	flag.Parse()

	if v {
		fmt.Println(version)
		return
	}

	cfg, err := config.Load(filename)
	if err != nil {
		log.Fatalf("[FATAL] %s. %s", version, err)
	}

	log.Printf("[INFO] Runtime config\n" + toJSON(cfg))
	log.Printf("[INFO] Version %s starting", version)
	log.Printf("[INFO] Go runtime is %s", runtime.Version())

	initRuntime(cfg)
	initMetrics(cfg)
	initBackend(cfg)
	go watchBackend()
	startAdmin(cfg)
	startListeners(cfg.Listen, cfg.Proxy.ShutdownWait, newProxy(cfg))
	registry.Default.Deregister()
}

func newProxy(cfg *config.Config) *proxy.Proxy {
	if err := route.SetPickerStrategy(cfg.Proxy.Strategy); err != nil {
		log.Fatal("[FATAL] ", err)
	}
	log.Printf("[INFO] Using routing strategy %q", cfg.Proxy.Strategy)

	if err := route.SetMatcher(cfg.Proxy.Matcher); err != nil {
		log.Fatal("[FATAL] ", err)
	}
	log.Printf("[INFO] Using routing matching %q", cfg.Proxy.Matcher)

	tr := &http.Transport{
		ResponseHeaderTimeout: cfg.Proxy.ResponseHeaderTimeout,
		MaxIdleConnsPerHost:   cfg.Proxy.MaxConn,
		Dial: (&net.Dialer{
			Timeout:   cfg.Proxy.DialTimeout,
			KeepAlive: cfg.Proxy.KeepAliveTimeout,
		}).Dial,
	}

	return proxy.New(tr, cfg.Proxy)
}

func startAdmin(cfg *config.Config) {
	log.Printf("[INFO] Admin server listening on %q", cfg.UI.Addr)
	go func() {
		if err := admin.ListenAndServe(cfg, version); err != nil {
			log.Fatal("[FATAL] ui: ", err)
		}
	}()
}

func initMetrics(cfg *config.Config) {
	if err := metrics.Init(cfg.Metrics); err != nil {
		log.Fatal("[FATAL] ", err)
	}
}

func initRuntime(cfg *config.Config) {
	if os.Getenv("GOGC") == "" {
		log.Print("[INFO] Setting GOGC=", cfg.Runtime.GOGC)
		debug.SetGCPercent(cfg.Runtime.GOGC)
	} else {
		log.Print("[INFO] Using GOGC=", os.Getenv("GOGC"), " from env")
	}

	if os.Getenv("GOMAXPROCS") == "" {
		log.Print("[INFO] Setting GOMAXPROCS=", cfg.Runtime.GOMAXPROCS)
		runtime.GOMAXPROCS(cfg.Runtime.GOMAXPROCS)
	} else {
		log.Print("[INFO] Using GOMAXPROCS=", os.Getenv("GOMAXPROCS"), " from env")
	}
}

func initBackend(cfg *config.Config) {
	var err error

	switch cfg.Registry.Backend {
	case "file":
		registry.Default, err = file.NewBackend(cfg.Registry.File.Path)
	case "static":
		registry.Default, err = static.NewBackend(cfg.Registry.Static.Routes)
	case "consul":
		registry.Default, err = consul.NewBackend(&cfg.Registry.Consul)
	default:
		log.Fatal("[FATAL] Unknown registry backend ", cfg.Registry.Backend)
	}

	if err != nil {
		log.Fatal("[FATAL] Error initializing backend. ", err)
	}
	if err := registry.Default.Register(); err != nil {
		log.Fatal("[FATAL] Error registering backend. ", err)
	}
}

func watchBackend() {
	var (
		last   string
		svccfg string
		mancfg string
	)

	svc := registry.Default.WatchServices()
	man := registry.Default.WatchManual()

	for {
		select {
		case svccfg = <-svc:
		case mancfg = <-man:
		}

		// manual config overrides service config
		// order matters
		next := svccfg + "\n" + mancfg
		if next == last {
			continue
		}

		t, err := route.ParseString(next)
		if err != nil {
			log.Printf("[WARN] %s", err)
			continue
		}
		route.SetTable(t)

		last = next
	}
}

func toJSON(v interface{}) string {
	data, err := json.MarshalIndent(v, "", "    ")
	if err != nil {
		panic("json: " + err.Error())
	}
	return string(data)
}
<|endoftext|>


<commit_before>package main

import (
    "encoding/json"
    "fmt"
    //"html"
    "io/ioutil"
    "net/http"
    "os"
    "path/filepath"
    "regexp"
    //"strconv"
    "strings"
    "time"
)

type Settings struct {
    RemoteDirectory string
    Address         string
}

type GameIDs map[string]string
var games GameIDs

var s Settings
var re_gamename = regexp.MustCompile(`<td itemprop="name">(.+?)</td>`)

func main() {
    loadSettings()

    if err := init_templates(); err != nil {
        fmt.Printf("Error loading templates: %s\n", err)
        return
    }

    mux := http.NewServeMux()
    mux.HandleFunc("/", handler_main)
    mux.HandleFunc("/thumb/", handler_thumb)
    mux.HandleFunc("/img/", handler_image)
    mux.HandleFunc("/banner/", handler_banner)

    server := &http.Server{
        Addr:           s.Address,
        Handler:        mux,
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }

    server.ListenAndServe()
}

// Returns a list of folders that have screenshot directories
func discover() (map[string][]string, error) {
    loadSettings()

    dir, err := filepath.Glob(filepath.Join(s.RemoteDirectory, "*"))
    if err != nil {
        return nil, fmt.Errorf("Error Globbing: %s", err)
    }

    found := map[string][]string{}

    for _, d := range dir {
        if strings.HasPrefix(filepath.Base(d), ".") {
            continue
        }

        dfound := []string{}
        jpg, err := filepath.Glob(filepath.Join(d, "screenshots", "*.jpg"))
        if err == nil {
            dfound = append(dfound, jpg...)
        }

        png, err := filepath.Glob(filepath.Join(d, "screenshots", "*.png"))
        if err == nil {
            dfound = append(dfound, png...)
        }

        if len(dfound) > 0 {
            found[filepath.Base(d)] = dfound
        }
    }

    return found, nil
}

func SliceContains(s []string, val string) bool {
    for _, v := range s {
        if v == val {
            return true
        }
    }
    return false
}

func GetKeys(m map[string][]string) []string {
    keys := []string{}
    for k, _ := range m {
        keys = append(keys, k)
    }

    return keys
}

func loadSettings() error {
    settingsFilename := "settings.json"
    if len(os.Args) > 1 {
        settingsFilename = os.Args[1]
    }

    settingsFile, err := ioutil.ReadFile(settingsFilename)
    if err != nil {
        return fmt.Errorf("Error reading settings file: %s", err)
    }

    err = json.Unmarshal(settingsFile, &s)
    if err != nil {
        return fmt.Errorf("Error unmarshaling settings: %s", err)
    }

    return loadGames()
}

func loadGames() error {
    gamesFile, err := ioutil.ReadFile("games.json")
    if err != nil {
        return fmt.Errorf("Error reading games file: %s", err)
    }

    err = json.Unmarshal(gamesFile, &games)
    if err != nil {
        return fmt.Errorf("Error unmarshaling games: %s", err)
    }

    return nil
}

func getGameName(appid string) (string, error) {
    if appid == ".stfolder" {
        return appid, nil
    }
    if name, ok := games[appid]; ok {
        return name, nil
    }

    fmt.Println("[getGameName] Unable to find name for appid: ", appid)
    return appid, nil

    //resp, err := http.Get("https://steamdb.info/app/" + appid)
    //if err != nil {
    //    return appid, fmt.Errorf("Unable to get appid from steamdb: %s", err)
    //}

    //page, err := ioutil.ReadAll(resp.Body)
    //if err != nil {
    //    return appid, fmt.Errorf("Unable to read steamdb response: %s", err)
    //}

    //match := re_gamename.FindSubmatch(page)
    //if len(match) != 2 {
    //    return appid, fmt.Errorf("Unable to find game name")
    //}

    //name := html.UnescapeString(string(match[1]))
    //unc, err := strconv.Unquote(name)
    //if err == nil {
    //    name = unc
    //}
    //games[appid] = name
    //fmt.Printf("Loaded new appid: [%s] %q\n", appid, name)

    //marshaled, err := json.MarshalIndent(games, "", "  ")
    //if err != nil {
    //    return name, fmt.Errorf("Unable to marshal game")
    //}

    //err = ioutil.WriteFile("games.json", marshaled, 0777)
    //if err != nil {
    //    return name, fmt.Errorf("Unable to save games.json: %s", err)
    //}

    //return name, nil
}

// Returns a filename
func getGameBanner(appid uint64) (string, error) {
    appstr := fmt.Sprintf("%d", appid)
    if exist, _ := exists("banners/" + appstr + ".jpg"); exist {
        return "banners/" + appstr + ".jpg", nil
    }

    resp, err := http.Get("http://cdn.akamai.steamstatic.com/steam/apps/" + appstr + "/header.jpg")
    if err != nil {
        return "", fmt.Errorf("Unable to DL header: %s", err)
    }

    if resp.StatusCode >= 400 && resp.StatusCode < 500 {
        // Game not found.  Use unknown.

        raw, err := ioutil.ReadFile("banners/unknown.jpg")
        if err != nil {
            return "", fmt.Errorf("Unable to read unknown.jpg")
        }

        if err = ioutil.WriteFile("banners/" + appstr + ".jpg", raw, 0777); err != nil {
            return "", fmt.Errorf("Unable to save file: %s", err)
        }

        return "banners/" + appstr + ".jpg", nil
    }

    defer resp.Body.Close()

    file, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return "", fmt.Errorf("Unable to read file: %s", err)
    }

    if err = ioutil.WriteFile("banners/" + appstr + ".jpg", file, 0777); err != nil {
        return "", fmt.Errorf("Unable to save file: %s", err)
    }

    return "banners/" + appstr + ".jpg", nil
}

// exists returns whether the given file or directory exists or not.
// Taken from https://stackoverflow.com/a/10510783
func exists(path string) (bool, error) {
    _, err := os.Stat(path)
    if err == nil { return true, nil }
    if os.IsNotExist(err) { return false, nil }
    return true, err
}
<commit_msg>Get game names from steam's servers<commit_after>package main

import (
    "encoding/json"
    "fmt"
    //"html"
    "io/ioutil"
    "net/http"
    "os"
    "path/filepath"
    "regexp"
    //"strconv"
    "strings"
    "time"
)

type Settings struct {
    RemoteDirectory string
    Address         string
}

type GameIDs map[string]string
var games GameIDs

var s Settings
var re_gamename = regexp.MustCompile(`<td itemprop="name">(.+?)</td>`)

// Structure of json from steam's servers
type steamapps struct {
    Applist struct {
        Apps    []struct {
            Appid   uint64  `json:"appid"`
            Name    string  `json:"name"`
        }   `json:"apps"`
    }   `json:"applist"`
}

func main() {
    loadSettings()

    if err := init_templates(); err != nil {
        fmt.Printf("Error loading templates: %s\n", err)
        return
    }

    mux := http.NewServeMux()
    mux.HandleFunc("/", handler_main)
    mux.HandleFunc("/thumb/", handler_thumb)
    mux.HandleFunc("/img/", handler_image)
    mux.HandleFunc("/banner/", handler_banner)

    server := &http.Server{
        Addr:           s.Address,
        Handler:        mux,
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }

    server.ListenAndServe()
}

// Returns a list of folders that have screenshot directories
func discover() (map[string][]string, error) {
    loadSettings()

    dir, err := filepath.Glob(filepath.Join(s.RemoteDirectory, "*"))
    if err != nil {
        return nil, fmt.Errorf("Error Globbing: %s", err)
    }

    found := map[string][]string{}

    for _, d := range dir {
        if strings.HasPrefix(filepath.Base(d), ".") {
            continue
        }

        dfound := []string{}
        jpg, err := filepath.Glob(filepath.Join(d, "screenshots", "*.jpg"))
        if err == nil {
            dfound = append(dfound, jpg...)
        }

        png, err := filepath.Glob(filepath.Join(d, "screenshots", "*.png"))
        if err == nil {
            dfound = append(dfound, png...)
        }

        if len(dfound) > 0 {
            found[filepath.Base(d)] = dfound
        }
    }

    return found, nil
}

func SliceContains(s []string, val string) bool {
    for _, v := range s {
        if v == val {
            return true
        }
    }
    return false
}

func GetKeys(m map[string][]string) []string {
    keys := []string{}
    for k, _ := range m {
        keys = append(keys, k)
    }

    return keys
}

func loadSettings() error {
    settingsFilename := "settings.json"
    if len(os.Args) > 1 {
        settingsFilename = os.Args[1]
    }

    settingsFile, err := ioutil.ReadFile(settingsFilename)
    if err != nil {
        return fmt.Errorf("Error reading settings file: %s", err)
    }

    err = json.Unmarshal(settingsFile, &s)
    if err != nil {
        return fmt.Errorf("Error unmarshaling settings: %s", err)
    }

    return loadGames()
}

func loadGames() error {
    gamesFile, err := ioutil.ReadFile("games.json")
    if err != nil {
        return fmt.Errorf("Error reading games file: %s", err)
    }

    err = json.Unmarshal(gamesFile, &games)
    if err != nil {
        return fmt.Errorf("Error unmarshaling games: %s", err)
    }

    return nil
}

func getGameName(appid string) (string, error) {
    if appid == ".stfolder" {
        return appid, nil
    }

    //fmt.Printf("Getting name for appid %q\n", appid)
    if name, ok := games[appid]; ok {
        return name, nil
    }

    // Large appid, must be a non-steam game.  This could have some edge cases
    // as non-steam games' appids are CRCs.
    if len(appid) > 18 {
        games[appid] = fmt.Sprintf("Non-Steam game (%s)", appid)
        return games[appid], nil
    }

    // TODO: rate limiting/cache age
    updateGamesJson(appid)
    if name, ok := games[appid]; ok {
        return name, nil
    }
    return appid, nil
}

// Update the local cache of appids from steam's servers.
func updateGamesJson(appid string) error {
    fmt.Printf("Updating games list; looking for %q\n", appid)
    resp, err := http.Get("http://api.steampowered.com/ISteamApps/GetAppList/v2")
    if err != nil {
        return fmt.Errorf("Unable to get appid list from steam: %s", err)
    }
    defer resp.Body.Close()

    js, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return fmt.Errorf("Unable to read appid json: %s", err)
    }

    alist := &steamapps{}
    if err := json.Unmarshal(js, alist); err != nil {
        return fmt.Errorf("Unable to unmarshal json: %s", err)
    }

    for _, a := range alist.Applist.Apps {
        id := fmt.Sprintf("%d", a.Appid)
        if _, ok := games[id]; ok {
            if games[id] == id {
                games[id] = a.Name
            }
        } else {
            games[id] = a.Name
        }

        if id == appid {
            fmt.Printf("Found game for appid %s: %q\n", appid, a.Name)
        }
    }

    // save games.json
    marshaled, err := json.MarshalIndent(games, "", "  ")
    if err != nil {
        return fmt.Errorf("Unable to marshal game json: %s", err)
    }

    err = ioutil.WriteFile("games.json", marshaled, 0777)
    if err != nil {
        return fmt.Errorf("Unable to save games.json: %s", err)
    }

    fmt.Printf("Finished updating games list.  Appids: %d\n", len(games))
    return nil
}

// Returns a filename
func getGameBanner(appid uint64) (string, error) {
    appstr := fmt.Sprintf("%d", appid)
    if exist, _ := exists("banners/" + appstr + ".jpg"); exist {
        return "banners/" + appstr + ".jpg", nil
    }

    resp, err := http.Get("http://cdn.akamai.steamstatic.com/steam/apps/" + appstr + "/header.jpg")
    if err != nil {
        return "", fmt.Errorf("Unable to DL header: %s", err)
    }

    if resp.StatusCode >= 400 && resp.StatusCode < 500 {
        // Game not found.  Use unknown.

        raw, err := ioutil.ReadFile("banners/unknown.jpg")
        if err != nil {
            return "", fmt.Errorf("Unable to read unknown.jpg")
        }

        if err = ioutil.WriteFile("banners/" + appstr + ".jpg", raw, 0777); err != nil {
            return "", fmt.Errorf("Unable to save file: %s", err)
        }

        return "banners/" + appstr + ".jpg", nil
    }

    defer resp.Body.Close()

    file, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return "", fmt.Errorf("Unable to read file: %s", err)
    }

    if err = ioutil.WriteFile("banners/" + appstr + ".jpg", file, 0777); err != nil {
        return "", fmt.Errorf("Unable to save file: %s", err)
    }

    return "banners/" + appstr + ".jpg", nil
}

// exists returns whether the given file or directory exists or not.
// Taken from https://stackoverflow.com/a/10510783
func exists(path string) (bool, error) {
    _, err := os.Stat(path)
    if err == nil { return true, nil }
    if os.IsNotExist(err) { return false, nil }
    return true, err
}
<|endoftext|>