<commit_before>package berlingo

import (
	"encoding/json"
)

type ResponseMove struct {
	From     int `json:"from"`
	To       int `json:"to"`
	Soldiers int `json:"number_of_soldiers"`
}
type Response struct {
	Request *Request
	Moves   []*ResponseMove
}

func NewResponse() (*Response, error) {
	return &Response{
		Moves: make([]*ResponseMove, 0, 10),
	}, nil
}

func (response *Response) ToJson() (response_json []byte, err error) {
	return json.Marshal(response.Moves)
}

func (response *Response) AddMove(from_node *Node, to_node *Node, num_soldiers int) {
	response.Moves = append(response.Moves, &ResponseMove{from_node.Id, to_node.Id, num_soldiers})
}
<commit_msg>Remove spurious struct member<commit_after>package berlingo

import (
	"encoding/json"
)

type ResponseMove struct {
	From     int `json:"from"`
	To       int `json:"to"`
	Soldiers int `json:"number_of_soldiers"`
}
type Response struct {
	Moves   []*ResponseMove
}

func NewResponse() (*Response, error) {
	return &Response{
		Moves: make([]*ResponseMove, 0, 10),
	}, nil
}

func (response *Response) ToJson() (response_json []byte, err error) {
	return json.Marshal(response.Moves)
}

func (response *Response) AddMove(from_node *Node, to_node *Node, num_soldiers int) {
	response.Moves = append(response.Moves, &ResponseMove{from_node.Id, to_node.Id, num_soldiers})
}
<|endoftext|>


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

package ir

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/types"
	"cmd/internal/src"
)

// A Decl is a declaration of a const, type, or var. (A declared func is a Func.)
type Decl struct {
	miniNode
	X Node // the thing being declared
}

func NewDecl(pos src.XPos, op Op, x Node) *Decl {
	n := &Decl{X: x}
	n.pos = pos
	switch op {
	default:
		panic("invalid Decl op " + op.String())
	case ODCL, ODCLCONST, ODCLTYPE:
		n.op = op
	}
	return n
}

func (*Decl) isStmt() {}

// A Stmt is a Node that can appear as a statement.
// This includes statement-like expressions such as f().
//
// (It's possible it should include <-c, but that would require
// splitting ORECV out of UnaryExpr, which hasn't yet been
// necessary. Maybe instead we will introduce ExprStmt at
// some point.)
type Stmt interface {
	Node
	isStmt()
}

// A miniStmt is a miniNode with extra fields common to statements.
type miniStmt struct {
	miniNode
	init Nodes
}

func (*miniStmt) isStmt() {}

func (n *miniStmt) Init() Nodes       { return n.init }
func (n *miniStmt) SetInit(x Nodes)   { n.init = x }
func (n *miniStmt) PtrInit() *Nodes   { return &n.init }
func (n *miniStmt) HasCall() bool     { return n.bits&miniHasCall != 0 }
func (n *miniStmt) SetHasCall(b bool) { n.bits.set(miniHasCall, b) }

// An AssignListStmt is an assignment statement with
// more than one item on at least one side: Lhs = Rhs.
// If Def is true, the assignment is a :=.
type AssignListStmt struct {
	miniStmt
	Lhs Nodes
	Def bool
	Rhs Nodes
}

func NewAssignListStmt(pos src.XPos, op Op, lhs, rhs []Node) *AssignListStmt {
	n := &AssignListStmt{}
	n.pos = pos
	n.SetOp(op)
	n.Lhs.Set(lhs)
	n.Rhs.Set(rhs)
	return n
}

func (n *AssignListStmt) SetOp(op Op) {
	switch op {
	default:
		panic(n.no("SetOp " + op.String()))
	case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV, OSELRECV2:
		n.op = op
	}
}

// An AssignStmt is a simple assignment statement: X = Y.
// If Def is true, the assignment is a :=.
type AssignStmt struct {
	miniStmt
	X   Node
	Def bool
	Y   Node
}

func NewAssignStmt(pos src.XPos, x, y Node) *AssignStmt {
	n := &AssignStmt{X: x, Y: y}
	n.pos = pos
	n.op = OAS
	return n
}

func (n *AssignStmt) SetOp(op Op) {
	switch op {
	default:
		panic(n.no("SetOp " + op.String()))
	case OAS:
		n.op = op
	}
}

// An AssignOpStmt is an AsOp= assignment statement: X AsOp= Y.
type AssignOpStmt struct {
	miniStmt
	typ    *types.Type
	X      Node
	AsOp   Op // OADD etc
	Y      Node
	IncDec bool // actually ++ or --
}

func NewAssignOpStmt(pos src.XPos, asOp Op, x, y Node) *AssignOpStmt {
	n := &AssignOpStmt{AsOp: asOp, X: x, Y: y}
	n.pos = pos
	n.op = OASOP
	return n
}

func (n *AssignOpStmt) Type() *types.Type     { return n.typ }
func (n *AssignOpStmt) SetType(x *types.Type) { n.typ = x }

// A BlockStmt is a block: { List }.
type BlockStmt struct {
	miniStmt
	List Nodes
}

func NewBlockStmt(pos src.XPos, list []Node) *BlockStmt {
	n := &BlockStmt{}
	n.pos = pos
	if !pos.IsKnown() {
		n.pos = base.Pos
		if len(list) > 0 {
			n.pos = list[0].Pos()
		}
	}
	n.op = OBLOCK
	n.List.Set(list)
	return n
}

// A BranchStmt is a break, continue, fallthrough, or goto statement.
//
// For back-end code generation, Op may also be RETJMP (return+jump),
// in which case the label names another function entirely.
type BranchStmt struct {
	miniStmt
	Label *types.Sym // label if present
}

func NewBranchStmt(pos src.XPos, op Op, label *types.Sym) *BranchStmt {
	switch op {
	case OBREAK, OCONTINUE, OFALL, OGOTO, ORETJMP:
		// ok
	default:
		panic("NewBranch " + op.String())
	}
	n := &BranchStmt{Label: label}
	n.pos = pos
	n.op = op
	return n
}

func (n *BranchStmt) Sym() *types.Sym { return n.Label }

// A CaseClause is a case statement in a switch or select: case List: Body.
type CaseClause struct {
	miniStmt
	Var  Node  // declared variable for this case in type switch
	List Nodes // list of expressions for switch, early select
	Body Nodes
}

func NewCaseStmt(pos src.XPos, list, body []Node) *CaseClause {
	n := &CaseClause{List: list, Body: body}
	n.pos = pos
	n.op = OCASE
	return n
}

// TODO(mdempsky): Generate these with mknode.go.
func copyCases(list []*CaseClause) []*CaseClause {
	if list == nil {
		return nil
	}
	c := make([]*CaseClause, len(list))
	copy(c, list)
	return c
}
func maybeDoCases(list []*CaseClause, err error, do func(Node) error) error {
	if err != nil {
		return err
	}
	for _, x := range list {
		if x != nil {
			if err := do(x); err != nil {
				return err
			}
		}
	}
	return nil
}
func editCases(list []*CaseClause, edit func(Node) Node) {
	for i, x := range list {
		if x != nil {
			list[i] = edit(x).(*CaseClause)
		}
	}
}

type CommClause struct {
	miniStmt
	Comm Node // communication case
	Body Nodes
}

func NewCommStmt(pos src.XPos, comm Node, body []Node) *CommClause {
	n := &CommClause{Comm: comm, Body: body}
	n.pos = pos
	n.op = OCASE
	return n
}

// TODO(mdempsky): Generate these with mknode.go.
func copyComms(list []*CommClause) []*CommClause {
	if list == nil {
		return nil
	}
	c := make([]*CommClause, len(list))
	copy(c, list)
	return c
}
func maybeDoComms(list []*CommClause, err error, do func(Node) error) error {
	if err != nil {
		return err
	}
	for _, x := range list {
		if x != nil {
			if err := do(x); err != nil {
				return err
			}
		}
	}
	return nil
}
func editComms(list []*CommClause, edit func(Node) Node) {
	for i, x := range list {
		if x != nil {
			list[i] = edit(x).(*CommClause)
		}
	}
}

// A ForStmt is a non-range for loop: for Init; Cond; Post { Body }
// Op can be OFOR or OFORUNTIL (!Cond).
type ForStmt struct {
	miniStmt
	Label    *types.Sym
	Cond     Node
	Late     Nodes
	Post     Node
	Body     Nodes
	HasBreak bool
}

func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node) *ForStmt {
	n := &ForStmt{Cond: cond, Post: post}
	n.pos = pos
	n.op = OFOR
	if init != nil {
		n.init = []Node{init}
	}
	n.Body.Set(body)
	return n
}

func (n *ForStmt) SetOp(op Op) {
	if op != OFOR && op != OFORUNTIL {
		panic(n.no("SetOp " + op.String()))
	}
	n.op = op
}

// A GoDeferStmt is a go or defer statement: go Call / defer Call.
//
// The two opcodes use a signle syntax because the implementations
// are very similar: both are concerned with saving Call and running it
// in a different context (a separate goroutine or a later time).
type GoDeferStmt struct {
	miniStmt
	Call Node
}

func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt {
	n := &GoDeferStmt{Call: call}
	n.pos = pos
	switch op {
	case ODEFER, OGO:
		n.op = op
	default:
		panic("NewGoDeferStmt " + op.String())
	}
	return n
}

// A IfStmt is a return statement: if Init; Cond { Then } else { Else }.
type IfStmt struct {
	miniStmt
	Cond   Node
	Body   Nodes
	Else   Nodes
	Likely bool // code layout hint
}

func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt {
	n := &IfStmt{Cond: cond}
	n.pos = pos
	n.op = OIF
	n.Body.Set(body)
	n.Else.Set(els)
	return n
}

// An InlineMarkStmt is a marker placed just before an inlined body.
type InlineMarkStmt struct {
	miniStmt
	Index int64
}

func NewInlineMarkStmt(pos src.XPos, index int64) *InlineMarkStmt {
	n := &InlineMarkStmt{Index: index}
	n.pos = pos
	n.op = OINLMARK
	return n
}

func (n *InlineMarkStmt) Offset() int64     { return n.Index }
func (n *InlineMarkStmt) SetOffset(x int64) { n.Index = x }

// A LabelStmt is a label statement (just the label, not including the statement it labels).
type LabelStmt struct {
	miniStmt
	Label *types.Sym // "Label:"
}

func NewLabelStmt(pos src.XPos, label *types.Sym) *LabelStmt {
	n := &LabelStmt{Label: label}
	n.pos = pos
	n.op = OLABEL
	return n
}

func (n *LabelStmt) Sym() *types.Sym { return n.Label }

// A RangeStmt is a range loop: for Key, Value = range X { Body }
type RangeStmt struct {
	miniStmt
	Label    *types.Sym
	Def      bool
	X        Node
	Key      Node
	Value    Node
	Body     Nodes
	HasBreak bool
	Prealloc *Name
}

func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt {
	n := &RangeStmt{X: x, Key: key, Value: value}
	n.pos = pos
	n.op = ORANGE
	n.Body.Set(body)
	return n
}

// A ReturnStmt is a return statement.
type ReturnStmt struct {
	miniStmt
	orig    Node  // for typecheckargs rewrite
	Results Nodes // return list
}

func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
	n := &ReturnStmt{}
	n.pos = pos
	n.op = ORETURN
	n.orig = n
	n.Results.Set(results)
	return n
}

func (n *ReturnStmt) Orig() Node     { return n.orig }
func (n *ReturnStmt) SetOrig(x Node) { n.orig = x }

// A SelectStmt is a block: { Cases }.
type SelectStmt struct {
	miniStmt
	Label    *types.Sym
	Cases    []*CommClause
	HasBreak bool

	// TODO(rsc): Instead of recording here, replace with a block?
	Compiled Nodes // compiled form, after walkswitch
}

func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt {
	n := &SelectStmt{Cases: cases}
	n.pos = pos
	n.op = OSELECT
	return n
}

// A SendStmt is a send statement: X <- Y.
type SendStmt struct {
	miniStmt
	Chan  Node
	Value Node
}

func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt {
	n := &SendStmt{Chan: ch, Value: value}
	n.pos = pos
	n.op = OSEND
	return n
}

// A SwitchStmt is a switch statement: switch Init; Expr { Cases }.
type SwitchStmt struct {
	miniStmt
	Tag      Node
	Cases    []*CaseClause
	Label    *types.Sym
	HasBreak bool

	// TODO(rsc): Instead of recording here, replace with a block?
	Compiled Nodes // compiled form, after walkswitch
}

func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt {
	n := &SwitchStmt{Tag: tag, Cases: cases}
	n.pos = pos
	n.op = OSWITCH
	return n
}

// A TypeSwitchGuard is the [Name :=] X.(type) in a type switch.
type TypeSwitchGuard struct {
	miniNode
	Tag  *Ident
	X    Node
	Used bool
}

func NewTypeSwitchGuard(pos src.XPos, tag *Ident, x Node) *TypeSwitchGuard {
	n := &TypeSwitchGuard{Tag: tag, X: x}
	n.pos = pos
	n.op = OTYPESW
	return n
}
<commit_msg>[dev.regabi] cmd/compile: remove typ from AssignOpStmt<commit_after>// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ir

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/types"
	"cmd/internal/src"
)

// A Decl is a declaration of a const, type, or var. (A declared func is a Func.)
type Decl struct {
	miniNode
	X Node // the thing being declared
}

func NewDecl(pos src.XPos, op Op, x Node) *Decl {
	n := &Decl{X: x}
	n.pos = pos
	switch op {
	default:
		panic("invalid Decl op " + op.String())
	case ODCL, ODCLCONST, ODCLTYPE:
		n.op = op
	}
	return n
}

func (*Decl) isStmt() {}

// A Stmt is a Node that can appear as a statement.
// This includes statement-like expressions such as f().
//
// (It's possible it should include <-c, but that would require
// splitting ORECV out of UnaryExpr, which hasn't yet been
// necessary. Maybe instead we will introduce ExprStmt at
// some point.)
type Stmt interface {
	Node
	isStmt()
}

// A miniStmt is a miniNode with extra fields common to statements.
type miniStmt struct {
	miniNode
	init Nodes
}

func (*miniStmt) isStmt() {}

func (n *miniStmt) Init() Nodes       { return n.init }
func (n *miniStmt) SetInit(x Nodes)   { n.init = x }
func (n *miniStmt) PtrInit() *Nodes   { return &n.init }
func (n *miniStmt) HasCall() bool     { return n.bits&miniHasCall != 0 }
func (n *miniStmt) SetHasCall(b bool) { n.bits.set(miniHasCall, b) }

// An AssignListStmt is an assignment statement with
// more than one item on at least one side: Lhs = Rhs.
// If Def is true, the assignment is a :=.
type AssignListStmt struct {
	miniStmt
	Lhs Nodes
	Def bool
	Rhs Nodes
}

func NewAssignListStmt(pos src.XPos, op Op, lhs, rhs []Node) *AssignListStmt {
	n := &AssignListStmt{}
	n.pos = pos
	n.SetOp(op)
	n.Lhs.Set(lhs)
	n.Rhs.Set(rhs)
	return n
}

func (n *AssignListStmt) SetOp(op Op) {
	switch op {
	default:
		panic(n.no("SetOp " + op.String()))
	case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV, OSELRECV2:
		n.op = op
	}
}

// An AssignStmt is a simple assignment statement: X = Y.
// If Def is true, the assignment is a :=.
type AssignStmt struct {
	miniStmt
	X   Node
	Def bool
	Y   Node
}

func NewAssignStmt(pos src.XPos, x, y Node) *AssignStmt {
	n := &AssignStmt{X: x, Y: y}
	n.pos = pos
	n.op = OAS
	return n
}

func (n *AssignStmt) SetOp(op Op) {
	switch op {
	default:
		panic(n.no("SetOp " + op.String()))
	case OAS:
		n.op = op
	}
}

// An AssignOpStmt is an AsOp= assignment statement: X AsOp= Y.
type AssignOpStmt struct {
	miniStmt
	X      Node
	AsOp   Op // OADD etc
	Y      Node
	IncDec bool // actually ++ or --
}

func NewAssignOpStmt(pos src.XPos, asOp Op, x, y Node) *AssignOpStmt {
	n := &AssignOpStmt{AsOp: asOp, X: x, Y: y}
	n.pos = pos
	n.op = OASOP
	return n
}

// A BlockStmt is a block: { List }.
type BlockStmt struct {
	miniStmt
	List Nodes
}

func NewBlockStmt(pos src.XPos, list []Node) *BlockStmt {
	n := &BlockStmt{}
	n.pos = pos
	if !pos.IsKnown() {
		n.pos = base.Pos
		if len(list) > 0 {
			n.pos = list[0].Pos()
		}
	}
	n.op = OBLOCK
	n.List.Set(list)
	return n
}

// A BranchStmt is a break, continue, fallthrough, or goto statement.
//
// For back-end code generation, Op may also be RETJMP (return+jump),
// in which case the label names another function entirely.
type BranchStmt struct {
	miniStmt
	Label *types.Sym // label if present
}

func NewBranchStmt(pos src.XPos, op Op, label *types.Sym) *BranchStmt {
	switch op {
	case OBREAK, OCONTINUE, OFALL, OGOTO, ORETJMP:
		// ok
	default:
		panic("NewBranch " + op.String())
	}
	n := &BranchStmt{Label: label}
	n.pos = pos
	n.op = op
	return n
}

func (n *BranchStmt) Sym() *types.Sym { return n.Label }

// A CaseClause is a case statement in a switch or select: case List: Body.
type CaseClause struct {
	miniStmt
	Var  Node  // declared variable for this case in type switch
	List Nodes // list of expressions for switch, early select
	Body Nodes
}

func NewCaseStmt(pos src.XPos, list, body []Node) *CaseClause {
	n := &CaseClause{List: list, Body: body}
	n.pos = pos
	n.op = OCASE
	return n
}

// TODO(mdempsky): Generate these with mknode.go.
func copyCases(list []*CaseClause) []*CaseClause {
	if list == nil {
		return nil
	}
	c := make([]*CaseClause, len(list))
	copy(c, list)
	return c
}
func maybeDoCases(list []*CaseClause, err error, do func(Node) error) error {
	if err != nil {
		return err
	}
	for _, x := range list {
		if x != nil {
			if err := do(x); err != nil {
				return err
			}
		}
	}
	return nil
}
func editCases(list []*CaseClause, edit func(Node) Node) {
	for i, x := range list {
		if x != nil {
			list[i] = edit(x).(*CaseClause)
		}
	}
}

type CommClause struct {
	miniStmt
	Comm Node // communication case
	Body Nodes
}

func NewCommStmt(pos src.XPos, comm Node, body []Node) *CommClause {
	n := &CommClause{Comm: comm, Body: body}
	n.pos = pos
	n.op = OCASE
	return n
}

// TODO(mdempsky): Generate these with mknode.go.
func copyComms(list []*CommClause) []*CommClause {
	if list == nil {
		return nil
	}
	c := make([]*CommClause, len(list))
	copy(c, list)
	return c
}
func maybeDoComms(list []*CommClause, err error, do func(Node) error) error {
	if err != nil {
		return err
	}
	for _, x := range list {
		if x != nil {
			if err := do(x); err != nil {
				return err
			}
		}
	}
	return nil
}
func editComms(list []*CommClause, edit func(Node) Node) {
	for i, x := range list {
		if x != nil {
			list[i] = edit(x).(*CommClause)
		}
	}
}

// A ForStmt is a non-range for loop: for Init; Cond; Post { Body }
// Op can be OFOR or OFORUNTIL (!Cond).
type ForStmt struct {
	miniStmt
	Label    *types.Sym
	Cond     Node
	Late     Nodes
	Post     Node
	Body     Nodes
	HasBreak bool
}

func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node) *ForStmt {
	n := &ForStmt{Cond: cond, Post: post}
	n.pos = pos
	n.op = OFOR
	if init != nil {
		n.init = []Node{init}
	}
	n.Body.Set(body)
	return n
}

func (n *ForStmt) SetOp(op Op) {
	if op != OFOR && op != OFORUNTIL {
		panic(n.no("SetOp " + op.String()))
	}
	n.op = op
}

// A GoDeferStmt is a go or defer statement: go Call / defer Call.
//
// The two opcodes use a signle syntax because the implementations
// are very similar: both are concerned with saving Call and running it
// in a different context (a separate goroutine or a later time).
type GoDeferStmt struct {
	miniStmt
	Call Node
}

func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt {
	n := &GoDeferStmt{Call: call}
	n.pos = pos
	switch op {
	case ODEFER, OGO:
		n.op = op
	default:
		panic("NewGoDeferStmt " + op.String())
	}
	return n
}

// A IfStmt is a return statement: if Init; Cond { Then } else { Else }.
type IfStmt struct {
	miniStmt
	Cond   Node
	Body   Nodes
	Else   Nodes
	Likely bool // code layout hint
}

func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt {
	n := &IfStmt{Cond: cond}
	n.pos = pos
	n.op = OIF
	n.Body.Set(body)
	n.Else.Set(els)
	return n
}

// An InlineMarkStmt is a marker placed just before an inlined body.
type InlineMarkStmt struct {
	miniStmt
	Index int64
}

func NewInlineMarkStmt(pos src.XPos, index int64) *InlineMarkStmt {
	n := &InlineMarkStmt{Index: index}
	n.pos = pos
	n.op = OINLMARK
	return n
}

func (n *InlineMarkStmt) Offset() int64     { return n.Index }
func (n *InlineMarkStmt) SetOffset(x int64) { n.Index = x }

// A LabelStmt is a label statement (just the label, not including the statement it labels).
type LabelStmt struct {
	miniStmt
	Label *types.Sym // "Label:"
}

func NewLabelStmt(pos src.XPos, label *types.Sym) *LabelStmt {
	n := &LabelStmt{Label: label}
	n.pos = pos
	n.op = OLABEL
	return n
}

func (n *LabelStmt) Sym() *types.Sym { return n.Label }

// A RangeStmt is a range loop: for Key, Value = range X { Body }
type RangeStmt struct {
	miniStmt
	Label    *types.Sym
	Def      bool
	X        Node
	Key      Node
	Value    Node
	Body     Nodes
	HasBreak bool
	Prealloc *Name
}

func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt {
	n := &RangeStmt{X: x, Key: key, Value: value}
	n.pos = pos
	n.op = ORANGE
	n.Body.Set(body)
	return n
}

// A ReturnStmt is a return statement.
type ReturnStmt struct {
	miniStmt
	orig    Node  // for typecheckargs rewrite
	Results Nodes // return list
}

func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
	n := &ReturnStmt{}
	n.pos = pos
	n.op = ORETURN
	n.orig = n
	n.Results.Set(results)
	return n
}

func (n *ReturnStmt) Orig() Node     { return n.orig }
func (n *ReturnStmt) SetOrig(x Node) { n.orig = x }

// A SelectStmt is a block: { Cases }.
type SelectStmt struct {
	miniStmt
	Label    *types.Sym
	Cases    []*CommClause
	HasBreak bool

	// TODO(rsc): Instead of recording here, replace with a block?
	Compiled Nodes // compiled form, after walkswitch
}

func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt {
	n := &SelectStmt{Cases: cases}
	n.pos = pos
	n.op = OSELECT
	return n
}

// A SendStmt is a send statement: X <- Y.
type SendStmt struct {
	miniStmt
	Chan  Node
	Value Node
}

func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt {
	n := &SendStmt{Chan: ch, Value: value}
	n.pos = pos
	n.op = OSEND
	return n
}

// A SwitchStmt is a switch statement: switch Init; Expr { Cases }.
type SwitchStmt struct {
	miniStmt
	Tag      Node
	Cases    []*CaseClause
	Label    *types.Sym
	HasBreak bool

	// TODO(rsc): Instead of recording here, replace with a block?
	Compiled Nodes // compiled form, after walkswitch
}

func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt {
	n := &SwitchStmt{Tag: tag, Cases: cases}
	n.pos = pos
	n.op = OSWITCH
	return n
}

// A TypeSwitchGuard is the [Name :=] X.(type) in a type switch.
type TypeSwitchGuard struct {
	miniNode
	Tag  *Ident
	X    Node
	Used bool
}

func NewTypeSwitchGuard(pos src.XPos, tag *Ident, x Node) *TypeSwitchGuard {
	n := &TypeSwitchGuard{Tag: tag, X: x}
	n.pos = pos
	n.op = OTYPESW
	return n
}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru 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 docker

import (
	"encoding/json"
	"github.com/tsuru/tsuru/api"
	"github.com/tsuru/tsuru/auth"
	"io"
	"io/ioutil"
	"net/http"
)

func init() {
	api.RegisterHandler("/node", "GET", api.AdminRequiredHandler(listNodeHandler))
	api.RegisterHandler("/node/apps/:appname/containers", "GET", api.AdminRequiredHandler(listContainersHandler))
	api.RegisterHandler("/node/:address/containers", "GET", api.AdminRequiredHandler(listContainersHandler))
	api.RegisterAdminHandler("/node/add", "POST", api.Handler(addNodeHandler))
	api.RegisterAdminHandler("/node/remove", "DELETE", api.Handler(removeNodeHandler))
}

// addNodeHandler calls scheduler.Register to registering a node into it.
func addNodeHandler(w http.ResponseWriter, r *http.Request) error {
	params, err := unmarshal(r.Body)
	if err != nil {
		return err
	}
	return dockerCluster().Register(params)
}

// removeNodeHandler calls scheduler.Unregister to unregistering a node into it.
func removeNodeHandler(w http.ResponseWriter, r *http.Request) error {
	params, err := unmarshal(r.Body)
	if err != nil {
		return err
	}
	return dockerCluster().Unregister(params)
}

//listNodeHandler call scheduler.Nodes to list all nodes into it.
func listNodeHandler(w http.ResponseWriter, r *http.Request, t *auth.Token) error {
	nodeList, err := dockerCluster().Nodes()
	if err != nil {
		return err
	}
	return json.NewEncoder(w).Encode(nodeList)
}

//listContainersHandler call scheduler.Containers to list all containers into it.
func listContainersHandler(w http.ResponseWriter, r *http.Request, t *auth.Token) error {
	address := r.URL.Query().Get(":address")
	if address != "" {
	containerList, err := ListContainers(address)
	if err != nil {
		return err
	}
	return json.NewEncoder(w).Encode(containerList)
	}
	app := r.URL.Query().Get(":appname")
	containerList, err := ListContainersByApp(app)
	if err != nil {
		return err
	}
	return json.NewEncoder(w).Encode(containerList)
}

func unmarshal(body io.ReadCloser) (map[string]string, error) {
	b, err := ioutil.ReadAll(body)
	if err != nil {
		return nil, err
	}
	params := map[string]string{}
	err = json.Unmarshal(b, &params)
	if err != nil {
		return nil, err
	}
	return params, nil
}
<commit_msg>go fmt<commit_after>// Copyright 2014 tsuru 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 docker

import (
	"encoding/json"
	"github.com/tsuru/tsuru/api"
	"github.com/tsuru/tsuru/auth"
	"io"
	"io/ioutil"
	"net/http"
)

func init() {
	api.RegisterHandler("/node", "GET", api.AdminRequiredHandler(listNodeHandler))
	api.RegisterHandler("/node/apps/:appname/containers", "GET", api.AdminRequiredHandler(listContainersHandler))
	api.RegisterHandler("/node/:address/containers", "GET", api.AdminRequiredHandler(listContainersHandler))
	api.RegisterAdminHandler("/node/add", "POST", api.Handler(addNodeHandler))
	api.RegisterAdminHandler("/node/remove", "DELETE", api.Handler(removeNodeHandler))
}

// addNodeHandler calls scheduler.Register to registering a node into it.
func addNodeHandler(w http.ResponseWriter, r *http.Request) error {
	params, err := unmarshal(r.Body)
	if err != nil {
		return err
	}
	return dockerCluster().Register(params)
}

// removeNodeHandler calls scheduler.Unregister to unregistering a node into it.
func removeNodeHandler(w http.ResponseWriter, r *http.Request) error {
	params, err := unmarshal(r.Body)
	if err != nil {
		return err
	}
	return dockerCluster().Unregister(params)
}

//listNodeHandler call scheduler.Nodes to list all nodes into it.
func listNodeHandler(w http.ResponseWriter, r *http.Request, t *auth.Token) error {
	nodeList, err := dockerCluster().Nodes()
	if err != nil {
		return err
	}
	return json.NewEncoder(w).Encode(nodeList)
}

//listContainersHandler call scheduler.Containers to list all containers into it.
func listContainersHandler(w http.ResponseWriter, r *http.Request, t *auth.Token) error {
	address := r.URL.Query().Get(":address")
	if address != "" {
		containerList, err := ListContainers(address)
		if err != nil {
			return err
		}
		return json.NewEncoder(w).Encode(containerList)
	}
	app := r.URL.Query().Get(":appname")
	containerList, err := ListContainersByApp(app)
	if err != nil {
		return err
	}
	return json.NewEncoder(w).Encode(containerList)
}

func unmarshal(body io.ReadCloser) (map[string]string, error) {
	b, err := ioutil.ReadAll(body)
	if err != nil {
		return nil, err
	}
	params := map[string]string{}
	err = json.Unmarshal(b, &params)
	if err != nil {
		return nil, err
	}
	return params, nil
}
<|endoftext|>


<commit_before>/*
  Copyright (C) 2016 H2O.ai, Inc. <http://h2o.ai/>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as
  published by the Free Software Foundation, either version 3 of the
  License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package ldap

import (
	"fmt"
	"path/filepath"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/go-ldap/ldap"
	"github.com/pkg/errors"
)

type Ldap struct {
	Address  string
	BindDN   string
	BindPass string `toml:"bindPassword"`

	UserBaseDn      string
	UserIdAttribute string
	UserObjectClass string

	// TODO implement TLS case
	isTLS     bool
	ForceBind bool

	// Users who are logged in
	Users *LdapUser
}

func (l *Ldap) CheckBind(user, password string) error {
	// Make connection to LDAP with read-only user
	conn, err := ldap.Dial("tcp", l.Address)
	if err != nil {
		return errors.Wrap(err, "dialing ldap")
	}
	defer conn.Close()
	if err := conn.Bind(l.BindDN, l.BindPass); err != nil {
		return errors.Wrap(err, "read user binding to ldap")
	}

	// Search request for userDN
	req := ldap.NewSearchRequest(
		l.UserBaseDn,
		ldap.ScopeWholeSubtree,
		ldap.NeverDerefAliases,
		0,
		0,
		false,
		fmt.Sprintf("(&(objectClass=%s)(%s=%s))",
			l.UserObjectClass, l.UserIdAttribute, user),
		nil,
		nil,
	)
	res, err := conn.Search(req)
	if err != nil {
		return errors.Wrap(err, "searching ldap")
	}
	if len(res.Entries) < 1 {
		return fmt.Errorf("user %s does not exist", user)
	} else if len(res.Entries) > 1 {
		return fmt.Errorf("too many user entries")
	}
	userDn := res.Entries[0].DN

	// Verify user Bind
	return errors.Wrapf(conn.Bind(userDn, password), "user %s binding to ldap", user)
}

func NewLdap(
	address, bindDn, bindPass string,
	userBaseDn, userIdAttribute, userObjectClass string,
	idleTime, maxTime time.Duration) *Ldap {
	return &Ldap{
		Address: address, BindDN: bindDn, BindPass: bindPass,

		UserBaseDn: userBaseDn, UserIdAttribute: userIdAttribute, UserObjectClass: userObjectClass,

		Users: NewLdapUser(idleTime, maxTime),
	}
}

func FromConfig(fileName string) (*Ldap, error) {
	A := struct {
		Hostname        string
		Port            int
		BindDn          string
		BindPassword    string
		UserBaseDn      string
		UserIdAttribute string
		UserObjectClass string

		IsTLS     bool `toml:"useLdaps"`
		ForceBind bool
		IdleTime  time.Duration
		MaxTime   time.Duration
	}{}

	f, err := filepath.Abs(fileName)
	if err != nil {
		return nil, errors.Wrap(err, "getting absolute path")
	}
	if _, err := toml.DecodeFile(f, &A); err != nil {
		return nil, errors.Wrap(err, "decoding config file")
	}

	return NewLdap(
		fmt.Sprintf("%s:%d", A.Hostname, A.Port),
		A.BindDn, A.BindPassword,
		A.UserBaseDn, A.UserIdAttribute, A.UserObjectClass,

		time.Minute*A.IdleTime, time.Minute*A.MaxTime), nil
}
<commit_msg>STEAM-496: Add force bind to LDAP config<commit_after>/*
  Copyright (C) 2016 H2O.ai, Inc. <http://h2o.ai/>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as
  published by the Free Software Foundation, either version 3 of the
  License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package ldap

import (
	"fmt"
	"path/filepath"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/go-ldap/ldap"
	"github.com/pkg/errors"
)

type Ldap struct {
	Address  string
	BindDN   string
	BindPass string `toml:"bindPassword"`

	UserBaseDn      string
	UserIdAttribute string
	UserObjectClass string

	// TODO implement TLS case
	isTLS     bool
	ForceBind bool

	// Users who are logged in
	Users *LdapUser
}

func (l *Ldap) CheckBind(user, password string) error {
	// Make connection to LDAP with read-only user
	conn, err := ldap.Dial("tcp", l.Address)
	if err != nil {
		return errors.Wrap(err, "dialing ldap")
	}
	defer conn.Close()
	if err := conn.Bind(l.BindDN, l.BindPass); err != nil {
		return errors.Wrap(err, "read user binding to ldap")
	}

	// Search request for userDN
	req := ldap.NewSearchRequest(
		l.UserBaseDn,
		ldap.ScopeWholeSubtree,
		ldap.NeverDerefAliases,
		0,
		0,
		false,
		fmt.Sprintf("(&(objectClass=%s)(%s=%s))",
			l.UserObjectClass, l.UserIdAttribute, user),
		nil,
		nil,
	)
	res, err := conn.Search(req)
	if err != nil {
		return errors.Wrap(err, "searching ldap")
	}
	if len(res.Entries) < 1 {
		return fmt.Errorf("user %s does not exist", user)
	} else if len(res.Entries) > 1 {
		return fmt.Errorf("too many user entries")
	}
	userDn := res.Entries[0].DN

	// Verify user Bind
	return errors.Wrapf(conn.Bind(userDn, password), "user %s binding to ldap", user)
}

func NewLdap(
	address, bindDn, bindPass string,
	userBaseDn, userIdAttribute, userObjectClass string,
	forceBind bool,
	idleTime, maxTime time.Duration) *Ldap {
	return &Ldap{
		// Base LDAP settings
		Address: address, BindDN: bindDn, BindPass: bindPass,
		// User filter settings
		UserBaseDn: userBaseDn, UserIdAttribute: userIdAttribute, UserObjectClass: userObjectClass,
		// Additional Configs
		ForceBind: forceBind,
		Users:     NewLdapUser(idleTime, maxTime),
	}
}

func FromConfig(fileName string) (*Ldap, error) {
	A := struct {
		Hostname        string
		Port            int
		BindDn          string
		BindPassword    string
		UserBaseDn      string
		UserIdAttribute string
		UserObjectClass string

		IsTLS     bool `toml:"useLdaps"`
		ForceBind bool
		IdleTime  time.Duration
		MaxTime   time.Duration
	}{}

	f, err := filepath.Abs(fileName)
	if err != nil {
		return nil, errors.Wrap(err, "getting absolute path")
	}
	if _, err := toml.DecodeFile(f, &A); err != nil {
		return nil, errors.Wrap(err, "decoding config file")
	}

	return NewLdap(
		fmt.Sprintf("%s:%d", A.Hostname, A.Port),
		A.BindDn, A.BindPassword,
		A.UserBaseDn, A.UserIdAttribute, A.UserObjectClass,

		A.ForceBind, time.Minute*A.IdleTime, time.Minute*A.MaxTime), nil
}
<|endoftext|>


<commit_before>package inbound

import (
	"context"
	"io"
	"sync"
	"sync/atomic"
	"time"

	"v2ray.com/core"
	"v2ray.com/core/app/proxyman"
	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/session"
	"v2ray.com/core/common/signal/done"
	"v2ray.com/core/proxy"
	"v2ray.com/core/transport/internet"
	"v2ray.com/core/transport/internet/tcp"
	"v2ray.com/core/transport/internet/udp"
)

type worker interface {
	Start() error
	Close() error
	Port() net.Port
	Proxy() proxy.Inbound
}

type tcpWorker struct {
	address         net.Address
	port            net.Port
	proxy           proxy.Inbound
	stream          *internet.StreamConfig
	recvOrigDest    bool
	tag             string
	dispatcher      core.Dispatcher
	sniffers        []proxyman.KnownProtocols
	uplinkCounter   core.StatCounter
	downlinkCounter core.StatCounter

	hub internet.Listener
}

func (w *tcpWorker) callback(conn internet.Connection) {
	ctx, cancel := context.WithCancel(context.Background())
	sid := session.NewID()
	ctx = session.ContextWithID(ctx, sid)

	if w.recvOrigDest {
		dest, err := tcp.GetOriginalDestination(conn)
		if err != nil {
			newError("failed to get original destination").WithContext(ctx).Base(err).WriteToLog()
		}
		if dest.IsValid() {
			ctx = proxy.ContextWithOriginalTarget(ctx, dest)
		}
	}
	if len(w.tag) > 0 {
		ctx = proxy.ContextWithInboundTag(ctx, w.tag)
	}
	ctx = proxy.ContextWithInboundEntryPoint(ctx, net.TCPDestination(w.address, w.port))
	ctx = proxy.ContextWithSource(ctx, net.DestinationFromAddr(conn.RemoteAddr()))
	if len(w.sniffers) > 0 {
		ctx = proxyman.ContextWithProtocolSniffers(ctx, w.sniffers)
	}
	if w.uplinkCounter != nil || w.downlinkCounter != nil {
		conn = &internet.StatCouterConnection{
			Connection: conn,
			Uplink:     w.uplinkCounter,
			Downlink:   w.downlinkCounter,
		}
	}
	if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil {
		newError("connection ends").Base(err).WithContext(ctx).WriteToLog()
	}
	cancel()
	if err := conn.Close(); err != nil {
		newError("failed to close connection").Base(err).WithContext(ctx).WriteToLog()
	}
}

func (w *tcpWorker) Proxy() proxy.Inbound {
	return w.proxy
}

func (w *tcpWorker) Start() error {
	ctx := internet.ContextWithStreamSettings(context.Background(), w.stream)
	hub, err := internet.ListenTCP(ctx, w.address, w.port, func(conn internet.Connection) {
		go w.callback(conn)
	})
	if err != nil {
		return newError("failed to listen TCP on ", w.port).AtWarning().Base(err)
	}
	w.hub = hub
	return nil
}

func (w *tcpWorker) Close() error {
	if w.hub != nil {
		common.Close(w.hub)
		common.Close(w.proxy)
	}

	return nil
}

func (w *tcpWorker) Port() net.Port {
	return w.port
}

type udpConn struct {
	lastActivityTime int64 // in seconds
	input            chan *buf.Buffer
	output           func([]byte) (int, error)
	remote           net.Addr
	local            net.Addr
	done             *done.Instance
	uplink           core.StatCounter
	downlink         core.StatCounter
}

func (c *udpConn) updateActivity() {
	atomic.StoreInt64(&c.lastActivityTime, time.Now().Unix())
}

// ReadMultiBuffer implements buf.Reader
func (c *udpConn) ReadMultiBuffer() (buf.MultiBuffer, error) {
	var payload buf.MultiBuffer

	select {
	case in := <-c.input:
		payload.Append(in)
	default:
		select {
		case in := <-c.input:
			payload.Append(in)
		case <-c.done.Wait():
			return nil, io.EOF
		}
	}

L:
	for {
		select {
		case in := <-c.input:
			payload.Append(in)
		default:
			break L
		}
	}

	return payload, nil
}

func (c *udpConn) Read(buf []byte) (int, error) {
	select {
	case in := <-c.input:
		defer in.Release()
		c.updateActivity()
		nBytes := copy(buf, in.Bytes())
		if c.uplink != nil {
			c.uplink.Add(int64(nBytes))
		}
		return nBytes, nil
	case <-c.done.Wait():
		return 0, io.EOF
	}
}

// Write implements io.Writer.
func (c *udpConn) Write(buf []byte) (int, error) {
	n, err := c.output(buf)
	if c.downlink != nil {
		c.downlink.Add(int64(n))
	}
	if err == nil {
		c.updateActivity()
	}
	return n, err
}

func (c *udpConn) Close() error {
	common.Must(c.done.Close())
	return nil
}

func (c *udpConn) RemoteAddr() net.Addr {
	return c.remote
}

func (c *udpConn) LocalAddr() net.Addr {
	return c.remote
}

func (*udpConn) SetDeadline(time.Time) error {
	return nil
}

func (*udpConn) SetReadDeadline(time.Time) error {
	return nil
}

func (*udpConn) SetWriteDeadline(time.Time) error {
	return nil
}

type connID struct {
	src  net.Destination
	dest net.Destination
}

type udpWorker struct {
	sync.RWMutex

	proxy           proxy.Inbound
	hub             *udp.Hub
	address         net.Address
	port            net.Port
	recvOrigDest    bool
	tag             string
	dispatcher      core.Dispatcher
	uplinkCounter   core.StatCounter
	downlinkCounter core.StatCounter

	done       *done.Instance
	activeConn map[connID]*udpConn
}

func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
	w.Lock()
	defer w.Unlock()

	if conn, found := w.activeConn[id]; found && !conn.done.Done() {
		return conn, true
	}

	conn := &udpConn{
		input: make(chan *buf.Buffer, 32),
		output: func(b []byte) (int, error) {
			return w.hub.WriteTo(b, id.src)
		},
		remote: &net.UDPAddr{
			IP:   id.src.Address.IP(),
			Port: int(id.src.Port),
		},
		local: &net.UDPAddr{
			IP:   w.address.IP(),
			Port: int(w.port),
		},
		done:     done.New(),
		uplink:   w.uplinkCounter,
		downlink: w.downlinkCounter,
	}
	w.activeConn[id] = conn

	conn.updateActivity()
	return conn, false
}

func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) {
	id := connID{
		src: source,
	}
	if originalDest.IsValid() {
		id.dest = originalDest
	}
	conn, existing := w.getConnection(id)
	select {
	case conn.input <- b:
	case <-conn.done.Wait():
		b.Release()
	default:
		b.Release()
	}

	if !existing {
		go func() {
			ctx := context.Background()
			sid := session.NewID()
			ctx = session.ContextWithID(ctx, sid)

			if originalDest.IsValid() {
				ctx = proxy.ContextWithOriginalTarget(ctx, originalDest)
			}
			if len(w.tag) > 0 {
				ctx = proxy.ContextWithInboundTag(ctx, w.tag)
			}
			ctx = proxy.ContextWithSource(ctx, source)
			ctx = proxy.ContextWithInboundEntryPoint(ctx, net.UDPDestination(w.address, w.port))
			if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil {
				newError("connection ends").Base(err).WriteToLog()
			}
			conn.Close()
			w.removeConn(id)
		}()
	}
}

func (w *udpWorker) removeConn(id connID) {
	w.Lock()
	delete(w.activeConn, id)
	w.Unlock()
}

func (w *udpWorker) Start() error {
	w.activeConn = make(map[connID]*udpConn, 16)
	w.done = done.New()
	h, err := udp.ListenUDP(w.address, w.port, w.callback, udp.HubReceiveOriginalDestination(w.recvOrigDest), udp.HubCapacity(256))
	if err != nil {
		return err
	}
	go w.monitor()
	w.hub = h
	return nil
}

func (w *udpWorker) Close() error {
	w.Lock()
	defer w.Unlock()

	if w.hub != nil {
		w.hub.Close()
	}

	if w.done != nil {
		common.Must(w.done.Close())
	}

	common.Close(w.proxy)
	return nil
}

func (w *udpWorker) monitor() {
	timer := time.NewTicker(time.Second * 16)
	defer timer.Stop()

	for {
		select {
		case <-w.done.Wait():
			return
		case <-timer.C:
			nowSec := time.Now().Unix()
			w.Lock()
			for addr, conn := range w.activeConn {
				if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 8 {
					delete(w.activeConn, addr)
					conn.Close()
				}
			}
			w.Unlock()
		}
	}
}

func (w *udpWorker) Port() net.Port {
	return w.port
}

func (w *udpWorker) Proxy() proxy.Inbound {
	return w.proxy
}
<commit_msg>update uplink traffic in read multibuffer<commit_after>package inbound

import (
	"context"
	"io"
	"sync"
	"sync/atomic"
	"time"

	"v2ray.com/core"
	"v2ray.com/core/app/proxyman"
	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/session"
	"v2ray.com/core/common/signal/done"
	"v2ray.com/core/proxy"
	"v2ray.com/core/transport/internet"
	"v2ray.com/core/transport/internet/tcp"
	"v2ray.com/core/transport/internet/udp"
)

type worker interface {
	Start() error
	Close() error
	Port() net.Port
	Proxy() proxy.Inbound
}

type tcpWorker struct {
	address         net.Address
	port            net.Port
	proxy           proxy.Inbound
	stream          *internet.StreamConfig
	recvOrigDest    bool
	tag             string
	dispatcher      core.Dispatcher
	sniffers        []proxyman.KnownProtocols
	uplinkCounter   core.StatCounter
	downlinkCounter core.StatCounter

	hub internet.Listener
}

func (w *tcpWorker) callback(conn internet.Connection) {
	ctx, cancel := context.WithCancel(context.Background())
	sid := session.NewID()
	ctx = session.ContextWithID(ctx, sid)

	if w.recvOrigDest {
		dest, err := tcp.GetOriginalDestination(conn)
		if err != nil {
			newError("failed to get original destination").WithContext(ctx).Base(err).WriteToLog()
		}
		if dest.IsValid() {
			ctx = proxy.ContextWithOriginalTarget(ctx, dest)
		}
	}
	if len(w.tag) > 0 {
		ctx = proxy.ContextWithInboundTag(ctx, w.tag)
	}
	ctx = proxy.ContextWithInboundEntryPoint(ctx, net.TCPDestination(w.address, w.port))
	ctx = proxy.ContextWithSource(ctx, net.DestinationFromAddr(conn.RemoteAddr()))
	if len(w.sniffers) > 0 {
		ctx = proxyman.ContextWithProtocolSniffers(ctx, w.sniffers)
	}
	if w.uplinkCounter != nil || w.downlinkCounter != nil {
		conn = &internet.StatCouterConnection{
			Connection: conn,
			Uplink:     w.uplinkCounter,
			Downlink:   w.downlinkCounter,
		}
	}
	if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil {
		newError("connection ends").Base(err).WithContext(ctx).WriteToLog()
	}
	cancel()
	if err := conn.Close(); err != nil {
		newError("failed to close connection").Base(err).WithContext(ctx).WriteToLog()
	}
}

func (w *tcpWorker) Proxy() proxy.Inbound {
	return w.proxy
}

func (w *tcpWorker) Start() error {
	ctx := internet.ContextWithStreamSettings(context.Background(), w.stream)
	hub, err := internet.ListenTCP(ctx, w.address, w.port, func(conn internet.Connection) {
		go w.callback(conn)
	})
	if err != nil {
		return newError("failed to listen TCP on ", w.port).AtWarning().Base(err)
	}
	w.hub = hub
	return nil
}

func (w *tcpWorker) Close() error {
	if w.hub != nil {
		common.Close(w.hub)
		common.Close(w.proxy)
	}

	return nil
}

func (w *tcpWorker) Port() net.Port {
	return w.port
}

type udpConn struct {
	lastActivityTime int64 // in seconds
	input            chan *buf.Buffer
	output           func([]byte) (int, error)
	remote           net.Addr
	local            net.Addr
	done             *done.Instance
	uplink           core.StatCounter
	downlink         core.StatCounter
}

func (c *udpConn) updateActivity() {
	atomic.StoreInt64(&c.lastActivityTime, time.Now().Unix())
}

// ReadMultiBuffer implements buf.Reader
func (c *udpConn) ReadMultiBuffer() (buf.MultiBuffer, error) {
	var payload buf.MultiBuffer

	select {
	case in := <-c.input:
		payload.Append(in)
	default:
		select {
		case in := <-c.input:
			payload.Append(in)
		case <-c.done.Wait():
			return nil, io.EOF
		}
	}

L:
	for {
		select {
		case in := <-c.input:
			payload.Append(in)
		default:
			break L
		}
	}

	if c.uplink != nil {
		c.uplink.Add(int64(payload.Len()))
	}

	return payload, nil
}

func (c *udpConn) Read(buf []byte) (int, error) {
	select {
	case in := <-c.input:
		defer in.Release()
		c.updateActivity()
		nBytes := copy(buf, in.Bytes())
		if c.uplink != nil {
			c.uplink.Add(int64(nBytes))
		}
		return nBytes, nil
	case <-c.done.Wait():
		return 0, io.EOF
	}
}

// Write implements io.Writer.
func (c *udpConn) Write(buf []byte) (int, error) {
	n, err := c.output(buf)
	if c.downlink != nil {
		c.downlink.Add(int64(n))
	}
	if err == nil {
		c.updateActivity()
	}
	return n, err
}

func (c *udpConn) Close() error {
	common.Must(c.done.Close())
	return nil
}

func (c *udpConn) RemoteAddr() net.Addr {
	return c.remote
}

func (c *udpConn) LocalAddr() net.Addr {
	return c.remote
}

func (*udpConn) SetDeadline(time.Time) error {
	return nil
}

func (*udpConn) SetReadDeadline(time.Time) error {
	return nil
}

func (*udpConn) SetWriteDeadline(time.Time) error {
	return nil
}

type connID struct {
	src  net.Destination
	dest net.Destination
}

type udpWorker struct {
	sync.RWMutex

	proxy           proxy.Inbound
	hub             *udp.Hub
	address         net.Address
	port            net.Port
	recvOrigDest    bool
	tag             string
	dispatcher      core.Dispatcher
	uplinkCounter   core.StatCounter
	downlinkCounter core.StatCounter

	done       *done.Instance
	activeConn map[connID]*udpConn
}

func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
	w.Lock()
	defer w.Unlock()

	if conn, found := w.activeConn[id]; found && !conn.done.Done() {
		return conn, true
	}

	conn := &udpConn{
		input: make(chan *buf.Buffer, 32),
		output: func(b []byte) (int, error) {
			return w.hub.WriteTo(b, id.src)
		},
		remote: &net.UDPAddr{
			IP:   id.src.Address.IP(),
			Port: int(id.src.Port),
		},
		local: &net.UDPAddr{
			IP:   w.address.IP(),
			Port: int(w.port),
		},
		done:     done.New(),
		uplink:   w.uplinkCounter,
		downlink: w.downlinkCounter,
	}
	w.activeConn[id] = conn

	conn.updateActivity()
	return conn, false
}

func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) {
	id := connID{
		src: source,
	}
	if originalDest.IsValid() {
		id.dest = originalDest
	}
	conn, existing := w.getConnection(id)
	select {
	case conn.input <- b:
	case <-conn.done.Wait():
		b.Release()
	default:
		b.Release()
	}

	if !existing {
		go func() {
			ctx := context.Background()
			sid := session.NewID()
			ctx = session.ContextWithID(ctx, sid)

			if originalDest.IsValid() {
				ctx = proxy.ContextWithOriginalTarget(ctx, originalDest)
			}
			if len(w.tag) > 0 {
				ctx = proxy.ContextWithInboundTag(ctx, w.tag)
			}
			ctx = proxy.ContextWithSource(ctx, source)
			ctx = proxy.ContextWithInboundEntryPoint(ctx, net.UDPDestination(w.address, w.port))
			if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil {
				newError("connection ends").Base(err).WriteToLog()
			}
			conn.Close()
			w.removeConn(id)
		}()
	}
}

func (w *udpWorker) removeConn(id connID) {
	w.Lock()
	delete(w.activeConn, id)
	w.Unlock()
}

func (w *udpWorker) Start() error {
	w.activeConn = make(map[connID]*udpConn, 16)
	w.done = done.New()
	h, err := udp.ListenUDP(w.address, w.port, w.callback, udp.HubReceiveOriginalDestination(w.recvOrigDest), udp.HubCapacity(256))
	if err != nil {
		return err
	}
	go w.monitor()
	w.hub = h
	return nil
}

func (w *udpWorker) Close() error {
	w.Lock()
	defer w.Unlock()

	if w.hub != nil {
		w.hub.Close()
	}

	if w.done != nil {
		common.Must(w.done.Close())
	}

	common.Close(w.proxy)
	return nil
}

func (w *udpWorker) monitor() {
	timer := time.NewTicker(time.Second * 16)
	defer timer.Stop()

	for {
		select {
		case <-w.done.Wait():
			return
		case <-timer.C:
			nowSec := time.Now().Unix()
			w.Lock()
			for addr, conn := range w.activeConn {
				if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 8 {
					delete(w.activeConn, addr)
					conn.Close()
				}
			}
			w.Unlock()
		}
	}
}

func (w *udpWorker) Port() net.Port {
	return w.port
}

func (w *udpWorker) Proxy() proxy.Inbound {
	return w.proxy
}
<|endoftext|>


<commit_before>package router_test

import (
	"context"
	"testing"

	"v2ray.com/core/app"
	"v2ray.com/core/app/dispatcher"
	_ "v2ray.com/core/app/dispatcher/impl"
	"v2ray.com/core/app/dns"
	_ "v2ray.com/core/app/dns/server"
	"v2ray.com/core/app/proxyman"
	_ "v2ray.com/core/app/proxyman/outbound"
	. "v2ray.com/core/app/router"
	"v2ray.com/core/common/net"
	"v2ray.com/core/proxy"
	. "v2ray.com/ext/assert"
)

func TestSimpleRouter(t *testing.T) {
	assert := With(t)

	config := &Config{
		Rule: []*RoutingRule{
			{
				Tag: "test",
				NetworkList: &net.NetworkList{
					Network: []net.Network{net.Network_TCP},
				},
			},
		},
	}

	space := app.NewSpace()
	ctx := app.ContextWithSpace(context.Background(), space)
	assert(app.AddApplicationToSpace(ctx, new(dns.Config)), IsNil)
	assert(app.AddApplicationToSpace(ctx, new(dispatcher.Config)), IsNil)
	assert(app.AddApplicationToSpace(ctx, new(proxyman.OutboundConfig)), IsNil)
	assert(app.AddApplicationToSpace(ctx, config), IsNil)
	assert(space.Initialize(), IsNil)

	r := FromSpace(space)

	ctx = proxy.ContextWithTarget(ctx, net.TCPDestination(net.DomainAddress("v2ray.com"), 80))
	tag, err := r.TakeDetour(ctx)
	assert(err, IsNil)
	assert(tag, Equals, "test")
}
<commit_msg>fix test<commit_after>package router_test

import (
	"context"
	"testing"

	"v2ray.com/core/app"
	"v2ray.com/core/app/dispatcher"
	_ "v2ray.com/core/app/dispatcher/impl"
	"v2ray.com/core/app/proxyman"
	_ "v2ray.com/core/app/proxyman/outbound"
	. "v2ray.com/core/app/router"
	"v2ray.com/core/common/net"
	"v2ray.com/core/proxy"
	. "v2ray.com/ext/assert"
)

func TestSimpleRouter(t *testing.T) {
	assert := With(t)

	config := &Config{
		Rule: []*RoutingRule{
			{
				Tag: "test",
				NetworkList: &net.NetworkList{
					Network: []net.Network{net.Network_TCP},
				},
			},
		},
	}

	space := app.NewSpace()
	ctx := app.ContextWithSpace(context.Background(), space)
	assert(app.AddApplicationToSpace(ctx, new(dispatcher.Config)), IsNil)
	assert(app.AddApplicationToSpace(ctx, new(proxyman.OutboundConfig)), IsNil)
	assert(app.AddApplicationToSpace(ctx, config), IsNil)
	assert(space.Initialize(), IsNil)

	r := FromSpace(space)

	ctx = proxy.ContextWithTarget(ctx, net.TCPDestination(net.DomainAddress("v2ray.com"), 80))
	tag, err := r.TakeDetour(ctx)
	assert(err, IsNil)
	assert(tag, Equals, "test")
}
<|endoftext|>


<commit_before>package bitmessage

// This file implements the main engine for this BitMessage node.

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"sync"
	"time"
)

type Node struct {
	cfg *Config

	knownNodes streamNodes

	// Stats. All access must be synchronized because it's often used by other
	// goroutines (UI).
	stats stats

	recvChan chan packet
}

const (
	portNumber = 9090
	// Using same value from PyBitmessage, which was originally added to avoid memory blowups.
	// The protocol itself doesn't restrict it. This should certainly be removed
	maxPayloadLength = 180000000
)

func (n *Node) Run() {
	n.cfg = openConfig(portNumber)

	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", portNumber))
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Listening at", listener.Addr())
	n.recvChan = make(chan packet)
	go listen(listener.(*net.TCPListener), n.recvChan)
	n.boot()

	for {
		select {
		case p := <-n.recvChan:
			fmt.Println("recvChan from %v, got: %q", p.raddr, p.b)
		}

	}
}

type packet struct {
	b     []byte
	raddr net.Addr
}

// Read from TCP , writes slice of byte into channel.
func listen(listener *net.TCPListener, recvChan chan packet) {
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			log.Fatal("Can't listen to network port:", err)
			return
		}
		go handleConn(conn, recvChan)
	}
}

type peerState struct {
	versionMatch bool
}

func handleConn(conn *net.TCPConn, recvChan chan packet) {
	defer conn.Close()

	p := peerState{}
	for {
		conn.SetReadDeadline(time.Now().Add(time.Second * 30))
		command, payload, err := readMessage(conn)
		if err != nil {
			log.Println("handleConn:", err)
			return
		}
		log.Printf("got command: %v", command)
		switch command {
		case "version":
			if p.versionMatch {
				log.Println("received a 'version' message from a host we already went through a version exchange. Closing the connection.")
				return
			}

			// XXX move readmessage to return a reader?
			version, err := parseVersion(bytes.NewBuffer(payload))
			if version.Version != protocolVersion {
				log.Printf("protocol version not supported: got %d, wanted %d.Closing the connection", version.Version, protocolVersion)
				return
			}
			fmt.Println("doing something with", version, err)
		}
	}
	// XXX send something to recvChan.
}

// Things needing implementation.
//
// broadcastToSendDataQueues((0, 'shutdown', self.HOST))
//
// Zero out the list of already contacted nodes every 30 minutes, to give it another chance.
//
// self.receivedgetbiginv = False #Gets set to true once we receive a
// getbiginv message from our peer. An abusive peer might request it too much
// so we use this variable to check whether they have already asked for a big
// inv message.

// #Only one singleListener thread will ever exist. It creates the
// #receiveDataThread and sendDataThread for each incoming connection. Note
// #that it cannot set the stream number because it is not known yet- the
// #other node will have to tell us its stream number in a version message. If
// #we don't care about their stream, we will close the connection (within the
// #recversion function of the recieveData thread)

// #For each stream to which we connect, several outgoingSynSender threads
// #will exist and will collectively create 8 connections with peers.
//
// Random nonce used to detect connections to self.

type stats struct {
	sync.RWMutex
	streamConnectionCount map[int]int
}

// ipPort is a string that can be resolved with net.ResolveTCPAddr("tcp",
// ipPort) and the first part can be parsed by net.ParseIP().
type ipPort string
<commit_msg>send verack when proto matches<commit_after>package bitmessage

// This file implements the main engine for this BitMessage node.

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"sync"
	"time"
)

type Node struct {
	cfg *Config

	knownNodes streamNodes

	// Stats. All access must be synchronized because it's often used by other
	// goroutines (UI).
	stats stats

	recvChan chan packet
}

const (
	portNumber = 9090
	// Using same value from PyBitmessage, which was originally added to avoid memory blowups.
	// The protocol itself doesn't restrict it. This should certainly be removed
	maxPayloadLength = 180000000
)

func (n *Node) Run() {
	n.cfg = openConfig(portNumber)

	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", portNumber))
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Listening at", listener.Addr())
	n.recvChan = make(chan packet)
	go listen(listener.(*net.TCPListener), n.recvChan)
	n.boot()

	for {
		select {
		case p := <-n.recvChan:
			fmt.Println("recvChan from %v, got: %q", p.raddr, p.b)
		}

	}
}

type packet struct {
	b     []byte
	raddr net.Addr
}

// Read from TCP , writes slice of byte into channel.
func listen(listener *net.TCPListener, recvChan chan packet) {
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			log.Fatal("Can't listen to network port:", err)
			return
		}
		go handleConn(conn, recvChan)
	}
}

type peerState struct {
	versionMatch bool
}

func handleConn(conn *net.TCPConn, recvChan chan packet) {
	defer conn.Close()

	p := peerState{}
	for {
		conn.SetReadDeadline(time.Now().Add(time.Second * 30))
		command, payload, err := readMessage(conn)
		if err != nil {
			log.Println("handleConn:", err)
			return
		}
		log.Printf("got command: %v", command)
		switch command {
		case "version":
			if p.versionMatch {
				log.Println("received a 'version' message from a host we already went through a version exchange. Closing the connection.")
				return
			}

			// XXX move readmessage to return a reader?
			version, err := parseVersion(bytes.NewBuffer(payload))
			if err != nil {
				log.Println("parseVersion:", err)
				return
			}
			if version.Version != protocolVersion {
				log.Printf("protocol version not supported: got %d, wanted %d.Closing the connection", version.Version, protocolVersion)
				return
			}
			p.versionMatch = true
			writeVerack(conn)
		}
	}
	// XXX send something to recvChan.
}

// Things needing implementation.
//
// broadcastToSendDataQueues((0, 'shutdown', self.HOST))
//
// Zero out the list of already contacted nodes every 30 minutes, to give it another chance.
//
// self.receivedgetbiginv = False #Gets set to true once we receive a
// getbiginv message from our peer. An abusive peer might request it too much
// so we use this variable to check whether they have already asked for a big
// inv message.

// #Only one singleListener thread will ever exist. It creates the
// #receiveDataThread and sendDataThread for each incoming connection. Note
// #that it cannot set the stream number because it is not known yet- the
// #other node will have to tell us its stream number in a version message. If
// #we don't care about their stream, we will close the connection (within the
// #recversion function of the recieveData thread)

// #For each stream to which we connect, several outgoingSynSender threads
// #will exist and will collectively create 8 connections with peers.
//
// Random nonce used to detect connections to self.

type stats struct {
	sync.RWMutex
	streamConnectionCount map[int]int
}

// ipPort is a string that can be resolved with net.ResolveTCPAddr("tcp",
// ipPort) and the first part can be parsed by net.ParseIP().
type ipPort string
<|endoftext|>


<commit_before>package wrtc

import (
	"fmt"

	"github.com/jeremija/peer-calls/src/server-go/logger"
	"github.com/pion/webrtc/v2"
)

type SignalCandidate struct {
	UserID string    `json:"userId"`
	Signal Candidate `json:"signal"`
}

type SignalSDP struct {
	UserID string                    `json:"userId"`
	Signal webrtc.SessionDescription `json:"signal"`
}

type Candidate struct {
	Candidate webrtc.ICECandidateInit `json:"candidate"`
}

type PeerConnection interface {
	OnICECandidate(func(*webrtc.ICECandidate))
	AddICECandidate(webrtc.ICECandidateInit) error
	SetRemoteDescription(webrtc.SessionDescription) error
	SetLocalDescription(webrtc.SessionDescription) error
	CreateAnswer(*webrtc.AnswerOptions) (webrtc.SessionDescription, error)
}

type Signaller struct {
	peerConnection    PeerConnection
	localPeerID       string
	onSignalSDP       func(signal SignalSDP) error
	onSignalCandidate func(signal SignalCandidate)
}

var log = logger.GetLogger("wrtc")

func NewSignaller(
	peerConnection PeerConnection,
	localPeerID string,
	onSignalSDP func(signal SignalSDP) error,
	onSignalCandidate func(signal SignalCandidate),
) *Signaller {
	s := Signaller{
		peerConnection:    peerConnection,
		localPeerID:       localPeerID,
		onSignalSDP:       onSignalSDP,
		onSignalCandidate: onSignalCandidate,
	}

	// peerConnection.OnICECandidate(s.handleICECandidate)

	return &s
}

func (s *Signaller) handleICECandidate(c *webrtc.ICECandidate) {
	if c == nil {
		return
	}

	payload := SignalCandidate{
		UserID: s.localPeerID,
		Signal: Candidate{
			Candidate: c.ToJSON(),
		},
	}

	log.Printf("Got ice candidate from server peer: %s", payload)
	s.onSignalCandidate(payload)
}

func (s *Signaller) Signal(payload map[string]interface{}) (err error) {
	// log.Printf("Got signal: %#v", payload)

	signal, _ := payload["signal"].(map[string]interface{})
	remotePeerID, _ := payload["userId"].(string)

	if remotePeerID != s.localPeerID {
		return fmt.Errorf("Peer2Server only sends signals to server as peer")
	}

	if candidate, ok := signal["candidate"]; ok {
		err = s.handleSignalCandidate(remotePeerID, candidate)
	} else if sdpTypeString, ok := signal["type"]; ok {
		err = s.handleSDP(sdpTypeString, signal["sdp"])
	} else {
		err = fmt.Errorf("Unexpected signal message: %#v", payload)
	}

	return
}

func (s *Signaller) handleSignalCandidate(targetClientID string, candidate interface{}) (err error) {
	// log.Printf("Got client ice candidate: %#v", candidate)
	candidateMap, ok := candidate.(map[string]interface{})
	if !ok {
		return fmt.Errorf("Expected ice candidate to be a map")
	}

	candidateString, _ := candidateMap["candidate"].(string)
	sdpMLineIndex, _ := candidateMap["sdpMLineIndex"].(uint16)
	sdpMid, _ := candidateMap["sdpMid"].(string)

	iceCandidate := webrtc.ICECandidateInit{
		Candidate:     candidateString,
		SDPMLineIndex: &sdpMLineIndex,
		SDPMid:        &sdpMid,
	}

	// log.Printf("Parsed ice candidate: %#v", iceCandidate)

	err = s.peerConnection.AddICECandidate(iceCandidate)
	return
}

func (s *Signaller) handleSDP(sdpType interface{}, sdp interface{}) (err error) {
	sdpTypeString, _ := sdpType.(string)
	sdpString, _ := sdp.(string)
	sessionDescription := webrtc.SessionDescription{}
	sessionDescription.SDP = sdpString
	log.Printf("Got client signal type: %s", sdpType)

	switch sdpTypeString {
	case "offer":
		sessionDescription.Type = webrtc.SDPTypeOffer
		// mediaEngine.PopulateFromSDP(sdp) TODO figure out if we need this
		// videoCodecs := mediaEngine.GetCodecsByKind(webrtc.RTPCodecTypeVideo)
		// audioCodecs := mediaEngine.GetCodecsByKind(webrtc.RTPCodecTypeAudio)
	case "answer":
		sessionDescription.Type = webrtc.SDPTypeAnswer
	case "pranswer":
		sessionDescription.Type = webrtc.SDPTypePranswer
	case "rollback":
		sessionDescription.Type = webrtc.SDPTypeRollback
	default:
		return fmt.Errorf("Unknown sdp type: %s", sdpString)
	}

	if err = s.peerConnection.SetRemoteDescription(sessionDescription); err != nil {
		return fmt.Errorf("Error setting remote description: %w", err)
	}
	answer, err := s.peerConnection.CreateAnswer(nil)
	if err != nil {
		return fmt.Errorf("Error creating answer: %w", err)
	}
	if err := s.peerConnection.SetLocalDescription(answer); err != nil {
		return fmt.Errorf("Error setting local description: %w", err)
	}

	answerSignalSDP := SignalSDP{
		UserID: s.localPeerID,
		Signal: answer,
	}
	// log.Printf("Sending answer: %#v", answerSignalSDP)
	err = s.onSignalSDP(answerSignalSDP)
	return err
}
<commit_msg>Add support for initiating requests to signaller<commit_after>package wrtc

import (
	"fmt"

	"github.com/jeremija/peer-calls/src/server-go/logger"
	"github.com/pion/webrtc/v2"
)

type SignalCandidate struct {
	UserID string    `json:"userId"`
	Signal Candidate `json:"signal"`
}

type SignalSDP struct {
	UserID string                    `json:"userId"`
	Signal webrtc.SessionDescription `json:"signal"`
}

type Candidate struct {
	Candidate webrtc.ICECandidateInit `json:"candidate"`
}

type PeerConnection interface {
	OnICECandidate(func(*webrtc.ICECandidate))
	AddICECandidate(webrtc.ICECandidateInit) error
	SetRemoteDescription(webrtc.SessionDescription) error
	SetLocalDescription(webrtc.SessionDescription) error
	CreateOffer(*webrtc.OfferOptions) (webrtc.SessionDescription, error)
	CreateAnswer(*webrtc.AnswerOptions) (webrtc.SessionDescription, error)
}

type Signaller struct {
	peerConnection    PeerConnection
	localPeerID       string
	onSignalSDP       func(signal SignalSDP) error
	onSignalCandidate func(signal SignalCandidate)
}

var log = logger.GetLogger("wrtc")

func NewSignaller(
	peerConnection PeerConnection,
	localPeerID string,
	onSignalSDP func(signal SignalSDP) error,
	onSignalCandidate func(signal SignalCandidate),
) *Signaller {
	s := Signaller{
		peerConnection:    peerConnection,
		localPeerID:       localPeerID,
		onSignalSDP:       onSignalSDP,
		onSignalCandidate: onSignalCandidate,
	}

	// peerConnection.OnICECandidate(s.handleICECandidate)

	return &s
}

func (s *Signaller) handleICECandidate(c *webrtc.ICECandidate) {
	if c == nil {
		return
	}

	payload := SignalCandidate{
		UserID: s.localPeerID,
		Signal: Candidate{
			Candidate: c.ToJSON(),
		},
	}

	log.Printf("Got ice candidate from server peer: %s", payload)
	s.onSignalCandidate(payload)
}

func (s *Signaller) Signal(payload map[string]interface{}) (err error) {
	signal, _ := payload["signal"].(map[string]interface{})
	remotePeerID, _ := payload["userId"].(string)

	if remotePeerID != s.localPeerID {
		return fmt.Errorf("Peer2Server only sends signals to server as peer")
	}

	if candidate, ok := signal["candidate"]; ok {
		log.Printf("Got remote ice candidate")
		err = s.handleSignalCandidate(remotePeerID, candidate)
	} else if _, ok := signal["renegotiate"]; ok {
		log.Printf("Got renegotiation request")
		s.Negotiate()
	} else if sdpType, ok := signal["type"]; ok {
		log.Printf("Got remote signal (type: %s)", sdpType)
		err = s.handleSDP(sdpType, signal["sdp"])
	} else {
		err = fmt.Errorf("Unexpected signal message: %#v", payload)
	}

	return
}

func (s *Signaller) handleSignalCandidate(targetClientID string, candidate interface{}) (err error) {
	// log.Printf("Got client ice candidate: %#v", candidate)
	candidateMap, ok := candidate.(map[string]interface{})
	if !ok {
		return fmt.Errorf("Expected ice candidate to be a map")
	}

	candidateString, _ := candidateMap["candidate"].(string)
	sdpMLineIndex, _ := candidateMap["sdpMLineIndex"].(uint16)
	sdpMid, _ := candidateMap["sdpMid"].(string)

	iceCandidate := webrtc.ICECandidateInit{
		Candidate:     candidateString,
		SDPMLineIndex: &sdpMLineIndex,
		SDPMid:        &sdpMid,
	}

	// log.Printf("Parsed ice candidate: %#v", iceCandidate)

	err = s.peerConnection.AddICECandidate(iceCandidate)
	return
}

func (s *Signaller) handleSDP(sdpType interface{}, sdp interface{}) (err error) {
	sdpTypeString, _ := sdpType.(string)
	sdpString, _ := sdp.(string)
	sessionDescription := webrtc.SessionDescription{}
	sessionDescription.SDP = sdpString

	switch sdpTypeString {
	case "offer":
		sessionDescription.Type = webrtc.SDPTypeOffer
		return s.handleOffer(sessionDescription)
		// mediaEngine.PopulateFromSDP(sdp) TODO figure out if we need this
		// videoCodecs := mediaEngine.GetCodecsByKind(webrtc.RTPCodecTypeVideo)
		// audioCodecs := mediaEngine.GetCodecsByKind(webrtc.RTPCodecTypeAudio)
	case "answer":
		sessionDescription.Type = webrtc.SDPTypeAnswer
		return s.handleAnswer(sessionDescription)
	case "pranswer":
		return fmt.Errorf("Handling of pranswer signal implemented")
	case "rollback":
		return fmt.Errorf("Handling of rollback signal not implemented")
	default:
		return fmt.Errorf("Unknown sdp type: %s", sdpString)
	}

}

func (s *Signaller) handleOffer(sessionDescription webrtc.SessionDescription) (err error) {
	if err = s.peerConnection.SetRemoteDescription(sessionDescription); err != nil {
		return fmt.Errorf("Error setting remote description: %w", err)
	}
	answer, err := s.peerConnection.CreateAnswer(nil)
	if err != nil {
		return fmt.Errorf("Error creating answer: %w", err)
	}
	if err := s.peerConnection.SetLocalDescription(answer); err != nil {
		return fmt.Errorf("Error setting local description: %w", err)
	}

	answerSignalSDP := SignalSDP{
		UserID: s.localPeerID,
		Signal: answer,
	}
	// log.Printf("Sending answer: %#v", answerSignalSDP)
	err = s.onSignalSDP(answerSignalSDP)
	return err
}

// TODO check offer voice activation detection feature of webrtc

// Create an offer and send it to remote peer
func (s *Signaller) Negotiate() (err error) {
	offer, err := s.peerConnection.CreateOffer(nil)
	if err != nil {
		return fmt.Errorf("Error creating offer: %w", err)
	}
	s.peerConnection.SetLocalDescription(offer)
	offerSignalSDP := SignalSDP{
		UserID: s.localPeerID,
		Signal: offer,
	}
	err = s.onSignalSDP(offerSignalSDP)
	return
}

func (s *Signaller) handleAnswer(sessionDescription webrtc.SessionDescription) (err error) {
	if err = s.peerConnection.SetRemoteDescription(sessionDescription); err != nil {
		return fmt.Errorf("Error setting remote description: %w", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package tests

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"
)

// TestWorkflow runs etcd-ca in the normal workflow
// and traverses all commands
func TestWorkflow(t *testing.T) {
	os.RemoveAll(depotDir)
	defer os.RemoveAll(depotDir)

	stdout, stderr, err := run(binPath, "init", "--passphrase", passphrase)
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	if strings.Count(stdout, "Created") != 2 {
		t.Fatalf("Received insufficient create: %v", stdout)
	}

	stdout, stderr, err = run(binPath, "new-cert", "--passphrase", passphrase, hostname)
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	if strings.Count(stdout, "Created") != 2 {
		t.Fatalf("Received insufficient create: %v", stdout)
	}

	stdout, stderr, err = run(binPath, "sign", "--passphrase", passphrase, hostname)
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	if strings.Count(stdout, "Created") != 1 {
		t.Fatalf("Received insufficient create: %v", stdout)
	}

	stdout, stderr, err = run(binPath, "chain")
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	if strings.Count(stdout, "CERTIFICATE") != 2 {
		t.Fatalf("Received insufficient CERTIFICATE: %v", stdout)
	}

	stdout, stderr, err = run(binPath, "chain", hostname)
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	if strings.Count(stdout, "CERTIFICATE") != 4 {
		t.Fatalf("Received insufficient CERTIFICATE: %v", stdout)
	}

	stdout, stderr, err = run(binPath, "export", hostname)
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	ioutil.WriteFile("1.tar", []byte(stdout), 0644)
	defer os.Remove("1.tar")

	err = exec.Command("tar", "xvf", "1.tar").Run()
	if err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	keyFile := fmt.Sprintf("%v.key", hostname)
	crtFile := fmt.Sprintf("%v.crt", hostname)
	defer os.Remove(keyFile)
	defer os.Remove(crtFile)
	if _, err = os.Stat(keyFile); err != nil {
		t.Fatal("Failed stating", keyFile)
	}
	if _, err = os.Stat(crtFile); err != nil {
		t.Fatal("Failed stating", keyFile)
	}

	stdout, stderr, err = run(binPath, "status")
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	if strings.Count(stdout, "expiration") != 2 {
		t.Fatalf("Received insufficient expiration: %v", stdout)
	}
}
<commit_msg>tests: fix broken tests<commit_after>package tests

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"testing"
)

// TestWorkflow runs etcd-ca in the normal workflow
// and traverses all commands
func TestWorkflow(t *testing.T) {
	os.RemoveAll(depotDir)
	defer os.RemoveAll(depotDir)

	stdout, stderr, err := run(binPath, "init", "--passphrase", passphrase)
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	if strings.Count(stdout, "Created") != 2 {
		t.Fatalf("Received insufficient create: %v", stdout)
	}

	stdout, stderr, err = run(binPath, "new-cert", "--passphrase", passphrase, hostname)
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	if strings.Count(stdout, "Created") != 2 {
		t.Fatalf("Received insufficient create: %v", stdout)
	}

	stdout, stderr, err = run(binPath, "sign", "--passphrase", passphrase, hostname)
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	if strings.Count(stdout, "Created") != 1 {
		t.Fatalf("Received insufficient create: %v", stdout)
	}

	stdout, stderr, err = run(binPath, "chain")
	if err != nil {
		t.Fatalf("Received unexpected error: %v", err)
	}
	if strings.Count(stdout, "CERTIFICATE") != 2 {
		t.Fatalf("Received insufficient CERTIFICATE: %v", stdout)
	}

	stdout, stderr, err = run(binPath, "chain", hostname)
	if err != nil {
		t.Fatalf("Received unexpected error: %v", err)
	}
	if strings.Count(stdout, "CERTIFICATE") != 4 {
		t.Fatalf("Received insufficient CERTIFICATE: %v", stdout)
	}

	stdout, stderr, err = run(binPath, "export", hostname)
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	ioutil.WriteFile("1.tar", []byte(stdout), 0644)
	defer os.Remove("1.tar")

	err = exec.Command("tar", "xvf", "1.tar").Run()
	if err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	keyFile := fmt.Sprintf("%v.key", hostname)
	crtFile := fmt.Sprintf("%v.crt", hostname)
	defer os.Remove(keyFile)
	defer os.Remove(crtFile)
	if _, err = os.Stat(keyFile); err != nil {
		t.Fatal("Failed stating", keyFile)
	}
	if _, err = os.Stat(crtFile); err != nil {
		t.Fatal("Failed stating", keyFile)
	}

	stdout, stderr, err = run(binPath, "status")
	if stderr != "" || err != nil {
		t.Fatalf("Received unexpected error: %v, %v", stderr, err)
	}
	if strings.Count(stdout, "expiration") != 2 {
		t.Fatalf("Received insufficient expiration: %v", stdout)
	}
}
<|endoftext|>


<commit_before>package backend

import (
	"facette/template"
	"sort"

	"github.com/facette/maputil"
	"github.com/facette/natsort"
	"github.com/facette/sliceutil"
	"github.com/jinzhu/gorm"
)

// Collection represents a back-end collection item instance.
type Collection struct {
	Item
	Entries    []*CollectionEntry `json:"entries,omitempty"`
	Link       *Collection        `json:"-"`
	LinkID     *string            `gorm:"column:link;type:varchar(36) DEFAULT NULL REFERENCES collections (id) ON DELETE CASCADE ON UPDATE CASCADE" json:"link,omitempty"`
	Attributes maputil.Map        `gorm:"type:text" json:"attributes,omitempty"`
	Alias      *string            `gorm:"type:varchar(128);unique_index" json:"alias,omitempty"`
	Options    maputil.Map        `gorm:"type:text" json:"options,omitempty"`
	Parent     *Collection        `json:"-"`
	ParentID   *string            `gorm:"column:parent;type:varchar(36) DEFAULT NULL REFERENCES collections (id) ON DELETE SET NULL ON UPDATE SET NULL" json:"parent,omitempty"`
	Template   bool               `gorm:"not null" json:"template"`

	resolved bool
	expanded bool
}

// NewCollection creates a new collection item instance.
func (b *Backend) NewCollection() *Collection {
	return &Collection{Item: Item{backend: b}}
}

// BeforeSave handles the ORM 'BeforeSave' callback.
func (c *Collection) BeforeSave(scope *gorm.Scope) error {
	if err := c.Item.BeforeSave(scope); err != nil {
		return err
	} else if c.Alias != nil && *c.Alias != "" && !nameRegexp.MatchString(*c.Alias) {
		return ErrInvalidAlias
	}

	for idx, entry := range c.Entries {
		entry.Index = idx + 1
	}

	// Ensure optional fields are null if empty
	if c.LinkID != nil && *c.LinkID == "" {
		scope.SetColumn("LinkID", nil)
	}

	if c.Alias != nil && *c.Alias == "" {
		scope.SetColumn("Alias", nil)
	}

	if c.ParentID != nil && *c.ParentID == "" {
		scope.SetColumn("ParentID", nil)
	}

	return nil
}

// Clone returns a clone of the collection item instance.
func (c *Collection) Clone() *Collection {
	clone := &Collection{}
	*clone = *c

	clone.Entries = make([]*CollectionEntry, len(c.Entries))
	for i, entry := range c.Entries {
		clone.Entries[i] = &CollectionEntry{}
		*clone.Entries[i] = *entry

		if entry.Options != nil {
			clone.Entries[i].Options = entry.Options.Clone()
		}
	}

	if c.Attributes != nil {
		clone.Attributes = c.Attributes.Clone()
	}

	if c.Options != nil {
		clone.Options = c.Options.Clone()
	}

	if c.Link != nil {
		clone.Link = &Collection{}
		*clone.Link = *c.Link
	}

	if c.Parent != nil {
		clone.Parent = &Collection{}
		*clone.Parent = *c.Parent
	}

	return clone
}

// Expand expands the collection item instance using its linked instance.
func (c *Collection) Expand(attrs maputil.Map) error {
	var err error

	if c.expanded {
		return nil
	}

	if len(attrs) > 0 {
		c.Attributes.Merge(attrs, true)
	}

	if c.backend != nil && c.LinkID != nil && *c.LinkID != "" {
		if err := c.Resolve(nil); err != nil {
			return err
		}

		// Expand template and applies current collection's attributes and options
		tmpl := c.Link.Clone()
		tmpl.ID = c.ID
		tmpl.Attributes.Merge(c.Attributes, true)
		tmpl.Options.Merge(c.Options, true)
		tmpl.Template = false

		if c.ParentID != nil && *c.ParentID != "" {
			tmpl.Parent = c.Parent.Clone()
			tmpl.ParentID = c.ParentID
		}

		if title, ok := tmpl.Options["title"].(string); ok {
			if tmpl.Options["title"], err = template.Expand(title, tmpl.Attributes); err != nil {
				return err
			}
		}

		// Fetch graph entries titles
		graphs := map[string]*Graph{}

		values := []string{}
		for _, entry := range tmpl.Entries {
			if !sliceutil.Has(values, entry.GraphID) {
				values = append(values, entry.GraphID)
			}
		}

		if len(values) > 0 {
			result := []*Graph{}
			if err := c.backend.Storage().Get("id", values, &result, false); err != nil {
				return err
			}

			for _, entry := range result {
				graphs[entry.ID] = entry
			}
		}

		for _, entry := range tmpl.Entries {
			g, ok := graphs[entry.GraphID]
			if !ok {
				continue
			}

			attrs := maputil.Map{}
			attrs.Merge(c.Attributes, true)
			attrs.Merge(g.Attributes, true)
			attrs.Merge(entry.Attributes, true)

			opts := maputil.Map{}
			opts.Merge(g.Options, true)
			opts.Merge(entry.Options, true)

			if opts.Has("title") {
				title, _ := opts.GetString("title", "")

				if opts["title"], err = template.Expand(title, attrs); err != nil {
					return err
				}
			}

			entry.Options = opts
		}

		*c = *tmpl
	}

	if len(c.Entries) > 0 {
		if !c.resolved {
			if err := c.Resolve(nil); err != nil {
				return err
			}
		}

		for _, entry := range c.Entries {
			entry.Graph.Expand(c.Attributes)

			if v, ok := entry.Graph.Options["title"]; ok {
				if entry.Options == nil {
					entry.Options = maputil.Map{}
				}
				entry.Options["title"] = v
			}
		}
	}

	c.expanded = true

	return nil
}

// HasParent returns whether or not the collection item has a parent instance.
func (c *Collection) HasParent() bool {
	return c.ParentID != nil && *c.ParentID != ""
}

// Resolve resolves both the collection item linked and parent instances.
func (c *Collection) Resolve(cache map[string]*Collection) error {
	if c.resolved {
		return nil
	} else if c.backend == nil {
		return ErrUnresolvableItem
	}

	if c.LinkID != nil && *c.LinkID != "" {
		if cache != nil {
			if link, ok := cache[*c.LinkID]; ok {
				c.Link = link
			}
		} else {
			c.Link = c.backend.NewCollection()
			if err := c.backend.Storage().Get("id", *c.LinkID, c.Link, true); err != nil {
				return err
			}
		}
	}

	if c.ParentID != nil && *c.ParentID != "" {
		if cache != nil {
			if parent, ok := cache[*c.ParentID]; ok {
				c.Parent = parent
			}
		} else {
			c.Parent = c.backend.NewCollection()
			if err := c.backend.Storage().Get("id", *c.ParentID, c.Parent, true); err != nil {
				return err
			}
		}
	}

	// Resolve associated graphs if any
	ids := []string{}
	for _, entry := range c.Entries {
		if !sliceutil.Has(ids, entry.GraphID) {
			ids = append(ids, entry.GraphID)
		}
	}

	if len(ids) > 0 {
		graphs := []*Graph{}
		if err := c.backend.Storage().Get("id", ids, &graphs, false); err != nil {
			return err
		}

		graphsMap := map[string]*Graph{}
		for _, g := range graphs {
			graphsMap[g.ID] = g
		}

		for _, entry := range c.Entries {
			if g, ok := graphsMap[entry.GraphID]; ok {
				entry.Graph = g.Clone()
			}
		}
	}

	c.resolved = true

	return nil
}

func (c *Collection) treeEntry() *CollectionTreeEntry {
	entry := &CollectionTreeEntry{
		ID:       c.ID,
		Children: &CollectionTree{},
	}

	if c.HasParent() {
		entry.Parent = *c.ParentID
	}

	// Use title as label if any or fallback to collection name
	if title, ok := c.Options["title"].(string); ok && title != "" {
		entry.Label = title
	} else {
		entry.Label = c.Name
	}

	return entry
}

// CollectionEntry represents a back-end collection entry instance.
type CollectionEntry struct {
	Index        int         `gorm:"type:int NOT NULL;primary_key" json:"-"`
	Collection   *Collection `json:"-"`
	CollectionID string      `gorm:"column:collection;type:varchar(36) NOT NULL REFERENCES collections (id) ON DELETE CASCADE ON UPDATE CASCADE;primary_key" json:"-"`
	Graph        *Graph      `json:"-"`
	GraphID      string      `gorm:"column:graph;type:varchar(36) NOT NULL REFERENCES graphs (id) ON DELETE CASCADE ON UPDATE CASCADE;primary_key" json:"graph"`
	Attributes   maputil.Map `gorm:"type:text" json:"attributes,omitempty"`
	Options      maputil.Map `gorm:"type:text" json:"options,omitempty"`
}

// CollectionTree represents a back-end collection tree instance.
type CollectionTree []*CollectionTreeEntry

// NewCollectionTree creates a new back-end collection tree instance.
func (b *Backend) NewCollectionTree(root string) (*CollectionTree, error) {
	collections := []*Collection{}
	if _, err := b.Storage().List(&collections, nil, nil, 0, 0, false); err != nil {
		return nil, err
	}

	collectionsCache := map[string]*Collection{}
	for _, c := range collections {
		collectionsCache[c.ID] = c
	}

	entries := map[string]*CollectionTreeEntry{}
	for _, c := range collections {
		if c.Template {
			continue
		}

		c.backend = b
		c.Resolve(collectionsCache)
		c.Expand(nil)

		// Fill collections tree
		if _, ok := entries[c.ID]; !ok {
			entries[c.ID] = c.treeEntry()
		}

		if c.HasParent() {
			parentID := *c.ParentID

			if _, ok := entries[parentID]; !ok {
				c.Parent.backend = b
				c.Parent.Resolve(collectionsCache)
				c.Parent.Expand(nil)

				entries[parentID] = c.Parent.treeEntry()
			}

			*entries[parentID].Children = append(*entries[parentID].Children, entries[c.ID])
		}
	}

	tree := &CollectionTree{}
	for _, entry := range entries {
		if entry.Parent == root {
			*tree = append(*tree, entry)
			sort.Sort(entry.Children)
		}
	}

	sort.Sort(tree)

	return tree, nil
}

func (c CollectionTree) Len() int {
	return len(c)
}

func (c CollectionTree) Less(i, j int) bool {
	return natsort.Compare(c[i].Label, c[j].Label)
}

func (c CollectionTree) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

// CollectionTreeEntry represents a back-end collections tree entry instance.
type CollectionTreeEntry struct {
	ID       string          `json:"id,omitempty"`
	Label    string          `json:"label,omitempty"`
	Parent   string          `json:"parent,omitempty"`
	Children *CollectionTree `json:"children,omitempty"`
}
<commit_msg>Simplify collection graphs titles expansion<commit_after>package backend

import (
	"facette/template"
	"sort"

	"github.com/facette/maputil"
	"github.com/facette/natsort"
	"github.com/facette/sliceutil"
	"github.com/jinzhu/gorm"
)

// Collection represents a back-end collection item instance.
type Collection struct {
	Item
	Entries    []*CollectionEntry `json:"entries,omitempty"`
	Link       *Collection        `json:"-"`
	LinkID     *string            `gorm:"column:link;type:varchar(36) DEFAULT NULL REFERENCES collections (id) ON DELETE CASCADE ON UPDATE CASCADE" json:"link,omitempty"`
	Attributes maputil.Map        `gorm:"type:text" json:"attributes,omitempty"`
	Alias      *string            `gorm:"type:varchar(128);unique_index" json:"alias,omitempty"`
	Options    maputil.Map        `gorm:"type:text" json:"options,omitempty"`
	Parent     *Collection        `json:"-"`
	ParentID   *string            `gorm:"column:parent;type:varchar(36) DEFAULT NULL REFERENCES collections (id) ON DELETE SET NULL ON UPDATE SET NULL" json:"parent,omitempty"`
	Template   bool               `gorm:"not null" json:"template"`

	resolved bool
	expanded bool
}

// NewCollection creates a new collection item instance.
func (b *Backend) NewCollection() *Collection {
	return &Collection{Item: Item{backend: b}}
}

// BeforeSave handles the ORM 'BeforeSave' callback.
func (c *Collection) BeforeSave(scope *gorm.Scope) error {
	if err := c.Item.BeforeSave(scope); err != nil {
		return err
	} else if c.Alias != nil && *c.Alias != "" && !nameRegexp.MatchString(*c.Alias) {
		return ErrInvalidAlias
	}

	for idx, entry := range c.Entries {
		entry.Index = idx + 1
	}

	// Ensure optional fields are null if empty
	if c.LinkID != nil && *c.LinkID == "" {
		scope.SetColumn("LinkID", nil)
	}

	if c.Alias != nil && *c.Alias == "" {
		scope.SetColumn("Alias", nil)
	}

	if c.ParentID != nil && *c.ParentID == "" {
		scope.SetColumn("ParentID", nil)
	}

	return nil
}

// Clone returns a clone of the collection item instance.
func (c *Collection) Clone() *Collection {
	clone := &Collection{}
	*clone = *c

	clone.Entries = make([]*CollectionEntry, len(c.Entries))
	for i, entry := range c.Entries {
		clone.Entries[i] = &CollectionEntry{}
		*clone.Entries[i] = *entry

		if entry.Options != nil {
			clone.Entries[i].Options = entry.Options.Clone()
		}
	}

	if c.Attributes != nil {
		clone.Attributes = c.Attributes.Clone()
	}

	if c.Options != nil {
		clone.Options = c.Options.Clone()
	}

	if c.Link != nil {
		clone.Link = &Collection{}
		*clone.Link = *c.Link
	}

	if c.Parent != nil {
		clone.Parent = &Collection{}
		*clone.Parent = *c.Parent
	}

	return clone
}

// Expand expands the collection item instance using its linked instance.
func (c *Collection) Expand(attrs maputil.Map) error {
	var err error

	if c.expanded {
		return nil
	}

	if len(attrs) > 0 {
		c.Attributes.Merge(attrs, true)
	}

	if c.backend != nil && c.LinkID != nil && *c.LinkID != "" {
		if err := c.Resolve(nil); err != nil {
			return err
		}

		// Expand template and applies current collection's attributes and options
		tmpl := c.Link.Clone()
		tmpl.ID = c.ID
		tmpl.Attributes.Merge(c.Attributes, true)
		tmpl.Options.Merge(c.Options, true)
		tmpl.Template = false

		if c.ParentID != nil && *c.ParentID != "" {
			tmpl.Parent = c.Parent.Clone()
			tmpl.ParentID = c.ParentID
		}

		if title, ok := tmpl.Options["title"].(string); ok {
			if tmpl.Options["title"], err = template.Expand(title, tmpl.Attributes); err != nil {
				return err
			}
		}

		*c = *tmpl
	}

	if len(c.Entries) > 0 {
		if !c.resolved {
			if err := c.Resolve(nil); err != nil {
				return err
			}
		}

		for _, entry := range c.Entries {
			attrs := maputil.Map{}
			attrs.Merge(c.Attributes, true)
			attrs.Merge(entry.Attributes, true)

			entry.Graph.Expand(attrs)

			if v, ok := entry.Graph.Options["title"]; ok {
				if entry.Options == nil {
					entry.Options = maputil.Map{}
				}
				entry.Options["title"] = v
			}
		}
	}

	c.expanded = true

	return nil
}

// HasParent returns whether or not the collection item has a parent instance.
func (c *Collection) HasParent() bool {
	return c.ParentID != nil && *c.ParentID != ""
}

// Resolve resolves both the collection item linked and parent instances.
func (c *Collection) Resolve(cache map[string]*Collection) error {
	if c.resolved {
		return nil
	} else if c.backend == nil {
		return ErrUnresolvableItem
	}

	if c.LinkID != nil && *c.LinkID != "" {
		if cache != nil {
			if link, ok := cache[*c.LinkID]; ok {
				c.Link = link
			}
		} else {
			c.Link = c.backend.NewCollection()
			if err := c.backend.Storage().Get("id", *c.LinkID, c.Link, true); err != nil {
				return err
			}
		}
	}

	if c.ParentID != nil && *c.ParentID != "" {
		if cache != nil {
			if parent, ok := cache[*c.ParentID]; ok {
				c.Parent = parent
			}
		} else {
			c.Parent = c.backend.NewCollection()
			if err := c.backend.Storage().Get("id", *c.ParentID, c.Parent, true); err != nil {
				return err
			}
		}
	}

	// Resolve associated graphs if any
	ids := []string{}
	for _, entry := range c.Entries {
		if !sliceutil.Has(ids, entry.GraphID) {
			ids = append(ids, entry.GraphID)
		}
	}

	if len(ids) > 0 {
		graphs := []*Graph{}
		if err := c.backend.Storage().Get("id", ids, &graphs, false); err != nil {
			return err
		}

		graphsMap := map[string]*Graph{}
		for _, g := range graphs {
			graphsMap[g.ID] = g
		}

		for _, entry := range c.Entries {
			if g, ok := graphsMap[entry.GraphID]; ok {
				entry.Graph = g.Clone()
			}
		}
	}

	c.resolved = true

	return nil
}

func (c *Collection) treeEntry() *CollectionTreeEntry {
	entry := &CollectionTreeEntry{
		ID:       c.ID,
		Children: &CollectionTree{},
	}

	if c.HasParent() {
		entry.Parent = *c.ParentID
	}

	// Use title as label if any or fallback to collection name
	if title, ok := c.Options["title"].(string); ok && title != "" {
		entry.Label = title
	} else {
		entry.Label = c.Name
	}

	return entry
}

// CollectionEntry represents a back-end collection entry instance.
type CollectionEntry struct {
	Index        int         `gorm:"type:int NOT NULL;primary_key" json:"-"`
	Collection   *Collection `json:"-"`
	CollectionID string      `gorm:"column:collection;type:varchar(36) NOT NULL REFERENCES collections (id) ON DELETE CASCADE ON UPDATE CASCADE;primary_key" json:"-"`
	Graph        *Graph      `json:"-"`
	GraphID      string      `gorm:"column:graph;type:varchar(36) NOT NULL REFERENCES graphs (id) ON DELETE CASCADE ON UPDATE CASCADE;primary_key" json:"graph"`
	Attributes   maputil.Map `gorm:"type:text" json:"attributes,omitempty"`
	Options      maputil.Map `gorm:"type:text" json:"options,omitempty"`
}

// CollectionTree represents a back-end collection tree instance.
type CollectionTree []*CollectionTreeEntry

// NewCollectionTree creates a new back-end collection tree instance.
func (b *Backend) NewCollectionTree(root string) (*CollectionTree, error) {
	collections := []*Collection{}
	if _, err := b.Storage().List(&collections, nil, nil, 0, 0, false); err != nil {
		return nil, err
	}

	collectionsCache := map[string]*Collection{}
	for _, c := range collections {
		collectionsCache[c.ID] = c
	}

	entries := map[string]*CollectionTreeEntry{}
	for _, c := range collections {
		if c.Template {
			continue
		}

		c.backend = b
		c.Resolve(collectionsCache)
		c.Expand(nil)

		// Fill collections tree
		if _, ok := entries[c.ID]; !ok {
			entries[c.ID] = c.treeEntry()
		}

		if c.HasParent() {
			parentID := *c.ParentID

			if _, ok := entries[parentID]; !ok {
				c.Parent.backend = b
				c.Parent.Resolve(collectionsCache)
				c.Parent.Expand(nil)

				entries[parentID] = c.Parent.treeEntry()
			}

			*entries[parentID].Children = append(*entries[parentID].Children, entries[c.ID])
		}
	}

	tree := &CollectionTree{}
	for _, entry := range entries {
		if entry.Parent == root {
			*tree = append(*tree, entry)
			sort.Sort(entry.Children)
		}
	}

	sort.Sort(tree)

	return tree, nil
}

func (c CollectionTree) Len() int {
	return len(c)
}

func (c CollectionTree) Less(i, j int) bool {
	return natsort.Compare(c[i].Label, c[j].Label)
}

func (c CollectionTree) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

// CollectionTreeEntry represents a back-end collections tree entry instance.
type CollectionTreeEntry struct {
	ID       string          `json:"id,omitempty"`
	Label    string          `json:"label,omitempty"`
	Parent   string          `json:"parent,omitempty"`
	Children *CollectionTree `json:"children,omitempty"`
}
<|endoftext|>