<commit_before>package demeter

import (
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"

	"golang.org/x/tools/go/ast/astutil"
)

// Violation represents a violation of the Law of Demeter.
type Violation struct {
	Filename string
	Line     int
	Col      int
}

// AnalyzeFile analyzes a single file and returns the violations.
func AnalyzeFile(filename string) ([]*Violation, error) {
	fset := token.NewFileSet()

	f, err := parser.ParseFile(fset, filename, nil, 0)
	if err != nil {
		return nil, err
	}

	return analyzeFile(filename, f, nil, fset)
}

// AnalyzeDir analyzes a directory (non-recursively) and returns the violations.
func AnalyzeDir(dirname string) ([]*Violation, error) {
	fset := token.NewFileSet()

	packages, err := parser.ParseDir(fset, dirname, nil, 0)
	if err != nil {
		return nil, err
	}

	violations := []*Violation{}
	for _, p := range packages {
		for filename, f := range p.Files {
			v, err := analyzeFile(filename, f, nil, fset)
			if err != nil {
				return nil, err
			}
			violations = append(violations, v...)
		}
	}

	return violations, nil
}

func analyzeFile(filename string, f *ast.File, src interface{}, fset *token.FileSet) ([]*Violation, error) {
	info := &types.Info{
		// TODO: check if we can remove any
		Types:      make(map[ast.Expr]types.TypeAndValue),
		Defs:       make(map[*ast.Ident]types.Object),
		Uses:       make(map[*ast.Ident]types.Object),
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
		Implicits:  make(map[ast.Node]types.Object),
	}

	config := &types.Config{
		Error: func(err error) {
			fmt.Println(err)
		},
		Importer: importer.Default(),
	}

	_, err := config.Check(filename, fset, []*ast.File{f}, info)
	if err != nil {
		return nil, err
	}

	visitor := newAstVisitor(f, fset, info)
	ast.Walk(visitor, f)

	return visitor.violations, nil
}

type astVisitor struct {
	info       *types.Info
	f          *ast.File
	fset       *token.FileSet
	violations []*Violation
}

func newAstVisitor(f *ast.File, fset *token.FileSet, info *types.Info) *astVisitor {
	return &astVisitor{
		info:       info,
		f:          f,
		fset:       fset,
		violations: []*Violation{},
	}
}

func (v *astVisitor) Visit(node ast.Node) ast.Visitor {
	if n, ok := node.(*ast.CallExpr); ok {
		return v.visitCallExpr(n)
	}
	return v
}

func (v *astVisitor) visitCallExpr(callExpr *ast.CallExpr) (visitor ast.Visitor) {
	visitor = v

	fun, ok := callExpr.Fun.(*ast.SelectorExpr)
	if !ok {
		// Package-local function call
		return
	}

	switch call := v.info.ObjectOf(fun.Sel).(type) {
	case *types.Var:
		// TODO
	case *types.Func:
		callRecv := call.Type().(*types.Signature).Recv()
		if callRecv == nil {
			// Not a method call
			return
		}

		funcDecl := v.enclosingFuncDecl(fun)
		if funcDecl.Recv == nil {
			// Not inside a method
			return
		}

		if _, ok := fun.X.(*ast.CallExpr); ok {
			// Chained method call
			v.addViolation(callExpr)
			return
		}

		funcDeclRecv := funcDecl.Recv.List[0].Names[0]
		if callRecv.Name() == funcDeclRecv.Name {
			// Call on O itself
			return
		}

		x, sel := v.lookupXSel(fun)

		if funcDecl.Type.Params.NumFields() > 0 {
			for _, param := range funcDecl.Type.Params.List {
				name := param.Names[0].Name
				if name == sel.Name {
					// Call on one of m's parameters
					return
				}
			}
		}

		funcDeclScope := v.info.ObjectOf(funcDecl.Name).(*types.Func).Scope()
		if funcDeclScope.Lookup(sel.Name) != nil {
			// Call on object created in m
			// XXX: check if object *instantiated* in m, as opposed
			// to just declared
			return
		}

		for _, name := range exprToIdent(funcDecl.Recv.List[0].Type).Obj.Decl.(*ast.TypeSpec).Type.(*ast.StructType).Fields.List {
			if name.Names[0].Name == sel.Name {
				if x != nil && x.Name == funcDeclRecv.Name {
					// Call on one of O's direct components
					return
				}
				break
			}
		}

		selVar := v.info.ObjectOf(sel).(*types.Var)
		if selVar.Pkg().Scope() == selVar.Parent() {
			// Call on global object
			return
		}

		v.addViolation(callExpr)
	}

	return
}

func (v *astVisitor) addViolation(expr *ast.CallExpr) {
	fpos := v.fset.Position(expr.Pos())
	violation := &Violation{
		Filename: fpos.Filename,
		Line:     fpos.Line,
		Col:      fpos.Column,
	}
	v.violations = append(v.violations, violation)
}

func (v *astVisitor) enclosingFuncDecl(expr ast.Node) *ast.FuncDecl {
	path, _ := astutil.PathEnclosingInterval(v.f, expr.Pos(), expr.End())
	for _, n := range path {
		// fmt.Printf("n: %#v\n", n)
		if funcDecl, ok := n.(*ast.FuncDecl); ok {
			return funcDecl
		}
	}
	return nil
}

func (v *astVisitor) lookupXSel(sexpr *ast.SelectorExpr) (retx, retsel *ast.Ident) {
	if ident, ok := sexpr.X.(*ast.Ident); ok {
		retsel = ident
		return
	}

	sexpr = sexpr.X.(*ast.SelectorExpr)
	retsel = sexpr.Sel

	ident, ok := sexpr.X.(*ast.Ident)
	if ok {
		retx = ident
	}
	for ; !ok; retx = sexpr.Sel {
		sexpr = sexpr.X.(*ast.SelectorExpr)
		_, ok = sexpr.X.(*ast.Ident)
	}

	return
}

func exprToIdent(expr ast.Expr) *ast.Ident {
	if ident, ok := expr.(*ast.Ident); ok {
		return ident
	}
	return expr.(*ast.StarExpr).X.(*ast.Ident)
}
<commit_msg>Detect call on objects instantiated outside m<commit_after>package demeter

import (
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"

	"golang.org/x/tools/go/ast/astutil"
)

// Violation represents a violation of the Law of Demeter.
type Violation struct {
	Filename string
	Line     int
	Col      int
}

// AnalyzeFile analyzes a single file and returns the violations.
func AnalyzeFile(filename string) ([]*Violation, error) {
	fset := token.NewFileSet()

	f, err := parser.ParseFile(fset, filename, nil, 0)
	if err != nil {
		return nil, err
	}

	return analyzeFile(filename, f, nil, fset)
}

// AnalyzeDir analyzes a directory (non-recursively) and returns the violations.
func AnalyzeDir(dirname string) ([]*Violation, error) {
	fset := token.NewFileSet()

	packages, err := parser.ParseDir(fset, dirname, nil, 0)
	if err != nil {
		return nil, err
	}

	violations := []*Violation{}
	for _, p := range packages {
		for filename, f := range p.Files {
			v, err := analyzeFile(filename, f, nil, fset)
			if err != nil {
				return nil, err
			}
			violations = append(violations, v...)
		}
	}

	return violations, nil
}

func analyzeFile(filename string, f *ast.File, src interface{}, fset *token.FileSet) ([]*Violation, error) {
	info := &types.Info{
		// TODO: check if we can remove any
		Types:      make(map[ast.Expr]types.TypeAndValue),
		Defs:       make(map[*ast.Ident]types.Object),
		Uses:       make(map[*ast.Ident]types.Object),
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
		Implicits:  make(map[ast.Node]types.Object),
	}

	config := &types.Config{
		Error: func(err error) {
			fmt.Println(err)
		},
		Importer: importer.Default(),
	}

	_, err := config.Check(filename, fset, []*ast.File{f}, info)
	if err != nil {
		return nil, err
	}

	visitor := newAstVisitor(f, fset, info)
	ast.Walk(visitor, f)

	return visitor.violations, nil
}

type astVisitor struct {
	info       *types.Info
	f          *ast.File
	fset       *token.FileSet
	violations []*Violation
}

func newAstVisitor(f *ast.File, fset *token.FileSet, info *types.Info) *astVisitor {
	return &astVisitor{
		info:       info,
		f:          f,
		fset:       fset,
		violations: []*Violation{},
	}
}

func (v *astVisitor) Visit(node ast.Node) ast.Visitor {
	if n, ok := node.(*ast.CallExpr); ok {
		return v.visitCallExpr(n)
	}
	return v
}

func (v *astVisitor) visitCallExpr(callExpr *ast.CallExpr) (visitor ast.Visitor) {
	visitor = v

	fun, ok := callExpr.Fun.(*ast.SelectorExpr)
	if !ok {
		// Package-local function call
		return
	}

	switch call := v.info.ObjectOf(fun.Sel).(type) {
	case *types.Var:
		// TODO
	case *types.Func:
		callRecv := call.Type().(*types.Signature).Recv()
		if callRecv == nil {
			// Not a method call
			return
		}

		funcDecl := v.enclosingFuncDecl(fun)
		if funcDecl.Recv == nil {
			// Not inside a method
			return
		}

		if _, ok := fun.X.(*ast.CallExpr); ok {
			// Chained method call
			v.addViolation(callExpr)
			return
		}

		funcDeclRecv := funcDecl.Recv.List[0].Names[0]
		if callRecv.Name() == funcDeclRecv.Name {
			// Call on O itself
			return
		}

		x, sel := v.lookupXSel(fun)

		if funcDecl.Type.Params.NumFields() > 0 {
			for _, param := range funcDecl.Type.Params.List {
				name := param.Names[0].Name
				if name == sel.Name {
					// Call on one of m's parameters
					return
				}
			}
		}

		if sel.Obj != nil {
			if assignStmt, ok := sel.Obj.Decl.(*ast.AssignStmt); ok {
				if _, ok = assignStmt.Rhs[0].(*ast.CallExpr); ok {
					// Call on object initialized in m
					// but instantiated elsewere
					v.addViolation(callExpr)
					return
				}
			}
		}

		funcDeclScope := v.info.ObjectOf(funcDecl.Name).(*types.Func).Scope()
		if funcDeclScope.Lookup(sel.Name) != nil {
			// Call on object created in m
			return
		}

		for _, name := range exprToIdent(funcDecl.Recv.List[0].Type).Obj.Decl.(*ast.TypeSpec).Type.(*ast.StructType).Fields.List {
			if name.Names[0].Name == sel.Name {
				if x != nil && x.Name == funcDeclRecv.Name {
					// Call on one of O's direct components
					return
				}
				break
			}
		}

		selVar := v.info.ObjectOf(sel).(*types.Var)
		if selVar.Pkg().Scope() == selVar.Parent() {
			// Call on global object
			return
		}

		v.addViolation(callExpr)
	}

	return
}

func (v *astVisitor) addViolation(expr *ast.CallExpr) {
	fpos := v.fset.Position(expr.Pos())
	violation := &Violation{
		Filename: fpos.Filename,
		Line:     fpos.Line,
		Col:      fpos.Column,
	}
	v.violations = append(v.violations, violation)
}

func (v *astVisitor) enclosingFuncDecl(expr ast.Node) *ast.FuncDecl {
	path, _ := astutil.PathEnclosingInterval(v.f, expr.Pos(), expr.End())
	for _, n := range path {
		// fmt.Printf("n: %#v\n", n)
		if funcDecl, ok := n.(*ast.FuncDecl); ok {
			return funcDecl
		}
	}
	return nil
}

func (v *astVisitor) lookupXSel(sexpr *ast.SelectorExpr) (retx, retsel *ast.Ident) {
	if ident, ok := sexpr.X.(*ast.Ident); ok {
		retsel = ident
		return
	}

	sexpr = sexpr.X.(*ast.SelectorExpr)
	retsel = sexpr.Sel

	ident, ok := sexpr.X.(*ast.Ident)
	if ok {
		retx = ident
	}
	for ; !ok; retx = sexpr.Sel {
		sexpr = sexpr.X.(*ast.SelectorExpr)
		_, ok = sexpr.X.(*ast.Ident)
	}

	return
}

func exprToIdent(expr ast.Expr) *ast.Ident {
	if ident, ok := expr.(*ast.Ident); ok {
		return ident
	}
	return expr.(*ast.StarExpr).X.(*ast.Ident)
}
<|endoftext|>


<commit_before>// Copyright 2011 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 main

func init() {
	addBuildFlagsNX(cmdVet)
}

var cmdVet = &Command{
	Run:       runVet,
	UsageLine: "vet [-n] [-x] [packages]",
	Short:     "run go tool vet on packages",
	Long: `
Vet runs the Go vet command on the packages named by the import paths.

For more about vet, see 'godoc vet'.
For more about specifying packages, see 'go help packages'.

To run the vet tool with specific options, run 'go tool vet'.

The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.

See also: go fmt, go fix.
	`,
}

func runVet(cmd *Command, args []string) {
	for _, pkg := range packages(args) {
		// Use pkg.gofiles instead of pkg.Dir so that
		// the command only applies to this package,
		// not to packages in subdirectories.
		run(tool("vet"), relPaths(stringList(pkg.allgofiles, pkg.IgnoredGoFiles)))
	}
}
<commit_msg>cmd/go: fix vet<commit_after>// Copyright 2011 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 main

func init() {
	addBuildFlagsNX(cmdVet)
}

var cmdVet = &Command{
	Run:       runVet,
	UsageLine: "vet [-n] [-x] [packages]",
	Short:     "run go tool vet on packages",
	Long: `
Vet runs the Go vet command on the packages named by the import paths.

For more about vet, see 'godoc vet'.
For more about specifying packages, see 'go help packages'.

To run the vet tool with specific options, run 'go tool vet'.

The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.

See also: go fmt, go fix.
	`,
}

func runVet(cmd *Command, args []string) {
	for _, pkg := range packages(args) {
		// Use pkg.gofiles instead of pkg.Dir so that
		// the command only applies to this package,
		// not to packages in subdirectories.
		run(tool("vet"), relPaths(pkg.allgofiles))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sync"
	"time"
)

var b map[string]float64
var mutex sync.Mutex
var count int

func main() {
	b = make(map[string]float64)
	beginTime := time.Now()
	fmt.Println(binomal(100, 50, 0.5))
	fmt.Println("耗时", time.Since(beginTime))
	fmt.Println(count, len(b))
}

func binomal(n, k int, p float64) float64 {
	if n < k {
		return 0
	}
	if n == 0 && k == 0 {
		return 1
	}

	if n < 0 || k < 0 {
		return 0
	}

	key := fmt.Sprintf("%d-%d-%f", n, k, p)
	if v, ok := b[key]; ok {
		return v
	}
	v := (1-p)*binomal(n-1, k, p) + p*binomal(n-1, k-1, p)

	{
		mutex.Lock()
		b[key] = v
		count++
		mutex.Unlock()
	}
	return v

}
<commit_msg>提供了不带锁的版本。<commit_after>package main

import (
	"fmt"
	"time"
)

var b map[string]float64

func main() {
	b = make(map[string]float64)
	beginTime := time.Now()
	fmt.Println(binomal(100, 50, 0.5))
	fmt.Println("耗时", time.Since(beginTime))
}

func binomal(n, k int, p float64) float64 {
	if n == 0 && k == 0 {
		return 1
	}

	if n < 0 || k < 0 || n < k {
		return 0
	}

	key := fmt.Sprintf("%d-%d-%f", n, k, p)
	if v, ok := b[key]; ok {
		return v
	}

	v := (1-p)*binomal(n-1, k, p) + p*binomal(n-1, k-1, p)
	b[key] = v
	return v

}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/boltdb/bolt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"time"
	"unicode"
)

const (
	TK_UNDEFINED = iota
	TK_LS
	TK_CLEAR
	TK_HELP
	TK_REPLAY
	TK_SHOW
	TK_NUM
	TK_STRING
	TK_COUNT
	TK_USERS
	TK_BIND
	TK_INFO
	TK_LPAREN
	TK_RPAREN
	TK_COMMA
	TK_DURATION
	TK_EOF
)

var cmds = map[string]int{
	"ls":    TK_LS,
	"help":  TK_HELP,
	"clear": TK_CLEAR,

	"users": TK_USERS,
	"info":  TK_INFO,

	"bind":     TK_BIND,
	"count":    TK_COUNT,
	"duration": TK_DURATION,
	"show":     TK_SHOW,
	"replay":   TK_REPLAY,
}

type token struct {
	typ     int
	literal string
	num     int
}

// set ts=8
var help = `REDO Replay Tool
Commands:

list all database files(sorted by time):
> ls
> help		-- print this text
> clear 	-- clear all bindings

Global operations to file:
> users(1)	-- print all users of file#1
> info(2)	-- print summary of file#2

Bind Operations to user:
> bind(1, 1234)	-- all operations below are binded to a file#1 & user#1234
> count			-- print number of records of the user
> show			-- show all elements of the user
> replay("mongodb://172.17.42.1/mydb")	-- replay all changes of the user

Bind operations to duration:
> duration("2015-10-28T14:53:27", "2015-10-29T14:53:27")
(all operations below are binded to this duration)
> count		-- print number of records of the user in this duration
> show		-- show all elements of the user in this duration
> replay("mongodb://172.17.42.1/mydb")	-- replay all changes of the user in this duration
`

type ToolBox struct {
	dbs          []*bolt.DB // all opened boltdb
	fileid       int        // current selected db
	userid       int        // current selected userid
	duration_a   time.Time
	duration_b   time.Time
	duration_set bool
	mgo_url      string
	cmd_reader   *bytes.Buffer // cmds
}

func (t *ToolBox) init(dir string) {
	t.cmd_clear()
	files, err := filepath.Glob(dir + "/*.RDO")
	if err != nil {
		log.Println(err)
		os.Exit(-1)
	}

	for _, file := range files {
		db, err := bolt.Open(file, 0600, &bolt.Options{Timeout: 1 * time.Second})
		if err != nil {
			log.Println(err)
			continue
		}
		t.dbs = append(t.dbs, db)
	}
}

//////////////////////////////////////////
// parser
func (t *ToolBox) next() *token {
	var r rune
	var err error
	for {
		r, _, err = t.cmd_reader.ReadRune()
		if err == io.EOF {
			return &token{typ: TK_EOF}
		} else if unicode.IsSpace(r) {
			continue
		}
		break
	}

	if unicode.IsLetter(r) {
		var runes []rune
		for {
			runes = append(runes, r)
			r, _, err = t.cmd_reader.ReadRune()
			if err == io.EOF {
				break
			} else if unicode.IsLetter(r) {
				continue
			} else {
				t.cmd_reader.UnreadRune()
				break
			}
		}

		t := &token{}
		t.literal = string(runes)
		t.typ = cmds[t.literal]
		return t
	} else if r == '"' { // quoted string
		var runes []rune
		for {
			r, _, err = t.cmd_reader.ReadRune()
			if err == io.EOF {
				break
			} else if r != '"' { // read until '"'
				runes = append(runes, r)
				continue
			} else {
				break
			}
		}
		t := &token{}
		t.literal = string(runes)
		t.typ = TK_STRING
		return t
	} else if unicode.IsDigit(r) {
		var runes []rune
		for {
			runes = append(runes, r)
			r, _, err = t.cmd_reader.ReadRune()
			if err == io.EOF {
				break
			} else if unicode.IsDigit(r) {
				continue
			} else {
				t.cmd_reader.UnreadRune()
				break
			}
		}

		t := &token{}
		t.num, _ = strconv.Atoi(string(runes))
		t.typ = TK_NUM
		return t
	} else if r == '(' {
		return &token{typ: TK_LPAREN}
	} else if r == ')' {
		return &token{typ: TK_RPAREN}
	} else if r == ',' {
		return &token{typ: TK_COMMA}
	}
	return &token{}
}

func (t *ToolBox) match(typ int) *token {
	tk := t.next()
	if tk.typ != typ {
		panic("syntax error")
	}
	return tk
}

func (t *ToolBox) parse_exec(cmd string) {
	defer func() {
		if x := recover(); x != nil {
			fmt.Println(x, cmd)
		}
	}()
	t.cmd_reader = bytes.NewBufferString(cmd)
	tk := t.next()
	switch tk.typ {
	case TK_LS:
		t.cmd_ls()
	case TK_HELP:
		t.cmd_help()
	case TK_CLEAR:
		t.cmd_clear()

	case TK_USERS:
		t.cmd_users()
	case TK_INFO:
		t.cmd_info()

	case TK_DURATION:
		t.cmd_duration()
	case TK_BIND:
		t.cmd_bind()
	case TK_COUNT:
		t.cmd_count()
	case TK_SHOW:
		t.cmd_show()
	case TK_REPLAY:
		t.cmd_replay()
	default:
		fmt.Println("unkown command:", cmd)
	}
}
<commit_msg>update<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/boltdb/bolt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"time"
	"unicode"
)

const (
	TK_UNDEFINED = iota
	TK_LS
	TK_CLEAR
	TK_HELP
	TK_REPLAY
	TK_SHOW
	TK_NUM
	TK_STRING
	TK_COUNT
	TK_USERS
	TK_BIND
	TK_INFO
	TK_LPAREN
	TK_RPAREN
	TK_COMMA
	TK_DURATION
	TK_EOF
)

var cmds = map[string]int{
	"ls":    TK_LS,
	"help":  TK_HELP,
	"clear": TK_CLEAR,

	"users": TK_USERS,
	"info":  TK_INFO,

	"bind":     TK_BIND,
	"count":    TK_COUNT,
	"duration": TK_DURATION,
	"show":     TK_SHOW,
	"replay":   TK_REPLAY,
}

type token struct {
	typ     int
	literal string
	num     int
}

// set ts=8
var help = `REDO Replay Tool
Commands:

list all database files(sorted by time):
> ls
> help		-- print this text
> clear 	-- clear all bindings

Global operations to file:
> users(1)	-- print all users of file#1
> info(2)	-- print summary of file#2

Bind Operations to user:
> bind(1, 1234)	-- all operations below are binded to a file#1 & user#1234
> count			-- print number of records of the user
> show			-- show all elements of the user
> replay("mongodb://172.17.42.1/mydb")	-- replay all changes of the user

Bind operations to duration:
> duration("2015-10-28T14:53:27", "2015-10-29T14:53:27")
(all operations below are binded to this duration)
> count		-- print number of records of the user in this duration
> show		-- show all elements of the user in this duration
> replay("mongodb://172.17.42.1/mydb")	-- replay all changes of the user in this duration
`

type ToolBox struct {
	dbs          []*bolt.DB // all opened boltdb
	fileid       int        // current selected db
	userid       int        // current selected userid
	duration_a   time.Time
	duration_b   time.Time
	duration_set bool
	mgo_url      string
	cmd_reader   *bytes.Buffer // cmds
}

func (t *ToolBox) init(dir string) {
	t.cmd_clear()
	files, err := filepath.Glob(dir + "/*.RDO")
	if err != nil {
		log.Println(err)
		os.Exit(-1)
	}

	for _, file := range files {
		db, err := bolt.Open(file, 0600, &bolt.Options{Timeout: 1 * time.Second, ReadOnly: true})
		if err != nil {
			log.Println(err)
			continue
		}
		t.dbs = append(t.dbs, db)
	}
}

//////////////////////////////////////////
// parser
func (t *ToolBox) next() *token {
	var r rune
	var err error
	for {
		r, _, err = t.cmd_reader.ReadRune()
		if err == io.EOF {
			return &token{typ: TK_EOF}
		} else if unicode.IsSpace(r) {
			continue
		}
		break
	}

	if unicode.IsLetter(r) {
		var runes []rune
		for {
			runes = append(runes, r)
			r, _, err = t.cmd_reader.ReadRune()
			if err == io.EOF {
				break
			} else if unicode.IsLetter(r) {
				continue
			} else {
				t.cmd_reader.UnreadRune()
				break
			}
		}

		t := &token{}
		t.literal = string(runes)
		t.typ = cmds[t.literal]
		return t
	} else if r == '"' { // quoted string
		var runes []rune
		for {
			r, _, err = t.cmd_reader.ReadRune()
			if err == io.EOF {
				break
			} else if r != '"' { // read until '"'
				runes = append(runes, r)
				continue
			} else {
				break
			}
		}
		t := &token{}
		t.literal = string(runes)
		t.typ = TK_STRING
		return t
	} else if unicode.IsDigit(r) {
		var runes []rune
		for {
			runes = append(runes, r)
			r, _, err = t.cmd_reader.ReadRune()
			if err == io.EOF {
				break
			} else if unicode.IsDigit(r) {
				continue
			} else {
				t.cmd_reader.UnreadRune()
				break
			}
		}

		t := &token{}
		t.num, _ = strconv.Atoi(string(runes))
		t.typ = TK_NUM
		return t
	} else if r == '(' {
		return &token{typ: TK_LPAREN}
	} else if r == ')' {
		return &token{typ: TK_RPAREN}
	} else if r == ',' {
		return &token{typ: TK_COMMA}
	}
	return &token{}
}

func (t *ToolBox) match(typ int) *token {
	tk := t.next()
	if tk.typ != typ {
		panic("syntax error")
	}
	return tk
}

func (t *ToolBox) parse_exec(cmd string) {
	defer func() {
		if x := recover(); x != nil {
			fmt.Println(x, cmd)
		}
	}()
	t.cmd_reader = bytes.NewBufferString(cmd)
	tk := t.next()
	switch tk.typ {
	case TK_LS:
		t.cmd_ls()
	case TK_HELP:
		t.cmd_help()
	case TK_CLEAR:
		t.cmd_clear()

	case TK_USERS:
		t.cmd_users()
	case TK_INFO:
		t.cmd_info()

	case TK_DURATION:
		t.cmd_duration()
	case TK_BIND:
		t.cmd_bind()
	case TK_COUNT:
		t.cmd_count()
	case TK_SHOW:
		t.cmd_show()
	case TK_REPLAY:
		t.cmd_replay()
	default:
		fmt.Println("unkown command:", cmd)
	}
}
<|endoftext|>


<commit_before>package som

import "fmt"

import "errors"

//import "os"

import "github.com/gonum/matrix/mat64"

//import "github.com/esemsch/gosom/pkg/utils"

func RunDeltaTests() {
	configDeltaTests()
	gridDeltaTests()
	somDeltaTests()
}

func somDeltaTests() {
	// default config should not throw any errors
	printD(NewMap(defaultConfig(), RandInit, inputData()))
	// incorrect config
	origLcool := defaultConfig().LCool
	defaultConfig().LCool = "foobar"
	printD(NewMap(defaultConfig(), RandInit, inputData()))
	defaultConfig().LCool = origLcool
	// when nil init function, use RandInit
	printD(NewMap(defaultConfig(), nil, inputData()))
	// incorrect init matrix
	printD(NewMap(defaultConfig(), RandInit, nil))
	// incorrect number of map units
	origDims := defaultConfig().Dims
	defaultConfig().Dims = []int{0, 0}
	printD(NewMap(defaultConfig(), RandInit, inputData()))
	defaultConfig().Dims = origDims
	// init func that always returns error
	printD(NewMap(defaultConfig(), func(inMx *mat64.Dense, dims []int) (*mat64.Dense, error) { return nil, errors.New("Failed") }, inputData()))
}

func inputData() *mat64.Dense {
	data := []float64{5.1, 3.5, 1.4, 0.1,
		4.9, 3.0, 1.4, 0.2,
		4.7, 3.2, 1.3, 0.3,
		4.6, 3.1, 1.5, 0.4,
		5.0, 3.6, 1.4, 0.5}
	return mat64.NewDense(5, 4, data)
}

func gridDeltaTests() {
	printD("RandInit")

	min1, max1 := 1.2, 4.5
	min2, max2 := 3.4, 6.7
	data := []float64{min1, min2, max1, max2}
	inMx := mat64.NewDense(2, 2, data)
	rows := []int{2, 2}
	printD(inMx)

	// initialize random matrix
	printD(RandInit(inMx, rows))

	// nil input matrix
	printD(RandInit(nil, rows))
	// negative number of rows
	printD(RandInit(inMx, []int{-2, 2}))
	// empty matrix
	emptyMx := mat64.NewDense(0, 0, nil)
	printD(RandInit(emptyMx, []int{2, 3}))

}

func defaultConfig() *Config {
	return &Config{
		Dims:     []int{2, 3},
		Grid:     "planar",
		UShape:   "hexagon",
		Radius:   0,
		RCool:    "lin",
		NeighbFn: "gaussian",
		LRate:    0,
		LCool:    "lin",
	}
}

func configDeltaTests() {
	printD("Default config")
	printD(validateConfig(defaultConfig()))

	printD("Dims")
	for _, t := range [][]int{
		[]int{1},
		[]int{},
		[]int{1, 2},
		[]int{-1, 2},
	} {
		c := defaultConfig()
		c.Dims = t
		printD(validateConfig(c))
	}

	printD("Grid")
	for _, t := range []string{
		"planar",
		"foobar",
	} {
		c := defaultConfig()
		c.Grid = t
		printD(validateConfig(c))
	}

	printD("UShape")
	for _, t := range []string{
		"hexagon",
		"foobar",
		"rectangle",
	} {
		c := defaultConfig()
		c.UShape = t
		printD(validateConfig(c))
	}

	printD("Radius")
	for _, t := range []int{
		1,
		-10,
		0,
	} {
		c := defaultConfig()
		c.Radius = t
		printD(validateConfig(c))
	}

	printD("RCool")
	for _, t := range []string{
		"lin",
		"foobar",
		"exp",
		"inv",
	} {
		c := defaultConfig()
		c.RCool = t
		printD(validateConfig(c))
	}

	printD("NeighbFn")
	for _, t := range []string{
		"gaussian",
		"foobar",
		"mexican",
		"bubble",
	} {
		c := defaultConfig()
		c.NeighbFn = t
		printD(validateConfig(c))
	}

	printD("LRate")
	for _, t := range []int{
		1,
		-10,
		0,
	} {
		c := defaultConfig()
		c.LRate = t
		printD(validateConfig(c))
	}

	printD("LCool")
	for _, t := range []string{
		"lin",
		"exp",
		"foobar",
		"inv",
	} {
		c := defaultConfig()
		c.LCool = t
		printD(validateConfig(c))
	}
}

func printD(args ...interface{}) {
	fmt.Printf("%s\n", args...)
}
<commit_msg>Added delta tests for new functionality: LinInit + GridCoord<commit_after>package som

import "fmt"
import "errors"
import "github.com/gonum/matrix/mat64"

func RunDeltaTests() {
	printSection("Config")
	configDeltaTests()
	printSection("Grid")
	gridDeltaTests()
	printSection("SOM")
	somDeltaTests()
}

func somDeltaTests() {
	printSection("New Map")
	// default config should not throw any errors
	printD(NewMap(defaultConfig(), RandInit, inputData()))
	// incorrect config
	origLcool := defaultConfig().LCool
	defaultConfig().LCool = "foobar"
	printD(NewMap(defaultConfig(), RandInit, inputData()))
	defaultConfig().LCool = origLcool
	// when nil init function, use RandInit
	printD(NewMap(defaultConfig(), nil, inputData()))
	// incorrect init matrix
	printD(NewMap(defaultConfig(), RandInit, nil))
	// incorrect number of map units
	origDims := defaultConfig().Dims
	defaultConfig().Dims = []int{0, 0}
	printD(NewMap(defaultConfig(), RandInit, inputData()))
	defaultConfig().Dims = origDims
	// init func that always returns error
	printD(NewMap(defaultConfig(), func(inMx *mat64.Dense, dims []int) (*mat64.Dense, error) { return nil, errors.New("Failed") }, inputData()))

	printSection("Codebook")
	// default config should not throw any errors
	m, err := NewMap(defaultConfig(), RandInit, inputData())
	printD(m, err)
	printD(m.Codebook())
	printD(m.Codebook().Dims())

}

func inputData() *mat64.Dense {
	data := []float64{5.1, 3.5, 1.4, 0.1,
		4.9, 3.0, 1.4, 0.2,
		4.7, 3.2, 1.3, 0.3,
		4.6, 3.1, 1.5, 0.4,
		5.0, 3.6, 1.4, 0.5}
	return mat64.NewDense(5, 4, data)
}

func gridDeltaTests() {
	printSection("RandInit")

	min1, max1 := 1.2, 4.5
	min2, max2 := 3.4, 6.7
	inMx := mat64.NewDense(2, 2, []float64{min1, min2, max1, max2})
	rows := []int{2, 2}
	printD(inMx)

	// initialize random matrix
	printD(RandInit(inMx, rows))

	// nil input matrix
	printD(RandInit(nil, rows))
	// negative number of rows
	printD(RandInit(inMx, []int{-2, 2}))
	// empty matrix
	emptyMx := mat64.NewDense(0, 0, nil)
	printD(RandInit(emptyMx, []int{2, 3}))

	printSection("GridDims")

	uShape := "hexagon"
	// 1D data with more than one sample
	data := mat64.NewDense(2, 1, []float64{2, 3})
	printD(GridDims(data, uShape))
	// 2D data with one sample
	data = mat64.NewDense(1, 2, []float64{2, 3})
	printD(GridDims(data, uShape))
	// 2D+ data with more than one sample
	data = mat64.NewDense(6, 4, []float64{
		5.1, 3.5, 1.4, 0.2,
		4.9, 3.0, 1.4, 0.2,
		4.7, 3.2, 1.3, 0.2,
		4.6, 3.1, 1.5, 0.2,
		5.0, 3.6, 1.4, 0.2,
		5.4, 3.9, 1.7, 0.4,
	})
	printD(GridDims(data, uShape))
	// data matrix can't be nil
	printD(GridDims(nil, uShape))

	printSection("LinInit")
	inMx = mat64.NewDense(6, 4, []float64{
		5.1, 3.5, 1.4, 0.2,
		4.9, 3.0, 1.4, 0.2,
		4.7, 3.2, 1.3, 0.2,
		4.6, 3.1, 1.5, 0.2,
		5.0, 3.6, 1.4, 0.2,
		5.4, 3.9, 1.7, 0.4,
	})

	xDim, yDim := 5, 2
	linMx, err := LinInit(inMx, []int{xDim, yDim})
	printD(linMx, err)
	// check if the dimensions are correct munits x datadim
	printD(linMx.Dims())
	// data is nil
	printD(LinInit(nil, []int{1, 2}))
	// nil dimensions
	printD(LinInit(inMx, nil))
	// non positive dimensions supplied
	printD(LinInit(inMx, []int{-1, 2}))
	// insufficient number of samples
	inMx = mat64.NewDense(1, 2, []float64{1, 1})
	printD(LinInit(inMx, []int{5, 2}))

	printSection("GridCoords")

	// hexagon shape
	dims := []int{4, 2}
	printD(GridCoords("hexagon", dims))
	// rectangle shape
	dims = []int{3, 2}
	printD(GridCoords("rectangle", dims))
	// incorrect units shape
	printD(GridCoords("fooshape", []int{2, 2}))
	// nil dimensions
	printD(GridCoords("hexagon", nil))
	// unsupported number of dimensions
	printD(GridCoords("hexagon", []int{1, 2, 3, 4}))
	// negative plane dimensions
	printD(GridCoords("hexagon", []int{-1, 2}))

}

func defaultConfig() *Config {
	return &Config{
		Dims:     []int{2, 3},
		Grid:     "planar",
		UShape:   "hexagon",
		Radius:   0,
		RCool:    "lin",
		NeighbFn: "gaussian",
		LRate:    0,
		LCool:    "lin",
	}
}

func configDeltaTests() {
	printSection("Default config")
	printD(validateConfig(defaultConfig()))

	printSection("Dims")
	for _, t := range [][]int{
		[]int{1},
		[]int{},
		[]int{1, 2},
		[]int{-1, 2},
	} {
		c := defaultConfig()
		c.Dims = t
		printD(validateConfig(c))
	}

	printSection("Grid")
	for _, t := range []string{
		"planar",
		"foobar",
	} {
		c := defaultConfig()
		c.Grid = t
		printD(validateConfig(c))
	}

	printSection("UShape")
	for _, t := range []string{
		"hexagon",
		"foobar",
		"rectangle",
	} {
		c := defaultConfig()
		c.UShape = t
		printD(validateConfig(c))
	}

	printSection("Radius")
	for _, t := range []int{
		1,
		-10,
		0,
	} {
		c := defaultConfig()
		c.Radius = t
		printD(validateConfig(c))
	}

	printSection("RCool")
	for _, t := range []string{
		"lin",
		"foobar",
		"exp",
		"inv",
	} {
		c := defaultConfig()
		c.RCool = t
		printD(validateConfig(c))
	}

	printSection("NeighbFn")
	for _, t := range []string{
		"gaussian",
		"foobar",
		"mexican",
		"bubble",
	} {
		c := defaultConfig()
		c.NeighbFn = t
		printD(validateConfig(c))
	}

	printSection("LRate")
	for _, t := range []int{
		1,
		-10,
		0,
	} {
		c := defaultConfig()
		c.LRate = t
		printD(validateConfig(c))
	}

	printSection("LCool")
	for _, t := range []string{
		"lin",
		"exp",
		"foobar",
		"inv",
	} {
		c := defaultConfig()
		c.LCool = t
		printD(validateConfig(c))
	}
}

func printSection(section string) {
	fmt.Printf("\n====== %s ======\n", section)
}

func printD(args ...interface{}) {
	fmt.Printf("%s\n", args...)
}
<|endoftext|>


<commit_before>//
// message.go
//
package srnd

import (
/*
  "bufio"
  "bytes"
  "crypto/rand"
  "crypto/sha512"
  "encoding/hex"
  "fmt"
  "github.com/majestrate/srndv2/src/nacl"
  "io"
  "log"
  "mime"
  "mime/multipart"
  "net/textproto"
  "path/filepath"
  "time"
*/
  "fmt"
  "io"
  "strings"
  "time"

)

type ArticleHeaders map[string]string

func (self ArticleHeaders) Has(key string) bool {
  _, ok := self[key]
  return ok
}

func (self ArticleHeaders) Get(key, fallback string) string {
  val , ok := self[key]
  if ok {
    return val
  } else {
    return fallback
  }
}

type NNTPMessage interface {
  // this message's messsge id
  MessageID() string
  // the parent message's messageid if it's specified
  Reference() string
  // the newsgroup this post is in
  Newsgroup() string
  // the name of the poster
  Name() string
  // any email address associated with the post
  Email() string
  // the subject of the post
  Subject() string
  // when this was posted
  Posted() int64
  // the message from the poster
  Message() string
  // the path header
  Path() string
  // append something to path
  // return message with new path
  AppendPath(part string) NNTPMessage
  // the type of this message usually a mimetype
  ContentType() string
  // was this post a sage?
  Sage() bool
  // was this post a root post?
  OP() bool
  // all attachments
  Attachments() []NNTPAttachment
  // all headers
  Headers() ArticleHeaders
  // write out body
  WriteBody(wr io.Writer) error
}

type MessageReader interface {
  // read a message from a reader
  ReadMessage(r io.Reader) (NNTPMessage, error)
}

type MessageWriter interface {
  // write a message to a writer
  WriteMessage(nntp NNTPMessage, wr io.Writer) error
}

type MessageSigner interface {
  // sign a message, return a new message that is signed
  SignMessage(nntp NNTPMessage) (NNTPMessage, error)
}

type MessageVerifier interface {
  // verify a message, return the message that is verified
  VerifyMessage(nntp NNTPMessage) (NNTPMessage, error)
}

type nntpArticle struct {
  headers ArticleHeaders
  message string
}

// create a simple plaintext nntp message
func newPlaintextArticle(message, email, subject, name, instance, newsgroup string) NNTPMessage {
  nntp := nntpArticle{make(ArticleHeaders), message}
  nntp.headers["From"] = fmt.Sprintf("%s <%s>", name, email)
  nntp.headers["Subject"] = subject
  nntp.headers["Path"] = instance
  nntp.headers["Content-Type"] = "text/plain"
  nntp.headers["MessageID"] = genMessageID(instance)
  // posted now
  nntp.headers["Posted"] = timeNowStr()
  nntp.headers["Newsgroups"] = newsgroup

  return nntp
}

func (self nntpArticle) MessageID() string {
  return self.headers.Get("MessageID", "")
}

func (self nntpArticle) Reference() string {
  return self.headers.Get("Reference", "")
}

func (self nntpArticle) Newsgroup() string {
  return self.headers.Get("Newsgroups", "")
}

func (self nntpArticle) Name() string {
  from := self.headers.Get("From", "anonymous <a@no.n>")
  idx := strings.Index(from, " ")
  if idx > 1 {
    return from[:idx]
  }
  return "[Invalid From header]"
}

func (self nntpArticle) Email() string {
  from := self.headers.Get("From", "anonymous <a@no.n>")
  idx := strings.Index(from, " ")
  if idx > 1 {
    idx_1 := strings.Index(from[:idx], "<")
    idx_2 := strings.Index(from[:idx], ">")
    if idx_2 > 0 && idx_1 > 0 && idx_2 > idx_1 {
      return from[1+idx+idx_1:idx+idx_2]
    }
  }
  return "[Invalid From header]"

}

func (self nntpArticle) Subject() string {
  return self.headers.Get("Subject", "")
}

func (self nntpArticle) Posted() int64 {
  posted := self.headers.Get("Date", "")
  t, err := time.Parse(time.RFC1123Z, posted)
  if err == nil {
    return t.Unix()
  }
  return 0
}

func (self nntpArticle) Message() string {
  return self.message
}

func (self nntpArticle) Path() string {
  return self.headers.Get("Path", "unspecified")
}

func (self nntpArticle) Headers() ArticleHeaders {
  return self.headers
}

func (self nntpArticle) AppendPath(part string) NNTPMessage {
  if self.headers.Has("Path") {
    self.headers["Path"] = part + "!" + self.Path()
  } else {
    self.headers["Path"] = part
  }
  return self
}
func (self nntpArticle) ContentType() string {
  // assumes text/plain if unspecified
  return self.headers.Get("Content-Type", "text/plain")
}

func (self nntpArticle) Sage() bool {
  return self.headers.Has("X-Sage")
}

func (self nntpArticle) OP() bool {
  return ! self.headers.Has("Reference")
}

func (self nntpArticle) Attachments() []NNTPAttachment {
  // TODO: implement?
  return nil
}

func (self nntpArticle) WriteBody(wr io.Writer) (err error) {
  // TODO: attachments
  _, err = io.WriteString(wr, self.message)
  return
}
<commit_msg>fix message post timestamps<commit_after>//
// message.go
//
package srnd

import (
/*
  "bufio"
  "bytes"
  "crypto/rand"
  "crypto/sha512"
  "encoding/hex"
  "fmt"
  "github.com/majestrate/srndv2/src/nacl"
  "io"
  "log"
  "mime"
  "mime/multipart"
  "net/textproto"
  "path/filepath"
  "time"
*/
  "fmt"
  "io"
  "strings"
  "time"

)

type ArticleHeaders map[string]string

func (self ArticleHeaders) Has(key string) bool {
  _, ok := self[key]
  return ok
}

func (self ArticleHeaders) Get(key, fallback string) string {
  val , ok := self[key]
  if ok {
    return val
  } else {
    return fallback
  }
}

type NNTPMessage interface {
  // this message's messsge id
  MessageID() string
  // the parent message's messageid if it's specified
  Reference() string
  // the newsgroup this post is in
  Newsgroup() string
  // the name of the poster
  Name() string
  // any email address associated with the post
  Email() string
  // the subject of the post
  Subject() string
  // when this was posted
  Posted() int64
  // the message from the poster
  Message() string
  // the path header
  Path() string
  // append something to path
  // return message with new path
  AppendPath(part string) NNTPMessage
  // the type of this message usually a mimetype
  ContentType() string
  // was this post a sage?
  Sage() bool
  // was this post a root post?
  OP() bool
  // all attachments
  Attachments() []NNTPAttachment
  // all headers
  Headers() ArticleHeaders
  // write out body
  WriteBody(wr io.Writer) error
}

type MessageReader interface {
  // read a message from a reader
  ReadMessage(r io.Reader) (NNTPMessage, error)
}

type MessageWriter interface {
  // write a message to a writer
  WriteMessage(nntp NNTPMessage, wr io.Writer) error
}

type MessageSigner interface {
  // sign a message, return a new message that is signed
  SignMessage(nntp NNTPMessage) (NNTPMessage, error)
}

type MessageVerifier interface {
  // verify a message, return the message that is verified
  VerifyMessage(nntp NNTPMessage) (NNTPMessage, error)
}

type nntpArticle struct {
  headers ArticleHeaders
  message string
}

// create a simple plaintext nntp message
func newPlaintextArticle(message, email, subject, name, instance, newsgroup string) NNTPMessage {
  nntp := nntpArticle{make(ArticleHeaders), message}
  nntp.headers["From"] = fmt.Sprintf("%s <%s>", name, email)
  nntp.headers["Subject"] = subject
  nntp.headers["Path"] = instance
  nntp.headers["Content-Type"] = "text/plain"
  nntp.headers["MessageID"] = genMessageID(instance)
  // posted now
  nntp.headers["Date"] = timeNowStr()
  nntp.headers["Newsgroups"] = newsgroup

  return nntp
}

func (self nntpArticle) MessageID() string {
  return self.headers.Get("MessageID", "")
}

func (self nntpArticle) Reference() string {
  return self.headers.Get("Reference", "")
}

func (self nntpArticle) Newsgroup() string {
  return self.headers.Get("Newsgroups", "")
}

func (self nntpArticle) Name() string {
  from := self.headers.Get("From", "anonymous <a@no.n>")
  idx := strings.Index(from, " ")
  if idx > 1 {
    return from[:idx]
  }
  return "[Invalid From header]"
}

func (self nntpArticle) Email() string {
  from := self.headers.Get("From", "anonymous <a@no.n>")
  idx := strings.Index(from, " ")
  if idx > 1 {
    idx_1 := strings.Index(from[:idx], "<")
    idx_2 := strings.Index(from[:idx], ">")
    if idx_2 > 0 && idx_1 > 0 && idx_2 > idx_1 {
      return from[1+idx+idx_1:idx+idx_2]
    }
  }
  return "[Invalid From header]"

}

func (self nntpArticle) Subject() string {
  return self.headers.Get("Subject", "")
}

func (self nntpArticle) Posted() int64 {
  posted := self.headers.Get("Date", "")
  t, err := time.Parse(time.RFC1123Z, posted)
  if err == nil {
    return t.Unix()
  }
  return 0
}

func (self nntpArticle) Message() string {
  return self.message
}

func (self nntpArticle) Path() string {
  return self.headers.Get("Path", "unspecified")
}

func (self nntpArticle) Headers() ArticleHeaders {
  return self.headers
}

func (self nntpArticle) AppendPath(part string) NNTPMessage {
  if self.headers.Has("Path") {
    self.headers["Path"] = part + "!" + self.Path()
  } else {
    self.headers["Path"] = part
  }
  return self
}
func (self nntpArticle) ContentType() string {
  // assumes text/plain if unspecified
  return self.headers.Get("Content-Type", "text/plain")
}

func (self nntpArticle) Sage() bool {
  return self.headers.Has("X-Sage")
}

func (self nntpArticle) OP() bool {
  return ! self.headers.Has("Reference")
}

func (self nntpArticle) Attachments() []NNTPAttachment {
  // TODO: implement?
  return nil
}

func (self nntpArticle) WriteBody(wr io.Writer) (err error) {
  // TODO: attachments
  _, err = io.WriteString(wr, self.message)
  return
}
<|endoftext|>


<commit_before>//
// message.go
//
package srnd

import (
  "encoding/base64"
  "fmt"
  "io"
  "log"
  "mime"
  "mime/multipart"
  "net/textproto"
  "strings"
  "time"
)

type ArticleHeaders map[string][]string

func (self ArticleHeaders) Has(key string) bool {
  _, ok := self[key]
  return ok
}

func (self ArticleHeaders) Set(key, val string) {
  self[key] = []string{val}
}

func (self ArticleHeaders) Get(key, fallback string) string {
  val , ok := self[key]
  if ok {
    return val[0]
  } else {
    return fallback
  }
}

type NNTPMessage interface {
  // this message's messsge id
  MessageID() string
  // the parent message's messageid if it's specified
  Reference() string
  // the newsgroup this post is in
  Newsgroup() string
  // the name of the poster
  Name() string
  // any email address associated with the post
  Email() string
  // the subject of the post
  Subject() string
  // when this was posted
  Posted() int64
  // the path header
  Path() string
  // append something to path
  // return message with new path
  AppendPath(part string) NNTPMessage
  // the type of this message usually a mimetype
  ContentType() string
  // was this post a sage?
  Sage() bool
  // was this post a root post?
  OP() bool
  // all attachments
  Attachments() []NNTPAttachment
  // all headers
  Headers() ArticleHeaders
  // write out body
  WriteBody(wr io.Writer) error
  // attach a file
  Attach(att NNTPAttachment) NNTPMessage
  // get the plaintext message if it exists
  Message() string
  // pack the whole message and prepare for write
  Pack()
}

type MessageReader interface {
  // read a message from a reader
  ReadMessage(r io.Reader) (NNTPMessage, error)
}

type MessageWriter interface {
  // write a message to a writer
  WriteMessage(nntp NNTPMessage, wr io.Writer) error
}

type MessageSigner interface {
  // sign a message, return a new message that is signed
  SignMessage(nntp NNTPMessage) (NNTPMessage, error)
}

type MessageVerifier interface {
  // verify a message, return the message that is verified
  VerifyMessage(nntp NNTPMessage) (NNTPMessage, error)
}

type nntpArticle struct {
  headers ArticleHeaders
  boundary string
  message nntpAttachment
  attachments []NNTPAttachment
}

// create a simple plaintext nntp message
func newPlaintextArticle(message, email, subject, name, instance, newsgroup string) NNTPMessage {
  nntp := nntpArticle{
    headers: make(ArticleHeaders),
  }
  nntp.headers.Set("From", fmt.Sprintf("%s <%s>", name, email))
  nntp.headers.Set("Subject", subject)
  if isSage(subject) {
    nntp.headers.Set("X-Sage", "1")
  }
  nntp.headers.Set("Path", instance)
  nntp.headers.Set("Message-ID", genMessageID(instance))
  // posted now
  nntp.headers.Set("Date", timeNowStr())
  nntp.headers.Set("Newsgroups", newsgroup)
  nntp.message = createPlaintextAttachment(message)
  nntp.Pack()
  return nntp
}

func (self nntpArticle) MessageID() string {
  return self.headers.Get("Message-ID", self.headers.Get("Messageid", self.headers.Get("MessageID", self.headers.Get("Message-Id", ""))))
}

func (self nntpArticle) Pack() {
  if len(self.boundary) == 0 {
    self.boundary = randStr(24)
    self.headers.Set("Mime-Version", "1.0")
    self.headers.Set("Content-Type", fmt.Sprintf("multipart/mixed; boundary=%s", self.boundary))
    log.Println("pack article boundary is", self.boundary)
  }

}

func (self nntpArticle) Reference() string {
  return self.headers.Get("Reference", self.headers.Get("References",""))
}

func (self nntpArticle) Newsgroup() string {
  return self.headers.Get("Newsgroups", "")
}

func (self nntpArticle) Name() string {
  from := self.headers.Get("From", "anonymous <a@no.n>")
  idx := strings.Index(from, " ")
  if idx > 1 {
    return from[:idx]
  }
  return "[Invalid From header]"
}

func (self nntpArticle) Email() string {
  from := self.headers.Get("From", "anonymous <a@no.n>")
  idx := strings.Index(from, " ")
  if idx > 1 {
    idx_1 := strings.Index(from[:idx], "<")
    idx_2 := strings.Index(from[:idx], ">")
    if idx_2 > 0 && idx_1 > 0 && idx_2 > idx_1 {
      return from[1+idx+idx_1:idx+idx_2]
    }
  }
  return "[Invalid From header]"

}

func (self nntpArticle) Subject() string {
  return self.headers.Get("Subject", "")
}

func (self nntpArticle) Posted() int64 {
  posted := self.headers.Get("Date", "")
  t, err := time.Parse(time.RFC1123Z, posted)
  if err == nil {
    return t.Unix()
  }
  return 0
}

func (self nntpArticle) Message() string {
  return self.message.body.String()
}

func (self nntpArticle) Path() string {
  return self.headers.Get("Path", "unspecified")
}

func (self nntpArticle) Headers() ArticleHeaders {
  return self.headers
}

func (self nntpArticle) AppendPath(part string) NNTPMessage {
  if self.headers.Has("Path") {
    self.headers.Set("Path", part + "!" + self.Path())
  } else {
    self.headers.Set("Path", part)
  }
  return self
}
func (self nntpArticle) ContentType() string {
  // assumes text/plain if unspecified
  return self.headers.Get("Content-Type", "text/plain")
}

func (self nntpArticle) Sage() bool {
  return self.headers.Has("X-Sage")
}

func (self nntpArticle) OP() bool {
  return ! self.headers.Has("Reference")
}

func (self nntpArticle) Attachments() []NNTPAttachment {
  return self.attachments
}

func (self nntpArticle) Attach(att NNTPAttachment) NNTPMessage {
  log.Println("attaching file", att.Filename())
  self.attachments = append(self.attachments, att)
  return self
}

func (self nntpArticle) WriteBody(wr io.Writer) (err error) {
  log.Printf("writing %d attachments", len(self.attachments))
  content_type := self.ContentType()
  _, params, err := mime.ParseMediaType(content_type)
  if err != nil {
    log.Println("failed to parse media type", err)
    return err
  }

  boundary, ok := params["boundary"]
  if ok {
    w := multipart.NewWriter(wr)

    err = w.SetBoundary(boundary)
    if err == nil {
      attachments := []NNTPAttachment{self.message}
      attachments = append(attachments, self.attachments...)
      log.Println("writing nntp body")
      for _ , att := range(attachments) {
        hdr := att.Header()
        if hdr == nil {
          hdr = make(textproto.MIMEHeader)
        }
        hdr.Set("Content-Transfer-Encoding", "base64")
        part, err := w.CreatePart(hdr)
        if err != nil {
          log.Println("error writing part", err)
        }
        if strings.HasPrefix(att.Mime(), "text/plain") {
          err = att.WriteTo(part)
        } else {
          enc := base64.NewEncoder(base64.StdEncoding, part)
          err = att.WriteTo(enc)
          enc.Close()
        }
        if err != nil {
          break
        }
      }
    }
    if err != nil {
      log.Println(err)
    }
    err = w.Close()
  } else {
    _, err = self.message.body.WriteTo(wr)
  }
  return err
}
<commit_msg>fix header name<commit_after>//
// message.go
//
package srnd

import (
  "encoding/base64"
  "fmt"
  "io"
  "log"
  "mime"
  "mime/multipart"
  "net/textproto"
  "strings"
  "time"
)

type ArticleHeaders map[string][]string

func (self ArticleHeaders) Has(key string) bool {
  _, ok := self[key]
  return ok
}

func (self ArticleHeaders) Set(key, val string) {
  self[key] = []string{val}
}

func (self ArticleHeaders) Get(key, fallback string) string {
  val , ok := self[key]
  if ok {
    return val[0]
  } else {
    return fallback
  }
}

type NNTPMessage interface {
  // this message's messsge id
  MessageID() string
  // the parent message's messageid if it's specified
  Reference() string
  // the newsgroup this post is in
  Newsgroup() string
  // the name of the poster
  Name() string
  // any email address associated with the post
  Email() string
  // the subject of the post
  Subject() string
  // when this was posted
  Posted() int64
  // the path header
  Path() string
  // append something to path
  // return message with new path
  AppendPath(part string) NNTPMessage
  // the type of this message usually a mimetype
  ContentType() string
  // was this post a sage?
  Sage() bool
  // was this post a root post?
  OP() bool
  // all attachments
  Attachments() []NNTPAttachment
  // all headers
  Headers() ArticleHeaders
  // write out body
  WriteBody(wr io.Writer) error
  // attach a file
  Attach(att NNTPAttachment) NNTPMessage
  // get the plaintext message if it exists
  Message() string
  // pack the whole message and prepare for write
  Pack()
}

type MessageReader interface {
  // read a message from a reader
  ReadMessage(r io.Reader) (NNTPMessage, error)
}

type MessageWriter interface {
  // write a message to a writer
  WriteMessage(nntp NNTPMessage, wr io.Writer) error
}

type MessageSigner interface {
  // sign a message, return a new message that is signed
  SignMessage(nntp NNTPMessage) (NNTPMessage, error)
}

type MessageVerifier interface {
  // verify a message, return the message that is verified
  VerifyMessage(nntp NNTPMessage) (NNTPMessage, error)
}

type nntpArticle struct {
  headers ArticleHeaders
  boundary string
  message nntpAttachment
  attachments []NNTPAttachment
}

// create a simple plaintext nntp message
func newPlaintextArticle(message, email, subject, name, instance, newsgroup string) NNTPMessage {
  nntp := nntpArticle{
    headers: make(ArticleHeaders),
  }
  nntp.headers.Set("From", fmt.Sprintf("%s <%s>", name, email))
  nntp.headers.Set("Subject", subject)
  if isSage(subject) {
    nntp.headers.Set("X-Sage", "1")
  }
  nntp.headers.Set("Path", instance)
  nntp.headers.Set("Message-ID", genMessageID(instance))
  // posted now
  nntp.headers.Set("Date", timeNowStr())
  nntp.headers.Set("Newsgroups", newsgroup)
  nntp.message = createPlaintextAttachment(message)
  nntp.Pack()
  return nntp
}

func (self nntpArticle) MessageID() string {
  return self.headers.Get("Message-ID", self.headers.Get("Messageid", self.headers.Get("MessageID", self.headers.Get("Message-Id", ""))))
}

func (self nntpArticle) Pack() {
  if len(self.boundary) == 0 {
    self.boundary = randStr(24)
    self.headers.Set("Mime-Version", "1.0")
    self.headers.Set("Content-Type", fmt.Sprintf("multipart/mixed; boundary=%s", self.boundary))
    log.Println("pack article boundary is", self.boundary)
  }

}

func (self nntpArticle) Reference() string {
  return self.headers.Get("Reference", self.headers.Get("References",""))
}

func (self nntpArticle) Newsgroup() string {
  return self.headers.Get("Newsgroups", "")
}

func (self nntpArticle) Name() string {
  from := self.headers.Get("From", "anonymous <a@no.n>")
  idx := strings.Index(from, " ")
  if idx > 1 {
    return from[:idx]
  }
  return "[Invalid From header]"
}

func (self nntpArticle) Email() string {
  from := self.headers.Get("From", "anonymous <a@no.n>")
  idx := strings.Index(from, " ")
  if idx > 1 {
    idx_1 := strings.Index(from[:idx], "<")
    idx_2 := strings.Index(from[:idx], ">")
    if idx_2 > 0 && idx_1 > 0 && idx_2 > idx_1 {
      return from[1+idx+idx_1:idx+idx_2]
    }
  }
  return "[Invalid From header]"

}

func (self nntpArticle) Subject() string {
  return self.headers.Get("Subject", "")
}

func (self nntpArticle) Posted() int64 {
  posted := self.headers.Get("Date", "")
  t, err := time.Parse(time.RFC1123Z, posted)
  if err == nil {
    return t.Unix()
  }
  return 0
}

func (self nntpArticle) Message() string {
  return self.message.body.String()
}

func (self nntpArticle) Path() string {
  return self.headers.Get("Path", "unspecified")
}

func (self nntpArticle) Headers() ArticleHeaders {
  return self.headers
}

func (self nntpArticle) AppendPath(part string) NNTPMessage {
  if self.headers.Has("Path") {
    self.headers.Set("Path", part + "!" + self.Path())
  } else {
    self.headers.Set("Path", part)
  }
  return self
}
func (self nntpArticle) ContentType() string {
  // assumes text/plain if unspecified
  return self.headers.Get("Content-Type", "text/plain")
}

func (self nntpArticle) Sage() bool {
  return self.headers.Has("X-Sage")
}

func (self nntpArticle) OP() bool {
  return ! ( self.headers.Has("References") || self.headers.Has("Reference") )
}

func (self nntpArticle) Attachments() []NNTPAttachment {
  return self.attachments
}

func (self nntpArticle) Attach(att NNTPAttachment) NNTPMessage {
  log.Println("attaching file", att.Filename())
  self.attachments = append(self.attachments, att)
  return self
}

func (self nntpArticle) WriteBody(wr io.Writer) (err error) {
  log.Printf("writing %d attachments", len(self.attachments))
  content_type := self.ContentType()
  _, params, err := mime.ParseMediaType(content_type)
  if err != nil {
    log.Println("failed to parse media type", err)
    return err
  }

  boundary, ok := params["boundary"]
  if ok {
    w := multipart.NewWriter(wr)

    err = w.SetBoundary(boundary)
    if err == nil {
      attachments := []NNTPAttachment{self.message}
      attachments = append(attachments, self.attachments...)
      log.Println("writing nntp body")
      for _ , att := range(attachments) {
        hdr := att.Header()
        if hdr == nil {
          hdr = make(textproto.MIMEHeader)
        }
        hdr.Set("Content-Transfer-Encoding", "base64")
        part, err := w.CreatePart(hdr)
        if err != nil {
          log.Println("error writing part", err)
        }
        if strings.HasPrefix(att.Mime(), "text/plain") {
          err = att.WriteTo(part)
        } else {
          enc := base64.NewEncoder(base64.StdEncoding, part)
          err = att.WriteTo(enc)
          enc.Close()
        }
        if err != nil {
          break
        }
      }
    }
    if err != nil {
      log.Println(err)
    }
    err = w.Close()
  } else {
    _, err = self.message.body.WriteTo(wr)
  }
  return err
}
<|endoftext|>


<commit_before>// Copyright 2009 Dimiter Stanev, malkia@gmail.com.
// Copyright 2010 David Roundy, roundyd@physics.oregonstate.edu.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"os"
	"fmt"
	"strings"
	"io/ioutil"
	"go/parser"
	"go/ast"
	"strconv"
	"path"
	"flag"
	"bytes"
	"go/printer"
	stringslice "./gotgo/slice(string)"
)

var ignoreInstalled = flag.Bool("ignore-installed", true,
	"don't require that installed gotgo packages be present")

var noGotgo = flag.Bool("without-gotgo", true,
	"don't require that gotgo be installed")

func getImports(filename string) (imports map[string]string, names map[string]string, error os.Error) {
	source, error := ioutil.ReadFile(filename)
	if error != nil {
		return
	}
	file, error := parser.ParseFile(filename, source, parser.ImportsOnly)
	if error != nil {
		return
	}
	for _, importDecl := range file.Decls {
		importDecl, ok := importDecl.(*ast.GenDecl)
		if ok {
			for _, importSpec := range importDecl.Specs {
				importSpec, ok := importSpec.(*ast.ImportSpec)
				if ok {
					importPath, _ := strconv.Unquote(string(importSpec.Path.Value))
					if len(importPath) > 0 {
						if imports == nil {
							imports = make(map[string]string)
							names = make(map[string]string)
						}
						if strings.Index(importPath, "(") != -1 {
							nicepath := path.Clean(importPath)
							imports[nicepath] = importPath
							//names[path.Join(dir,path.Clean(importSpec.Name.String()))]=importPath
							names[importSpec.Name.String()]=nicepath
						}
					}
				}
			}
		}
	}
	return
}

func handleFile(f string) {
	imports, names, err := getImports(f)
	if err != nil {
		fmt.Println("# error: ", err)
	}
	for i,ii := range imports {
		// fmt.Printf("# %s imports %s\n", f, i)
		createGofile(i, ii, names)
	}
}

func createGofile(sourcePath, importPath string, names map[string]string) {
	n := strings.Index(sourcePath, "(")
	// Begin by scanning the type parameters in the import
	types := parseImport(sourcePath)
	basename := sourcePath[0:n]
	goname := path.Join(srcpkgdir, importPath+".go")
	nip := strings.Index(importPath, "(")
	installedGotname := path.Join(srcpkgdir, importPath[0:nip]+".got")

	fmt.Printf("\n# Got template: %s\n", basename)
	fmt.Printf("%s:\n\tgoinstall \"%s\"\n", installedGotname, basename)
	fmt.Printf("%s: %s\n\tnewgotgo -o \"%s\" \"%s\"", goname, installedGotname,
		goname, installedGotname)
	for _, a := range types {
		fmt.Printf(` "%s"`, a)
	}
	fmt.Println()
	fmt.Printf("%s.%s: %s\n\t%sg \"$<\"\n", goname[0:len(goname)-3], arch,
		goname, arch)
	return
}

var srcpkgdir = `$GOROOT/src/pkg`
var pkgdir = `$GOROOT/${GOOS}_${GOARCH}`
// path.Join(os.Getenv("GOROOT"),"pkg",os.Getenv("GOOS")+"_"+os.Getenv("GOARCH"))
var arch = func() string {
	return map[string]string{"amd64":"6","386":"8","arm":"5"}[os.Getenv("GOARCH")]
}()

func main() {
	flag.Parse()
	if len(flag.Args()) != 1 {
		fmt.Println(flag.Args())
		dieWith("gotimports requires just one argument: a .go file")
	}
	handleFile(flag.Args()[0])
}

func dieWith(e string) {
	fmt.Fprintln(os.Stderr, e)
	os.Exit(1)
}

// This parses an import statement, such as "./foo/bar(int,baz.T(foo.T))",
// returning a list of types and a list of imports that are needed
// (e.g. baz and foo in the above example).
func parseImport(s string) (types []string) {
	// First, I want to cut off any preliminary directories, so the
	// import should look like a function call.
	n := strings.Index(s, "(")
	if n < 1 { return }
	start := 0
	for i := n-1; i>=0; i-- {
		if s[i] == '/' {
			start = i+1
			break
		}
	}
	s = s[start:]
	// Now we just need to parse the apparent function call...
	x, _ := parser.ParseExpr(s, s)
	callexpr, ok := x.(*ast.CallExpr)
	if !ok { return } // FIXME: need error handling?
	for _, texpr := range callexpr.Args {
		types = stringslice.Append(types, pretty(texpr))
	}
	return types
}

func pretty(expr interface{}) string {
	b := bytes.NewBufferString("")
	printer.Fprint(b, expr)
	// Here we do a hokey trick to make the pretty-printer give paths
	// without extra spaces.  This is a pretty fragile system, and I
	// really ought to just implement a special pretty printer of my
	// own, since these paths are pretty darn simple.
	x := b.String()
	for i := strings.Index(x," / "); i != -1; i = strings.Index(x," / ") {
		x = x[0:i] + "/" + x[i+3:]
	}
	return x
}

func fileexists(f string) bool {
	x, err := os.Stat(f)
	return err == nil && x.IsRegular()
}
<commit_msg>gotimports.go: Compile correctly for new 'go/parser' interface.<commit_after>// Copyright 2009 Dimiter Stanev, malkia@gmail.com.
// Copyright 2010 David Roundy, roundyd@physics.oregonstate.edu.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"os"
	"fmt"
	"strings"
	"io/ioutil"
	"go/parser"
	"go/ast"
	"strconv"
	"path"
	"flag"
	"bytes"
	"go/printer"
	stringslice "./gotgo/slice(string)"
)

var ignoreInstalled = flag.Bool("ignore-installed", true,
	"don't require that installed gotgo packages be present")

var noGotgo = flag.Bool("without-gotgo", true,
	"don't require that gotgo be installed")

func getImports(filename string) (imports map[string]string, names map[string]string, error os.Error) {
	source, error := ioutil.ReadFile(filename)
	if error != nil {
		return
	}
	file, error := parser.ParseFile(filename, source, nil, parser.ImportsOnly)
	if error != nil {
		return
	}
	for _, importDecl := range file.Decls {
		importDecl, ok := importDecl.(*ast.GenDecl)
		if ok {
			for _, importSpec := range importDecl.Specs {
				importSpec, ok := importSpec.(*ast.ImportSpec)
				if ok {
					importPath, _ := strconv.Unquote(string(importSpec.Path.Value))
					if len(importPath) > 0 {
						if imports == nil {
							imports = make(map[string]string)
							names = make(map[string]string)
						}
						if strings.Index(importPath, "(") != -1 {
							nicepath := path.Clean(importPath)
							imports[nicepath] = importPath
							//names[path.Join(dir,path.Clean(importSpec.Name.String()))]=importPath
							names[importSpec.Name.String()]=nicepath
						}
					}
				}
			}
		}
	}
	return
}

func handleFile(f string) {
	imports, names, err := getImports(f)
	if err != nil {
		fmt.Println("# error: ", err)
	}
	for i,ii := range imports {
		// fmt.Printf("# %s imports %s\n", f, i)
		createGofile(i, ii, names)
	}
}

func createGofile(sourcePath, importPath string, names map[string]string) {
	n := strings.Index(sourcePath, "(")
	// Begin by scanning the type parameters in the import
	types := parseImport(sourcePath)
	basename := sourcePath[0:n]
	goname := path.Join(srcpkgdir, importPath+".go")
	nip := strings.Index(importPath, "(")
	installedGotname := path.Join(srcpkgdir, importPath[0:nip]+".got")

	fmt.Printf("\n# Got template: %s\n", basename)
	fmt.Printf("%s:\n\tgoinstall \"%s\"\n", installedGotname, basename)
	fmt.Printf("%s: %s\n\tnewgotgo -o \"%s\" \"%s\"", goname, installedGotname,
		goname, installedGotname)
	for _, a := range types {
		fmt.Printf(` "%s"`, a)
	}
	fmt.Println()
	fmt.Printf("%s.%s: %s\n\t%sg \"$<\"\n", goname[0:len(goname)-3], arch,
		goname, arch)
	return
}

var srcpkgdir = `$GOROOT/src/pkg`
var pkgdir = `$GOROOT/${GOOS}_${GOARCH}`
// path.Join(os.Getenv("GOROOT"),"pkg",os.Getenv("GOOS")+"_"+os.Getenv("GOARCH"))
var arch = func() string {
	return map[string]string{"amd64":"6","386":"8","arm":"5"}[os.Getenv("GOARCH")]
}()

func main() {
	flag.Parse()
	if len(flag.Args()) != 1 {
		fmt.Println(flag.Args())
		dieWith("gotimports requires just one argument: a .go file")
	}
	handleFile(flag.Args()[0])
}

func dieWith(e string) {
	fmt.Fprintln(os.Stderr, e)
	os.Exit(1)
}

// This parses an import statement, such as "./foo/bar(int,baz.T(foo.T))",
// returning a list of types and a list of imports that are needed
// (e.g. baz and foo in the above example).
func parseImport(s string) (types []string) {
	// First, I want to cut off any preliminary directories, so the
	// import should look like a function call.
	n := strings.Index(s, "(")
	if n < 1 { return }
	start := 0
	for i := n-1; i>=0; i-- {
		if s[i] == '/' {
			start = i+1
			break
		}
	}
	s = s[start:]
	// Now we just need to parse the apparent function call...
	x, _ := parser.ParseExpr(s, s, nil)
	callexpr, ok := x.(*ast.CallExpr)
	if !ok { return } // FIXME: need error handling?
	for _, texpr := range callexpr.Args {
		types = stringslice.Append(types, pretty(texpr))
	}
	return types
}

func pretty(expr interface{}) string {
	b := bytes.NewBufferString("")
	printer.Fprint(b, expr)
	// Here we do a hokey trick to make the pretty-printer give paths
	// without extra spaces.  This is a pretty fragile system, and I
	// really ought to just implement a special pretty printer of my
	// own, since these paths are pretty darn simple.
	x := b.String()
	for i := strings.Index(x," / "); i != -1; i = strings.Index(x," / ") {
		x = x[0:i] + "/" + x[i+3:]
	}
	return x
}

func fileexists(f string) bool {
	x, err := os.Stat(f)
	return err == nil && x.IsRegular()
}
<|endoftext|>


<commit_before>package bencode

type BencodedList struct {
	str_value string
}

func (bl *BencodedList) StringValue() string {
	return "l" + bl.str_value + "e"
}

func (bl *BencodedList) AppendInt(i int) {
	bl.str_value += BencodeInt(i)
}

func (bl *BencodedList) AppendString(str string) {
	bl.str_value += BencodeString(str)
}

func (bl *BencodedList) AppendBencodedList(l BencodedList) {
	bl.str_value += l.StringValue()
}

func (bl *BencodedList) AppendBencodedDict(d BencodedDict) {
	bl.str_value += d.StringValue()
}
<commit_msg>Added documentation for bencodedlist.go<commit_after>package bencode

// Store the string value of a list of bencoded values
type BencodedList struct {
	str_value string
}

// Get the String value of a BencodedList
func (bl *BencodedList) StringValue() string {
	return "l" + bl.str_value + "e"
}

// Append an int to a BencodedList
func (bl *BencodedList) AppendInt(i int) {
	bl.str_value += BencodeInt(i)
}

// Append a String to a BencodedList
func (bl *BencodedList) AppendString(str string) {
	bl.str_value += BencodeString(str)
}

// Append a BencodedList to a BencodedList
func (bl *BencodedList) AppendBencodedList(l BencodedList) {
	bl.str_value += l.StringValue()
}

// Append a BencodedDict to a BencodedList
func (bl *BencodedList) AppendBencodedDict(d BencodedDict) {
	bl.str_value += d.StringValue()
}
<|endoftext|>


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

package bigquery

import (
	"golang.org/x/net/context"
	bq "google.golang.org/api/bigquery/v2"
)

// A Job represents an operation which has been submitted to BigQuery for processing.
type Job struct {
	service   service
	projectID string
	jobID     string
}

// State is one of a sequence of states that a Job progresses through as it is processed.
type State int

const (
	Pending State = iota
	Running
	Done
)

// JobStatus contains the current State of a job, and errors encountered while processing that job.
type JobStatus struct {
	State State

	err error

	// All errors encountered during the running of the job.
	// Not all Errors are fatal, so errors here do not necessarily mean that the job has completed or was unsuccessful.
	Errors []*Error
}

// jobOption is an Option which modifies a bq.Job proto.
// This is used for configuring values that apply to all operations, such as setting a jobReference.
type jobOption interface {
	customizeJob(job *bq.Job, projectID string)
}

type jobID string

// JobID returns an Option that sets the job ID of a BigQuery job.
// If this Option is not used, a job ID is generated automatically.
func JobID(ID string) Option {
	return jobID(ID)
}

func (opt jobID) implementsOption() {}

func (opt jobID) customizeJob(job *bq.Job, projectID string) {
	job.JobReference = &bq.JobReference{
		JobId:     string(opt),
		ProjectId: projectID,
	}
}

// Done reports whether the job has completed.
// After Done returns true, the Err method will return an error if the job completed unsuccesfully.
func (s *JobStatus) Done() bool {
	return s.State == Done
}

// Err returns the error that caused the job to complete unsuccesfully (if any).
func (s *JobStatus) Err() error {
	return s.err
}

// Status returns the current status of the job.  It fails if the Status could not be determined.
func (j *Job) Status(ctx context.Context) (*JobStatus, error) {
	return j.service.jobStatus(ctx, j.projectID, j.jobID)
}
<commit_msg>cloud/bigquery: Enable creation of a Job struct by id.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bigquery

import (
	"golang.org/x/net/context"
	bq "google.golang.org/api/bigquery/v2"
)

// A Job represents an operation which has been submitted to BigQuery for processing.
type Job struct {
	service   service
	projectID string
	jobID     string
}

// JobFromID creates a Job which refers to an existing BigQuery job. The job
// need not have been created by this package. For example, the job may have
// been created in the BigQuery console.
func (c *Client) JobFromID(id string) *Job {
	return &Job{
		service:   c.service,
		projectID: c.projectID,
		jobID:     id,
	}
}

// State is one of a sequence of states that a Job progresses through as it is processed.
type State int

const (
	Pending State = iota
	Running
	Done
)

// JobStatus contains the current State of a job, and errors encountered while processing that job.
type JobStatus struct {
	State State

	err error

	// All errors encountered during the running of the job.
	// Not all Errors are fatal, so errors here do not necessarily mean that the job has completed or was unsuccessful.
	Errors []*Error
}

// jobOption is an Option which modifies a bq.Job proto.
// This is used for configuring values that apply to all operations, such as setting a jobReference.
type jobOption interface {
	customizeJob(job *bq.Job, projectID string)
}

type jobID string

// JobID returns an Option that sets the job ID of a BigQuery job.
// If this Option is not used, a job ID is generated automatically.
func JobID(ID string) Option {
	return jobID(ID)
}

func (opt jobID) implementsOption() {}

func (opt jobID) customizeJob(job *bq.Job, projectID string) {
	job.JobReference = &bq.JobReference{
		JobId:     string(opt),
		ProjectId: projectID,
	}
}

// Done reports whether the job has completed.
// After Done returns true, the Err method will return an error if the job completed unsuccesfully.
func (s *JobStatus) Done() bool {
	return s.State == Done
}

// Err returns the error that caused the job to complete unsuccesfully (if any).
func (s *JobStatus) Err() error {
	return s.err
}

// Status returns the current status of the job.  It fails if the Status could not be determined.
func (j *Job) Status(ctx context.Context) (*JobStatus, error) {
	return j.service.jobStatus(ctx, j.projectID, j.jobID)
}
<|endoftext|>


<commit_before>package event_strings

import (
	"github.com/karimra/gnmic/formatters"
)

// Strings provides some of Golang's strings functions to transform, tags, tag keys, values and value keys
type Strings struct{}

func init() {
	formatters.Register("event_strings", func() formatters.EventProcessor {
		return &Strings{}
	})
}

func (r *Strings) Init(cfg interface{}) error { return nil }

func (r *Strings) Apply(e *formatters.EventMsg) {}
<commit_msg>placeholder event_strings<commit_after>package event_strings

import (
	"github.com/karimra/gnmic/formatters"
)

// TODO

// Strings provides some of Golang's strings functions to transform: tags, tag keys, values and value keys
type Strings struct {
	Tags   []string `mapstructure:"tags,omitempty"`
	Values []string `mapstructure:"values,omitempty"`

	TagKeys   []string `mapstructure:"tag_keys,omitempty"`
	ValueKeys []string `mapstructure:"value_keys,omitempty"`
}

func init() {
	formatters.Register("event_strings", func() formatters.EventProcessor {
		return &Strings{}
	})
}

func (r *Strings) Init(cfg interface{}) error { return nil }

func (r *Strings) Apply(e *formatters.EventMsg) {}
<|endoftext|>


<commit_before>package api_auth_impl

import (
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"github.com/watermint/toolbox/infra/api/api_auth"
	"github.com/watermint/toolbox/infra/api/api_context"
	"github.com/watermint/toolbox/infra/api/api_context_impl"
	"github.com/watermint/toolbox/infra/app"
	"github.com/watermint/toolbox/infra/control/app_control"
	"github.com/watermint/toolbox/infra/security/sc_random"
	"go.uber.org/zap"
	"golang.org/x/oauth2"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
)

func NewWeb(control app_control.Control) api_auth.Web {
	w := &Web{
		control:       control,
		app:           NewApp(control),
		sessions:      map[string]WebAuthSession{},
		sessionTokens: map[string]string{},
	}
	return w
}

type WebAuthSession struct {
	TokenType   string
	RedirectUrl string
	PeerName    string
}

type Web struct {
	control       app_control.Control
	app           api_auth.App
	sessions      map[string]WebAuthSession
	sessionTokens map[string]string
	sessionLock   sync.Mutex
	databaseLock  sync.Mutex // database write lock
}

func (z *Web) generatePeerName() string {
	return sc_random.MustGenerateRandomString(8)
}

func (z *Web) New(tokenType, redirectUrl string) (state, url string, err error) {
	z.sessionLock.Lock()
	defer z.sessionLock.Unlock()

	peerName := z.generatePeerName()

	l := z.control.Log().With(
		zap.String("peerName", peerName),
		zap.String("tokenType", tokenType),
	)
	l.Debug("Start Web OAuth sequence", zap.String("redirectUrl", redirectUrl))

	for {
		state, err = sc_random.GenerateRandomString(12)
		if err != nil {
			l.Error("Unable to generate `state`", zap.Error(err))
			return "", "", err
		}
		if _, ok := z.sessions[state]; !ok {
			z.sessions[state] = WebAuthSession{
				TokenType:   tokenType,
				RedirectUrl: redirectUrl,
				PeerName:    peerName,
			}
			break
		}
		// recreate state when conflicted state found in existing sessions
	}

	cfg := z.app.Config(tokenType)
	url = cfg.AuthCodeURL(
		state,
		oauth2.SetAuthURLParam("client_id", cfg.ClientID),
		oauth2.SetAuthURLParam("response_type", "code"),
		oauth2.SetAuthURLParam("redirect_uri", redirectUrl),
	)
	l.Debug("Auth url generated", zap.String("url", url))
	return
}

func (z *Web) Auth(state, code string) (peerName string, ctx api_context.Context, err error) {
	z.sessionLock.Lock()
	defer z.sessionLock.Unlock()

	l := z.control.Log().With(
		zap.String("peerName", peerName),
		zap.String("state", state),
	)
	l.Debug("Start auth sequence")

	session, ok := z.sessions[state]
	if !ok {
		l.Debug("State not found")
		return "", nil, errors.New("state not found")
	}

	cfg := z.app.Config(session.TokenType)
	cfg.RedirectURL = session.RedirectUrl
	token, err := cfg.Exchange(context.Background(), code)
	if err != nil {
		l.Debug("Auth failed", zap.Error(err))
		return "", nil, err
	}

	tc := api_auth.TokenContainer{
		Token:     token.AccessToken,
		TokenType: session.TokenType,
		PeerName:  session.PeerName,
	}
	ctx = api_context_impl.New(z.control, tc)

	desc, suppl, err := VerifyToken(session.TokenType, ctx)
	if err != nil {
		l.Debug("Verification failed", zap.Error(err))
		return "", nil, err
	}
	tc.Description = desc
	tc.Supplemental = suppl

	z.sessionTokens[state] = token.AccessToken
	z.updateDatabase(tc)

	l.Debug("Successfully finished auth sequence")
	return tc.PeerName, ctx, nil
}

func (z *Web) Get(state string) (peerName string, ctx api_context.Context, err error) {
	if t, ok := z.sessionTokens[state]; ok {
		if c, ok := z.sessions[state]; ok {
			tc := api_auth.TokenContainer{
				Token:     t,
				TokenType: c.TokenType,
				PeerName:  c.PeerName,
			}
			ctx = api_context_impl.New(z.control, tc)

			return c.PeerName, ctx, nil
		}
	}
	z.control.Log().Debug("State not found", zap.String("state", state))
	return "", nil, errors.New("state not found")
}

func (z *Web) List(tokenType string) (token []api_auth.TokenContainer, err error) {
	token = make([]api_auth.TokenContainer, 0)
	tf := z.databaseFile(tokenType)
	l := z.control.Log().With(zap.String("tokenType", tokenType))

	_, err = os.Stat(tf)
	if os.IsNotExist(err) {
		return token, nil
	}
	tb, err := ioutil.ReadFile(tf)
	if err != nil {
		l.Debug("unable to read tokens file", zap.String("path", tf), zap.Error(err))
		return
	}

	bk, err := aes.NewCipher(z.databaseKey())
	if err != nil {
		l.Debug("unable to create new cipher", zap.Error(err))
		return
	}
	gcm, err := cipher.NewGCM(bk)
	if err != nil {
		l.Debug("unable to create new GCM", zap.Error(err))
		return
	}
	ns := gcm.NonceSize()
	nonce, ct := tb[:ns], tb[ns:]
	v, err := gcm.Open(nil, nonce, ct, nil)
	if err != nil {
		l.Debug("unable to open gcm", zap.Error(err))
		return
	}
	err = json.Unmarshal(v, &token)
	if err != nil {
		l.Debug("unable to unmarshal tokens file", zap.Error(err))
		return
	}
	return
}

func (z *Web) databaseKey() []byte {
	key := []byte(app.BuilderKey + app.Name)
	key32 := sha256.Sum224([]byte(key))
	kb := make([]byte, 32)
	copy(kb[:], key32[:])

	return kb
}

func (z *Web) databaseFile(tokenType string) string {
	p := z.control.Workspace().Secrets()
	return filepath.Join(p, tokenType+".t")
}

func (z *Web) updateDatabase(tc api_auth.TokenContainer) {
	z.databaseLock.Lock()
	defer z.databaseLock.Unlock()

	l := z.control.Log()

	tokens, err := z.List(tc.TokenType)
	if err != nil {
		l.Debug("Unable to retrieve existing tokens", zap.Error(err))
		return
	}

	tokens = append(tokens, tc)

	tb, err := json.Marshal(tokens)
	if err != nil {
		l.Debug("Unable to marshal tokens into JSON", zap.Error(err))
		return
	}
	bk, err := aes.NewCipher(z.databaseKey())
	if err != nil {
		l.Debug("Unable to create new cipher", zap.Error(err))
		return
	}
	gcm, err := cipher.NewGCM(bk)
	if err != nil {
		l.Debug("Unable to create new GCM", zap.Error(err))
		return
	}
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		l.Debug("Unable to prepare nonce", zap.Error(err))
		return
	}
	sealed := gcm.Seal(nonce, nonce, tb, nil)

	tf := z.databaseFile(tc.TokenType)
	err = ioutil.WriteFile(tf, sealed, 0600)
	if err != nil {
		l.Debug("unable to write tokens into file", zap.Error(err))
		return
	}
}
<commit_msg>fix #137 : salt for web auth file name<commit_after>package api_auth_impl

import (
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/watermint/toolbox/infra/api/api_auth"
	"github.com/watermint/toolbox/infra/api/api_context"
	"github.com/watermint/toolbox/infra/api/api_context_impl"
	"github.com/watermint/toolbox/infra/app"
	"github.com/watermint/toolbox/infra/control/app_control"
	"github.com/watermint/toolbox/infra/security/sc_random"
	"go.uber.org/zap"
	"golang.org/x/oauth2"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
)

func NewWeb(control app_control.Control) api_auth.Web {
	w := &Web{
		control:       control,
		app:           NewApp(control),
		sessions:      map[string]WebAuthSession{},
		sessionTokens: map[string]string{},
	}
	return w
}

type WebAuthSession struct {
	TokenType   string
	RedirectUrl string
	PeerName    string
}

type Web struct {
	control       app_control.Control
	app           api_auth.App
	sessions      map[string]WebAuthSession
	sessionTokens map[string]string
	sessionLock   sync.Mutex
	databaseLock  sync.Mutex // database write lock
}

func (z *Web) generatePeerName() string {
	return sc_random.MustGenerateRandomString(8)
}

func (z *Web) New(tokenType, redirectUrl string) (state, url string, err error) {
	z.sessionLock.Lock()
	defer z.sessionLock.Unlock()

	peerName := z.generatePeerName()

	l := z.control.Log().With(
		zap.String("peerName", peerName),
		zap.String("tokenType", tokenType),
	)
	l.Debug("Start Web OAuth sequence", zap.String("redirectUrl", redirectUrl))

	for {
		state, err = sc_random.GenerateRandomString(12)
		if err != nil {
			l.Error("Unable to generate `state`", zap.Error(err))
			return "", "", err
		}
		if _, ok := z.sessions[state]; !ok {
			z.sessions[state] = WebAuthSession{
				TokenType:   tokenType,
				RedirectUrl: redirectUrl,
				PeerName:    peerName,
			}
			break
		}
		// recreate state when conflicted state found in existing sessions
	}

	cfg := z.app.Config(tokenType)
	url = cfg.AuthCodeURL(
		state,
		oauth2.SetAuthURLParam("client_id", cfg.ClientID),
		oauth2.SetAuthURLParam("response_type", "code"),
		oauth2.SetAuthURLParam("redirect_uri", redirectUrl),
	)
	l.Debug("Auth url generated", zap.String("url", url))
	return
}

func (z *Web) Auth(state, code string) (peerName string, ctx api_context.Context, err error) {
	z.sessionLock.Lock()
	defer z.sessionLock.Unlock()

	l := z.control.Log().With(
		zap.String("peerName", peerName),
		zap.String("state", state),
	)
	l.Debug("Start auth sequence")

	session, ok := z.sessions[state]
	if !ok {
		l.Debug("State not found")
		return "", nil, errors.New("state not found")
	}

	cfg := z.app.Config(session.TokenType)
	cfg.RedirectURL = session.RedirectUrl
	token, err := cfg.Exchange(context.Background(), code)
	if err != nil {
		l.Debug("Auth failed", zap.Error(err))
		return "", nil, err
	}

	tc := api_auth.TokenContainer{
		Token:     token.AccessToken,
		TokenType: session.TokenType,
		PeerName:  session.PeerName,
	}
	ctx = api_context_impl.New(z.control, tc)

	desc, suppl, err := VerifyToken(session.TokenType, ctx)
	if err != nil {
		l.Debug("Verification failed", zap.Error(err))
		return "", nil, err
	}
	tc.Description = desc
	tc.Supplemental = suppl

	z.sessionTokens[state] = token.AccessToken
	z.updateDatabase(tc)

	l.Debug("Successfully finished auth sequence")
	return tc.PeerName, ctx, nil
}

func (z *Web) Get(state string) (peerName string, ctx api_context.Context, err error) {
	if t, ok := z.sessionTokens[state]; ok {
		if c, ok := z.sessions[state]; ok {
			tc := api_auth.TokenContainer{
				Token:     t,
				TokenType: c.TokenType,
				PeerName:  c.PeerName,
			}
			ctx = api_context_impl.New(z.control, tc)

			return c.PeerName, ctx, nil
		}
	}
	z.control.Log().Debug("State not found", zap.String("state", state))
	return "", nil, errors.New("state not found")
}

func (z *Web) List(tokenType string) (token []api_auth.TokenContainer, err error) {
	token = make([]api_auth.TokenContainer, 0)
	tf := z.databaseFile(tokenType)
	l := z.control.Log().With(zap.String("tokenType", tokenType))

	_, err = os.Stat(tf)
	if os.IsNotExist(err) {
		return token, nil
	}
	tb, err := ioutil.ReadFile(tf)
	if err != nil {
		l.Debug("unable to read tokens file", zap.String("path", tf), zap.Error(err))
		return
	}

	bk, err := aes.NewCipher(z.databaseKey())
	if err != nil {
		l.Debug("unable to create new cipher", zap.Error(err))
		return
	}
	gcm, err := cipher.NewGCM(bk)
	if err != nil {
		l.Debug("unable to create new GCM", zap.Error(err))
		return
	}
	ns := gcm.NonceSize()
	nonce, ct := tb[:ns], tb[ns:]
	v, err := gcm.Open(nil, nonce, ct, nil)
	if err != nil {
		l.Debug("unable to open gcm", zap.Error(err))
		return
	}
	err = json.Unmarshal(v, &token)
	if err != nil {
		l.Debug("unable to unmarshal tokens file", zap.Error(err))
		return
	}
	return
}

func (z *Web) databaseKey() []byte {
	key := []byte(app.BuilderKey + app.Name)
	key32 := sha256.Sum224([]byte(key))
	kb := make([]byte, 32)
	copy(kb[:], key32[:])

	return kb
}

func (z *Web) databaseFile(tokenType string) string {
	p := z.control.Workspace().Secrets()
	px := sha256.Sum224([]byte(tokenType + app.BuilderKey + app.Name))
	pn := fmt.Sprintf("%x.t", px)

	return filepath.Join(p, pn)
}

func (z *Web) updateDatabase(tc api_auth.TokenContainer) {
	z.databaseLock.Lock()
	defer z.databaseLock.Unlock()

	l := z.control.Log()

	tokens, err := z.List(tc.TokenType)
	if err != nil {
		l.Debug("Unable to retrieve existing tokens", zap.Error(err))
		return
	}

	tokens = append(tokens, tc)

	tb, err := json.Marshal(tokens)
	if err != nil {
		l.Debug("Unable to marshal tokens into JSON", zap.Error(err))
		return
	}
	bk, err := aes.NewCipher(z.databaseKey())
	if err != nil {
		l.Debug("Unable to create new cipher", zap.Error(err))
		return
	}
	gcm, err := cipher.NewGCM(bk)
	if err != nil {
		l.Debug("Unable to create new GCM", zap.Error(err))
		return
	}
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		l.Debug("Unable to prepare nonce", zap.Error(err))
		return
	}
	sealed := gcm.Seal(nonce, nonce, tb, nil)

	tf := z.databaseFile(tc.TokenType)
	err = ioutil.WriteFile(tf, sealed, 0600)
	if err != nil {
		l.Debug("unable to write tokens into file", zap.Error(err))
		return
	}
}
<|endoftext|>


<commit_before>package announcer

import (
	"context"
	"math"
	"net"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/tracker"
	"github.com/cenkalti/rain/internal/tracker/httptracker"
)

type Status int

const (
	NotContactedYet Status = iota
	Contacting
	Working
	NotWorking
)

type PeriodicalAnnouncer struct {
	Tracker       tracker.Tracker
	status        Status
	statsCommandC chan statsRequest
	numWant       int
	interval      time.Duration
	minInterval   time.Duration
	seeders       int
	leechers      int
	lastError     *AnnounceError
	log           logger.Logger
	completedC    chan struct{}
	newPeers      chan []*net.TCPAddr
	backoff       backoff.BackOff
	getTorrent    func() tracker.Torrent
	lastAnnounce  time.Time
	HasAnnounced  bool
	responseC     chan *tracker.AnnounceResponse
	errC          chan error
	closeC        chan struct{}
	doneC         chan struct{}

	needMorePeers  bool
	mNeedMorePeers sync.RWMutex
	needMorePeersC chan struct{}
}

func NewPeriodicalAnnouncer(trk tracker.Tracker, numWant int, minInterval time.Duration, getTorrent func() tracker.Torrent, completedC chan struct{}, newPeers chan []*net.TCPAddr, l logger.Logger) *PeriodicalAnnouncer {
	return &PeriodicalAnnouncer{
		Tracker:        trk,
		status:         NotContactedYet,
		statsCommandC:  make(chan statsRequest),
		numWant:        numWant,
		minInterval:    minInterval,
		log:            l,
		completedC:     completedC,
		newPeers:       newPeers,
		getTorrent:     getTorrent,
		needMorePeersC: make(chan struct{}, 1),
		responseC:      make(chan *tracker.AnnounceResponse),
		errC:           make(chan error),
		closeC:         make(chan struct{}),
		doneC:          make(chan struct{}),
		backoff: &backoff.ExponentialBackOff{
			InitialInterval:     5 * time.Second,
			RandomizationFactor: 0.5,
			Multiplier:          2,
			MaxInterval:         30 * time.Minute,
			MaxElapsedTime:      0, // never stop
			Clock:               backoff.SystemClock,
		},
	}
}

func (a *PeriodicalAnnouncer) Close() {
	close(a.closeC)
	<-a.doneC
}

type statsRequest struct {
	Response chan Stats
}

func (a *PeriodicalAnnouncer) Stats() Stats {
	var stats Stats
	req := statsRequest{Response: make(chan Stats, 1)}
	select {
	case a.statsCommandC <- req:
	case <-a.closeC:
	}
	select {
	case stats = <-req.Response:
	case <-a.closeC:
	}
	return stats
}

func (a *PeriodicalAnnouncer) NeedMorePeers(val bool) {
	a.mNeedMorePeers.Lock()
	a.needMorePeers = val
	a.mNeedMorePeers.Unlock()
	select {
	case a.needMorePeersC <- struct{}{}:
	case <-a.doneC:
	default:
	}
}

func (a *PeriodicalAnnouncer) Run() {
	defer close(a.doneC)
	a.backoff.Reset()

	timer := time.NewTimer(math.MaxInt64)
	defer timer.Stop()

	// BEP 0003: No completed is sent if the file was complete when started.
	select {
	case <-a.completedC:
		a.completedC = nil
	default:
	}

	ctx, cancel := context.WithCancel(context.Background())
	go a.announce(ctx, tracker.EventStarted, a.numWant)
	a.status = Contacting
	for {
		select {
		case <-timer.C:
			if a.status == Contacting {
				break
			}
			go a.announce(ctx, tracker.EventNone, a.numWant)
			a.status = Contacting
		case resp := <-a.responseC:
			a.status = Working
			a.lastAnnounce = time.Now()
			a.seeders = int(resp.Seeders)
			a.leechers = int(resp.Leechers)
			a.interval = resp.Interval
			if resp.MinInterval > 0 {
				a.minInterval = resp.MinInterval
			}
			a.HasAnnounced = true
			a.lastError = nil
			a.backoff.Reset()
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if needMorePeers {
				timer.Reset(a.minInterval)
			} else {
				timer.Reset(a.interval)
			}
		case err := <-a.errC:
			a.status = NotWorking
			a.lastAnnounce = time.Now()
			// Give more friendly error to the user
			a.lastError = newAnnounceError(err)
			if a.lastError.Unknown {
				a.log.Errorln("announce error:", a.lastError.ErrorWithType())
			} else {
				a.log.Debugln("announce error:", a.lastError.Err.Error())
			}
			if terr, ok := a.lastError.Err.(*tracker.Error); ok && terr.RetryIn > 0 {
				timer.Reset(terr.RetryIn)
			} else {
				timer.Reset(a.backoff.NextBackOff())
			}
		case <-a.needMorePeersC:
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if a.status == Contacting {
				break
			}
			if needMorePeers {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.minInterval)))
			} else {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.interval)))
			}
		case <-a.completedC:
			if a.status == Contacting {
				cancel()
				ctx, cancel = context.WithCancel(context.Background())
			}
			go a.announce(ctx, tracker.EventCompleted, 0)
			a.status = Contacting
			a.completedC = nil // do not send more than one "completed" event
		case req := <-a.statsCommandC:
			req.Response <- a.stats()
		case <-a.closeC:
			cancel()
			return
		}
	}
}

func (a *PeriodicalAnnouncer) announce(ctx context.Context, event tracker.Event, numWant int) {
	announce(ctx, a.Tracker, event, numWant, a.getTorrent(), a.responseC, a.errC)
}

type Stats struct {
	Status   Status
	Error    *AnnounceError
	Seeders  int
	Leechers int
}

func (a *PeriodicalAnnouncer) stats() Stats {
	return Stats{
		Status:   a.status,
		Error:    a.lastError,
		Seeders:  a.seeders,
		Leechers: a.leechers,
	}
}

type AnnounceError struct {
	Err     error
	Message string
	Unknown bool
}

func newAnnounceError(err error) (e *AnnounceError) {
	e = &AnnounceError{Err: err}
	switch err := err.(type) {
	case *net.DNSError:
		s := err.Error()
		if strings.HasSuffix(s, "no such host") {
			e.Message = "host not found: " + err.Name
			return
		}
	case *url.Error:
		s := err.Error()
		if strings.HasSuffix(s, "connection refused") {
			e.Message = "tracker refused the connection"
			return
		}
	case net.Error:
		if err.Timeout() {
			e.Message = "timeout contacting tracker"
			return
		}
	case *httptracker.StatusError:
		if err.Code == 403 || err.Code == 404 {
			e.Message = "tracker returned http status: " + strconv.Itoa(err.Code)
			return
		}
	case *tracker.Error:
		e.Message = "announce error: " + err.FailureReason
		return
	}
	e.Message = "unknown error in announce"
	e.Unknown = true
	return
}

func (e *AnnounceError) ErrorWithType() string {
	return reflect.TypeOf(e.Err).String() + ": " + e.Err.Error()
}
<commit_msg>handle 5xx errors<commit_after>package announcer

import (
	"context"
	"math"
	"net"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/tracker"
	"github.com/cenkalti/rain/internal/tracker/httptracker"
)

type Status int

const (
	NotContactedYet Status = iota
	Contacting
	Working
	NotWorking
)

type PeriodicalAnnouncer struct {
	Tracker       tracker.Tracker
	status        Status
	statsCommandC chan statsRequest
	numWant       int
	interval      time.Duration
	minInterval   time.Duration
	seeders       int
	leechers      int
	lastError     *AnnounceError
	log           logger.Logger
	completedC    chan struct{}
	newPeers      chan []*net.TCPAddr
	backoff       backoff.BackOff
	getTorrent    func() tracker.Torrent
	lastAnnounce  time.Time
	HasAnnounced  bool
	responseC     chan *tracker.AnnounceResponse
	errC          chan error
	closeC        chan struct{}
	doneC         chan struct{}

	needMorePeers  bool
	mNeedMorePeers sync.RWMutex
	needMorePeersC chan struct{}
}

func NewPeriodicalAnnouncer(trk tracker.Tracker, numWant int, minInterval time.Duration, getTorrent func() tracker.Torrent, completedC chan struct{}, newPeers chan []*net.TCPAddr, l logger.Logger) *PeriodicalAnnouncer {
	return &PeriodicalAnnouncer{
		Tracker:        trk,
		status:         NotContactedYet,
		statsCommandC:  make(chan statsRequest),
		numWant:        numWant,
		minInterval:    minInterval,
		log:            l,
		completedC:     completedC,
		newPeers:       newPeers,
		getTorrent:     getTorrent,
		needMorePeersC: make(chan struct{}, 1),
		responseC:      make(chan *tracker.AnnounceResponse),
		errC:           make(chan error),
		closeC:         make(chan struct{}),
		doneC:          make(chan struct{}),
		backoff: &backoff.ExponentialBackOff{
			InitialInterval:     5 * time.Second,
			RandomizationFactor: 0.5,
			Multiplier:          2,
			MaxInterval:         30 * time.Minute,
			MaxElapsedTime:      0, // never stop
			Clock:               backoff.SystemClock,
		},
	}
}

func (a *PeriodicalAnnouncer) Close() {
	close(a.closeC)
	<-a.doneC
}

type statsRequest struct {
	Response chan Stats
}

func (a *PeriodicalAnnouncer) Stats() Stats {
	var stats Stats
	req := statsRequest{Response: make(chan Stats, 1)}
	select {
	case a.statsCommandC <- req:
	case <-a.closeC:
	}
	select {
	case stats = <-req.Response:
	case <-a.closeC:
	}
	return stats
}

func (a *PeriodicalAnnouncer) NeedMorePeers(val bool) {
	a.mNeedMorePeers.Lock()
	a.needMorePeers = val
	a.mNeedMorePeers.Unlock()
	select {
	case a.needMorePeersC <- struct{}{}:
	case <-a.doneC:
	default:
	}
}

func (a *PeriodicalAnnouncer) Run() {
	defer close(a.doneC)
	a.backoff.Reset()

	timer := time.NewTimer(math.MaxInt64)
	defer timer.Stop()

	// BEP 0003: No completed is sent if the file was complete when started.
	select {
	case <-a.completedC:
		a.completedC = nil
	default:
	}

	ctx, cancel := context.WithCancel(context.Background())
	go a.announce(ctx, tracker.EventStarted, a.numWant)
	a.status = Contacting
	for {
		select {
		case <-timer.C:
			if a.status == Contacting {
				break
			}
			go a.announce(ctx, tracker.EventNone, a.numWant)
			a.status = Contacting
		case resp := <-a.responseC:
			a.status = Working
			a.lastAnnounce = time.Now()
			a.seeders = int(resp.Seeders)
			a.leechers = int(resp.Leechers)
			a.interval = resp.Interval
			if resp.MinInterval > 0 {
				a.minInterval = resp.MinInterval
			}
			a.HasAnnounced = true
			a.lastError = nil
			a.backoff.Reset()
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if needMorePeers {
				timer.Reset(a.minInterval)
			} else {
				timer.Reset(a.interval)
			}
		case err := <-a.errC:
			a.status = NotWorking
			a.lastAnnounce = time.Now()
			// Give more friendly error to the user
			a.lastError = newAnnounceError(err)
			if a.lastError.Unknown {
				a.log.Errorln("announce error:", a.lastError.ErrorWithType())
			} else {
				a.log.Debugln("announce error:", a.lastError.Err.Error())
			}
			if terr, ok := a.lastError.Err.(*tracker.Error); ok && terr.RetryIn > 0 {
				timer.Reset(terr.RetryIn)
			} else {
				timer.Reset(a.backoff.NextBackOff())
			}
		case <-a.needMorePeersC:
			a.mNeedMorePeers.RLock()
			needMorePeers := a.needMorePeers
			a.mNeedMorePeers.RUnlock()
			if a.status == Contacting {
				break
			}
			if needMorePeers {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.minInterval)))
			} else {
				timer.Reset(time.Until(a.lastAnnounce.Add(a.interval)))
			}
		case <-a.completedC:
			if a.status == Contacting {
				cancel()
				ctx, cancel = context.WithCancel(context.Background())
			}
			go a.announce(ctx, tracker.EventCompleted, 0)
			a.status = Contacting
			a.completedC = nil // do not send more than one "completed" event
		case req := <-a.statsCommandC:
			req.Response <- a.stats()
		case <-a.closeC:
			cancel()
			return
		}
	}
}

func (a *PeriodicalAnnouncer) announce(ctx context.Context, event tracker.Event, numWant int) {
	announce(ctx, a.Tracker, event, numWant, a.getTorrent(), a.responseC, a.errC)
}

type Stats struct {
	Status   Status
	Error    *AnnounceError
	Seeders  int
	Leechers int
}

func (a *PeriodicalAnnouncer) stats() Stats {
	return Stats{
		Status:   a.status,
		Error:    a.lastError,
		Seeders:  a.seeders,
		Leechers: a.leechers,
	}
}

type AnnounceError struct {
	Err     error
	Message string
	Unknown bool
}

func newAnnounceError(err error) (e *AnnounceError) {
	e = &AnnounceError{Err: err}
	switch err := err.(type) {
	case *net.DNSError:
		s := err.Error()
		if strings.HasSuffix(s, "no such host") {
			e.Message = "host not found: " + err.Name
			return
		}
	case *url.Error:
		s := err.Error()
		if strings.HasSuffix(s, "connection refused") {
			e.Message = "tracker refused the connection"
			return
		}
	case net.Error:
		if err.Timeout() {
			e.Message = "timeout contacting tracker"
			return
		}
	case *httptracker.StatusError:
		if err.Code >= 400 {
			e.Message = "tracker returned http status: " + strconv.Itoa(err.Code)
			return
		}
	case *tracker.Error:
		e.Message = "announce error: " + err.FailureReason
		return
	}
	e.Message = "unknown error in announce"
	e.Unknown = true
	return
}

func (e *AnnounceError) ErrorWithType() string {
	return reflect.TypeOf(e.Err).String() + ": " + e.Err.Error()
}
<|endoftext|>