<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/objectclient"
	"github.com/Symantec/Dominator/objectserver"
	"io"
	"net/rpc"
	"os"
	"path"
	"syscall"
)

var dirPerms os.FileMode = syscall.S_IRWXU

func getImageSubcommand(imageClient *rpc.Client,
	objectClient *objectclient.ObjectClient, args []string) {
	err := getImageAndWrite(imageClient, objectClient, args[0], args[1])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error getting image\t%s\n", err)
		os.Exit(1)
	}
	os.Exit(0)
}

func getImageAndWrite(imageClient *rpc.Client,
	objectClient *objectclient.ObjectClient, name, dirname string) error {
	inodesDir := dirname + ".inodes"
	if err := os.Mkdir(inodesDir, dirPerms); err != nil {
		return err
	}
	defer os.RemoveAll(inodesDir)
	fs, err := getImage(imageClient, name)
	if err != nil {
		return err
	}
	var statfs syscall.Statfs_t
	if err := syscall.Statfs(inodesDir, &statfs); err != nil {
		return errors.New(fmt.Sprintf("Unable to Statfs: %s %s\n",
			inodesDir, err))
	}
	if fs.TotalDataBytes > uint64(statfs.Bsize)*statfs.Bfree {
		return errors.New("image will not fit on file-system")
	}
	hashes, inums, lengths := getHashes(fs)
	err = writeObjects(objectClient, hashes, inums, lengths, inodesDir)
	if err != nil {
		return err
	}
	if err := writeInodes(fs.InodeTable, inodesDir); err != nil {
		return err
	}
	if err = fs.DirectoryInode.Write(dirname); err != nil {
		return err
	}
	return buildTree(&fs.DirectoryInode, dirname, inodesDir)
}

func getHashes(fs *filesystem.FileSystem) ([]hash.Hash, []uint64, []uint64) {
	hashes := make([]hash.Hash, 0, fs.NumRegularInodes)
	inums := make([]uint64, 0, fs.NumRegularInodes)
	lengths := make([]uint64, 0, fs.NumRegularInodes)
	for inum, inode := range fs.InodeTable {
		if inode, ok := inode.(*filesystem.RegularInode); ok {
			if inode.Size > 0 {
				hashes = append(hashes, inode.Hash)
				inums = append(inums, inum)
				lengths = append(lengths, inode.Size)
			}
		}
	}
	return hashes, inums, lengths
}

func writeObjects(objectClient *objectclient.ObjectClient, hashes []hash.Hash,
	inums []uint64, lenghts []uint64, inodesDir string) error {
	objectsReader, err := objectClient.GetObjects(hashes)
	if err != nil {
		return errors.New(fmt.Sprintf("Error getting object reader: %s\n",
			err.Error()))
	}
	for index, hash := range hashes {
		err = writeObject(objectsReader, hash, inums[index], lenghts[index],
			inodesDir)
		if err != nil {
			return err
		}
	}
	return nil
}

func writeObject(objectsReader objectserver.ObjectsReader, hash hash.Hash,
	inodeNumber uint64, length uint64, inodesDir string) error {
	rlength, reader, err := objectsReader.NextObject()
	if err != nil {
		return err
	}
	defer reader.Close()
	if rlength != length {
		return errors.New("mismatched lengths")
	}
	filename := path.Join(inodesDir, fmt.Sprintf("%d", inodeNumber))
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	writer := bufio.NewWriter(file)
	defer writer.Flush()
	if _, err = io.Copy(writer, reader); err != nil {
		return errors.New(fmt.Sprintf("error copying: %s", err.Error()))
	}
	return nil
}

func writeInodes(inodeTable filesystem.InodeTable, inodesDir string) error {
	for inodeNumber, inode := range inodeTable {
		filename := path.Join(inodesDir, fmt.Sprintf("%d", inodeNumber))
		switch inode := inode.(type) {
		case *filesystem.RegularInode:
			if inode.Size < 1 {
				if _, err := os.Create(filename); err != nil {
					return err
				}
			}
			err := os.Lchown(filename, int(inode.Uid), int(inode.Gid))
			if err != nil && !os.IsPermission(err) {
				return err
			}
			if err := syscall.Chmod(filename, uint32(inode.Mode)); err != nil {
				return err
			}
		case *filesystem.SymlinkInode:
			if err := inode.Write(filename); err != nil {
				return err
			}
		case *filesystem.SpecialInode:
			if err := inode.Write(filename); err != nil {
				return err
			}
		case *filesystem.DirectoryInode:
			if err := inode.Write(filename); err != nil {
				return err
			}
		default:
			return errors.New("unsupported inode type")
		}
	}
	return nil
}

func buildTree(directory *filesystem.DirectoryInode,
	myPathName, inodesDir string) error {
	for _, dirent := range directory.EntryList {
		oldPath := path.Join(inodesDir, fmt.Sprintf("%d", dirent.InodeNumber))
		newPath := path.Join(myPathName, dirent.Name)
		if inode, ok := dirent.Inode().(*filesystem.DirectoryInode); ok {
			if err := os.Rename(oldPath, newPath); err != nil {
				return err
			}
			if err := buildTree(inode, newPath, inodesDir); err != nil {
				return err
			}
		} else {
			if err := os.Link(oldPath, newPath); err != nil {
				if !os.IsNotExist(err) {
					return err
				}
			}
		}
	}
	return nil
}
<commit_msg>Fix bug in imagetool get subcommand: restore file times.<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/objectclient"
	"github.com/Symantec/Dominator/objectserver"
	"io"
	"net/rpc"
	"os"
	"path"
	"syscall"
	"time"
)

var dirPerms os.FileMode = syscall.S_IRWXU

func getImageSubcommand(imageClient *rpc.Client,
	objectClient *objectclient.ObjectClient, args []string) {
	err := getImageAndWrite(imageClient, objectClient, args[0], args[1])
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error getting image\t%s\n", err)
		os.Exit(1)
	}
	os.Exit(0)
}

func getImageAndWrite(imageClient *rpc.Client,
	objectClient *objectclient.ObjectClient, name, dirname string) error {
	inodesDir := dirname + ".inodes"
	if err := os.Mkdir(inodesDir, dirPerms); err != nil {
		return err
	}
	defer os.RemoveAll(inodesDir)
	fs, err := getImage(imageClient, name)
	if err != nil {
		return err
	}
	var statfs syscall.Statfs_t
	if err := syscall.Statfs(inodesDir, &statfs); err != nil {
		return errors.New(fmt.Sprintf("Unable to Statfs: %s %s\n",
			inodesDir, err))
	}
	if fs.TotalDataBytes > uint64(statfs.Bsize)*statfs.Bfree {
		return errors.New("image will not fit on file-system")
	}
	hashes, inums, lengths := getHashes(fs)
	err = writeObjects(objectClient, hashes, inums, lengths, inodesDir)
	if err != nil {
		return err
	}
	if err := writeInodes(fs.InodeTable, inodesDir); err != nil {
		return err
	}
	if err = fs.DirectoryInode.Write(dirname); err != nil {
		return err
	}
	return buildTree(&fs.DirectoryInode, dirname, inodesDir)
}

func getHashes(fs *filesystem.FileSystem) ([]hash.Hash, []uint64, []uint64) {
	hashes := make([]hash.Hash, 0, fs.NumRegularInodes)
	inums := make([]uint64, 0, fs.NumRegularInodes)
	lengths := make([]uint64, 0, fs.NumRegularInodes)
	for inum, inode := range fs.InodeTable {
		if inode, ok := inode.(*filesystem.RegularInode); ok {
			if inode.Size > 0 {
				hashes = append(hashes, inode.Hash)
				inums = append(inums, inum)
				lengths = append(lengths, inode.Size)
			}
		}
	}
	return hashes, inums, lengths
}

func writeObjects(objectClient *objectclient.ObjectClient, hashes []hash.Hash,
	inums []uint64, lenghts []uint64, inodesDir string) error {
	objectsReader, err := objectClient.GetObjects(hashes)
	if err != nil {
		return errors.New(fmt.Sprintf("Error getting object reader: %s\n",
			err.Error()))
	}
	for index, hash := range hashes {
		err = writeObject(objectsReader, hash, inums[index], lenghts[index],
			inodesDir)
		if err != nil {
			return err
		}
	}
	return nil
}

func writeObject(objectsReader objectserver.ObjectsReader, hash hash.Hash,
	inodeNumber uint64, length uint64, inodesDir string) error {
	rlength, reader, err := objectsReader.NextObject()
	if err != nil {
		return err
	}
	defer reader.Close()
	if rlength != length {
		return errors.New("mismatched lengths")
	}
	filename := path.Join(inodesDir, fmt.Sprintf("%d", inodeNumber))
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	writer := bufio.NewWriter(file)
	defer writer.Flush()
	if _, err = io.Copy(writer, reader); err != nil {
		return errors.New(fmt.Sprintf("error copying: %s", err.Error()))
	}
	return nil
}

func writeInodes(inodeTable filesystem.InodeTable, inodesDir string) error {
	for inodeNumber, inode := range inodeTable {
		filename := path.Join(inodesDir, fmt.Sprintf("%d", inodeNumber))
		switch inode := inode.(type) {
		case *filesystem.RegularInode:
			if inode.Size < 1 {
				if _, err := os.Create(filename); err != nil {
					return err
				}
			}
			err := os.Lchown(filename, int(inode.Uid), int(inode.Gid))
			if err != nil && !os.IsPermission(err) {
				return err
			}
			if err := syscall.Chmod(filename, uint32(inode.Mode)); err != nil {
				return err
			}
			t := time.Unix(inode.MtimeSeconds, int64(inode.MtimeNanoSeconds))
			if err := os.Chtimes(filename, t, t); err != nil {
				return err
			}
		case *filesystem.SymlinkInode:
			if err := inode.Write(filename); err != nil {
				return err
			}
		case *filesystem.SpecialInode:
			if err := inode.Write(filename); err != nil {
				return err
			}
		case *filesystem.DirectoryInode:
			if err := inode.Write(filename); err != nil {
				return err
			}
		default:
			return errors.New("unsupported inode type")
		}
	}
	return nil
}

func buildTree(directory *filesystem.DirectoryInode,
	myPathName, inodesDir string) error {
	for _, dirent := range directory.EntryList {
		oldPath := path.Join(inodesDir, fmt.Sprintf("%d", dirent.InodeNumber))
		newPath := path.Join(myPathName, dirent.Name)
		if inode, ok := dirent.Inode().(*filesystem.DirectoryInode); ok {
			if err := os.Rename(oldPath, newPath); err != nil {
				return err
			}
			if err := buildTree(inode, newPath, inodesDir); err != nil {
				return err
			}
		} else {
			if err := os.Link(oldPath, newPath); err != nil {
				if !os.IsNotExist(err) {
					return err
				}
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package mccli

import "github.com/codegangsta/cli"

var DownloadCommand = cli.Command{
	Name:    "download",
	Aliases: []string{"down", "d"},
	Usage:   "Downloads files, directories or projects",
	Subcommands: []cli.Command{
		downloadProjectCommand,
		downloadFileCommand,
		downloadDirCommand,
	},
}
<commit_msg>Remove download directory as an option.<commit_after>package mccli

import "github.com/codegangsta/cli"

var DownloadCommand = cli.Command{
	Name:    "download",
	Aliases: []string{"down", "d"},
	Usage:   "Downloads files, directories or projects",
	Subcommands: []cli.Command{
		downloadProjectCommand,
		downloadFileCommand,
		//downloadDirCommand,
	},
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"context"

	"github.com/nelhage/taktician/ai"
	"github.com/nelhage/taktician/playtak"
	"github.com/nelhage/taktician/playtak/bot"
	"github.com/nelhage/taktician/tak"
)

const (
	minThink = 5 * time.Second
	maxThink = time.Minute

	undoTimeout = 30 * time.Second

	defaultLevel = 7

	docURL = "http://bit.ly/25h33rC"
)

type Friendly struct {
	client *playtak.Commands
	ai     ai.TakPlayer
	check  *ai.MinimaxAI
	g      *bot.Game

	fpa FPARule

	level    int
	levelSet time.Time

	log *os.File
}

func (f *Friendly) NewGame(g *bot.Game) {
	if time.Now().Sub(f.levelSet) > 1*time.Hour {
		f.level = defaultLevel
	}
	f.g = g
	f.ai = wrapWithBook(g.Size, ai.NewMinimax(f.AIConfig()))
	f.check = ai.NewMinimax(ai.MinimaxConfig{
		Depth:    3,
		Size:     g.Size,
		Debug:    0,
		TableMem: -1,
		Evaluate: ai.EvaluateWinner,
	})
	f.client.Tell(g.Opponent,
		fmt.Sprintf("%s@level %d: %s",
			*user, f.level, docURL))
	if f.fpa != nil {
		if gs := f.fpa.Greeting(g.Color); gs != nil {
			for _, m := range gs {
				f.client.Tell(g.Opponent, m)
			}
		}
	}
}

func (f *Friendly) GameOver() {
	if f.fpa != nil && f.g.Result != "" && f.g.Result != "0-1" && f.g.Result != "1-0" {
		url := f.fpa.SurveyURL()
		if url != "" {
			f.client.Tell(f.g.Opponent,
				fmt.Sprintf("Thanks for playing! Please share your feedback about this rule variation: %s", url))
		}
	}
	if *logFile != "" {
		l, e := os.OpenFile(*logFile,
			os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
		if e != nil {
			log.Printf("log: open(%s): %v", *logFile, e)
		} else {
			defer l.Close()
			var winner tak.Color
			var moves int
			if len(f.g.Positions) > 0 {
				p := f.g.Positions[len(f.g.Positions)-1]
				_, winner = p.GameOver()
				moves = p.MoveNumber()
			}
			fmt.Fprintf(l,
				"game=%s\ttime=%s\tmycolor=%s\tsize=%d\topponent=%s\tlevel=%d\tresult=%s\twinner=%s\tmoves=%d\n",
				f.g.ID,
				time.Now().Format(time.RFC3339),
				f.g.Color,
				f.g.Size,
				f.g.Opponent,
				f.level,
				f.g.Result,
				winner,
				moves,
			)
		}
	}
	f.g = nil
}

func (f *Friendly) GetMove(
	ctx context.Context,
	p *tak.Position,
	mine, theirs time.Duration) tak.Move {
	if f.fpa != nil {
		if p.MoveNumber() > 0 {
			prevP := f.g.Positions[len(f.g.Positions)-2]
			prevM := f.g.Moves[len(f.g.Moves)-1]
			if err := f.fpa.LegalMove(prevP, prevM); err != nil {
				f.client.SendCommand(f.g.GameStr, "Resign")
				f.client.Tell(f.g.Opponent, err.Error())
				<-ctx.Done()
				return tak.Move{}
			}
		}
	}

	if p.ToMove() != f.g.Color {
		return tak.Move{}
	}

	if f.fpa != nil {
		m, ok := f.fpa.GetMove(p)
		if ok {
			return m
		}
	}
	var deadline <-chan time.Time
	if f.waitUndo(p) {
		deadline = time.After(undoTimeout)
	} else {
		deadline = time.After(minThink)
	}
	ctx, cancel := context.WithDeadline(ctx, time.Now().Add(maxThink))
	defer cancel()
	m := f.ai.GetMove(ctx, p)
	select {
	case <-deadline:
	case <-ctx.Done():
	}

	return m
}

func (f *Friendly) Config(size int) tak.Config {
	cfg := tak.Config{Size: size}
	if f.fpa != nil {
		cfg.BlackWinsTies = true
	}
	return cfg
}

func (f *Friendly) waitUndo(p *tak.Position) bool {
	ctx := context.Background()
	_, v, st := f.check.Analyze(ctx, p)
	if v < ai.WinThreshold || st.Depth > 1 {
		return false
	}
	_, v, st = f.check.Analyze(ctx, f.g.Positions[len(f.g.Positions)-2])
	if v > -ai.WinThreshold {
		return true
	}
	return false
}

func (f *Friendly) handleCommand(who, cmd, arg string) string {
	switch strings.ToLower(cmd) {
	case "level":
		if arg == "max" {
			f.level = 100
			f.levelSet = time.Now()
			return "OK! I'll play as best as I can!"
		}
		l, e := strconv.ParseUint(arg, 10, 64)
		if e != nil {
			log.Printf("bad level: %v", e)
			return ""
		}
		if int(l) < 1 || int(l) > len(levels)+1 {
			return fmt.Sprintf("I only know about levels up to %d", len(levels)+1)
		}
		f.level = int(l)
		f.levelSet = time.Now()
		if f.g == nil || who != f.g.Opponent {
			return fmt.Sprintf("OK! I'll play at level %d for future games.", l)
		} else if f.g != nil {
			f.ai = wrapWithBook(f.g.Size, ai.NewMinimax(f.AIConfig()))
			return fmt.Sprintf("OK! I'll play at level %d, starting right now.", l)
		}
	case "size":
		sz, err := strconv.Atoi(arg)
		if err != nil {
			log.Printf("bad size size=%q", arg)
			return ""
		}
		if sz >= 3 && sz <= 8 {
			*size = sz
			f.client.SendCommand("Seek",
				strconv.Itoa(*size),
				strconv.Itoa(int(gameTime.Seconds())),
				strconv.Itoa(int(increment.Seconds())))
		}
	case "help":
		return fmt.Sprintf("[%s@level %d]: %s",
			*user, f.level, docURL)
	}
	return ""
}

func (f *Friendly) HandleTell(who string, msg string) {
	bits := strings.SplitN(msg, " ", 2)
	cmd := bits[0]
	var arg string
	if len(bits) == 2 {
		arg = bits[1]
	}

	if reply := f.handleCommand(who, cmd, arg); reply != "" {
		f.client.Tell(who, reply)
	}
}

func (f *Friendly) HandleChat(room string, who string, msg string) {
	log.Printf("chat room=%q from=%q msg=%q", room, who, msg)
	cmd, arg := parseCommand(msg)
	if cmd == "" {
		return
	}
	if reply := f.handleCommand(who, cmd, arg); reply != "" {
		f.client.Shout(room, reply)
	}
}

func (f *Friendly) AIConfig() ai.MinimaxConfig {
	cfg := ai.MinimaxConfig{
		Size:  f.g.Size,
		Debug: *debug,

		NoSort:   !*sort,
		TableMem: *tableMem,
		MultiCut: *multicut,
	}
	cfg.Depth, cfg.Evaluate = f.levelSettings(f.g.Size, f.level)

	return cfg
}

var (
	easyWeights = ai.Weights{
		TopFlat: 100,
	}
	medWeights = ai.Weights{
		TopFlat:          200,
		Standing:         100,
		Capstone:         150,
		FlatCaptives:     ai.FlatScores{Hard: 50},
		StandingCaptives: ai.FlatScores{Hard: 50},
		CapstoneCaptives: ai.FlatScores{Hard: 50},
		Groups:           [8]int{0, 0, 0, 100, 200, 300, 310, 320},
	}
)

func constw(w ai.Weights) func(int) *ai.Weights {
	return func(int) *ai.Weights { return &w }
}

func indexw(ws []ai.Weights) func(int) *ai.Weights {
	return func(sz int) *ai.Weights {
		if sz < len(ws) {
			return &ws[sz]
		}
		panic("bad weights/size")
	}
}

var levels = []struct {
	depth   int
	weights func(size int) *ai.Weights
}{
	{2, constw(easyWeights)},
	{2, constw(medWeights)},
	{2, indexw(ai.DefaultWeights)},
	{3, constw(easyWeights)},
	{3, constw(medWeights)},
	{4, constw(medWeights)},
	{3, indexw(ai.DefaultWeights)},
	{5, constw(easyWeights)},
	{5, constw(medWeights)},
	{4, indexw(ai.DefaultWeights)},
	{5, indexw(ai.DefaultWeights)},
	{7, indexw(ai.DefaultWeights)},
	{0, indexw(ai.DefaultWeights)},
}

func (f *Friendly) levelSettings(size int, level int) (int, ai.EvaluationFunc) {
	if level == 0 {
		level = 3
	}
	if level > len(levels) {
		level = len(levels)
	}
	s := levels[level-1]
	return s.depth, ai.MakeEvaluator(size, s.weights(size))
}

func (f *Friendly) AcceptUndo() bool {
	return true
}
<commit_msg>make logs straight-up TSV<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"context"

	"github.com/nelhage/taktician/ai"
	"github.com/nelhage/taktician/playtak"
	"github.com/nelhage/taktician/playtak/bot"
	"github.com/nelhage/taktician/tak"
)

const (
	minThink = 5 * time.Second
	maxThink = time.Minute

	undoTimeout = 30 * time.Second

	defaultLevel = 7

	docURL = "http://bit.ly/25h33rC"
)

type Friendly struct {
	client *playtak.Commands
	ai     ai.TakPlayer
	check  *ai.MinimaxAI
	g      *bot.Game

	fpa FPARule

	level    int
	levelSet time.Time

	log *os.File
}

func (f *Friendly) NewGame(g *bot.Game) {
	if time.Now().Sub(f.levelSet) > 1*time.Hour {
		f.level = defaultLevel
	}
	f.g = g
	f.ai = wrapWithBook(g.Size, ai.NewMinimax(f.AIConfig()))
	f.check = ai.NewMinimax(ai.MinimaxConfig{
		Depth:    3,
		Size:     g.Size,
		Debug:    0,
		TableMem: -1,
		Evaluate: ai.EvaluateWinner,
	})
	f.client.Tell(g.Opponent,
		fmt.Sprintf("%s@level %d: %s",
			*user, f.level, docURL))
	if f.fpa != nil {
		if gs := f.fpa.Greeting(g.Color); gs != nil {
			for _, m := range gs {
				f.client.Tell(g.Opponent, m)
			}
		}
	}
}

func (f *Friendly) GameOver() {
	if f.fpa != nil && f.g.Result != "" && f.g.Result != "0-1" && f.g.Result != "1-0" {
		url := f.fpa.SurveyURL()
		if url != "" {
			f.client.Tell(f.g.Opponent,
				fmt.Sprintf("Thanks for playing! Please share your feedback about this rule variation: %s", url))
		}
	}
	if *logFile != "" {
		l, e := os.OpenFile(*logFile,
			os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
		if e != nil {
			log.Printf("log: open(%s): %v", *logFile, e)
		} else {
			defer l.Close()
			var winner tak.Color
			var moves int
			if len(f.g.Positions) > 0 {
				p := f.g.Positions[len(f.g.Positions)-1]
				_, winner = p.GameOver()
				moves = p.MoveNumber()
			}
			fmt.Fprintf(l,
				"%s\t%s\t%s\t%d\t%s\t%d\t%s\t%s\t%d\n",
				f.g.ID,
				time.Now().Format(time.RFC3339),
				f.g.Color,
				f.g.Size,
				f.g.Opponent,
				f.level,
				f.g.Result,
				winner,
				moves,
			)
		}
	}
	f.g = nil
}

func (f *Friendly) GetMove(
	ctx context.Context,
	p *tak.Position,
	mine, theirs time.Duration) tak.Move {
	if f.fpa != nil {
		if p.MoveNumber() > 0 {
			prevP := f.g.Positions[len(f.g.Positions)-2]
			prevM := f.g.Moves[len(f.g.Moves)-1]
			if err := f.fpa.LegalMove(prevP, prevM); err != nil {
				f.client.SendCommand(f.g.GameStr, "Resign")
				f.client.Tell(f.g.Opponent, err.Error())
				<-ctx.Done()
				return tak.Move{}
			}
		}
	}

	if p.ToMove() != f.g.Color {
		return tak.Move{}
	}

	if f.fpa != nil {
		m, ok := f.fpa.GetMove(p)
		if ok {
			return m
		}
	}
	var deadline <-chan time.Time
	if f.waitUndo(p) {
		deadline = time.After(undoTimeout)
	} else {
		deadline = time.After(minThink)
	}
	ctx, cancel := context.WithDeadline(ctx, time.Now().Add(maxThink))
	defer cancel()
	m := f.ai.GetMove(ctx, p)
	select {
	case <-deadline:
	case <-ctx.Done():
	}

	return m
}

func (f *Friendly) Config(size int) tak.Config {
	cfg := tak.Config{Size: size}
	if f.fpa != nil {
		cfg.BlackWinsTies = true
	}
	return cfg
}

func (f *Friendly) waitUndo(p *tak.Position) bool {
	ctx := context.Background()
	_, v, st := f.check.Analyze(ctx, p)
	if v < ai.WinThreshold || st.Depth > 1 {
		return false
	}
	_, v, st = f.check.Analyze(ctx, f.g.Positions[len(f.g.Positions)-2])
	if v > -ai.WinThreshold {
		return true
	}
	return false
}

func (f *Friendly) handleCommand(who, cmd, arg string) string {
	switch strings.ToLower(cmd) {
	case "level":
		if arg == "max" {
			f.level = 100
			f.levelSet = time.Now()
			return "OK! I'll play as best as I can!"
		}
		l, e := strconv.ParseUint(arg, 10, 64)
		if e != nil {
			log.Printf("bad level: %v", e)
			return ""
		}
		if int(l) < 1 || int(l) > len(levels)+1 {
			return fmt.Sprintf("I only know about levels up to %d", len(levels)+1)
		}
		f.level = int(l)
		f.levelSet = time.Now()
		if f.g == nil || who != f.g.Opponent {
			return fmt.Sprintf("OK! I'll play at level %d for future games.", l)
		} else if f.g != nil {
			f.ai = wrapWithBook(f.g.Size, ai.NewMinimax(f.AIConfig()))
			return fmt.Sprintf("OK! I'll play at level %d, starting right now.", l)
		}
	case "size":
		sz, err := strconv.Atoi(arg)
		if err != nil {
			log.Printf("bad size size=%q", arg)
			return ""
		}
		if sz >= 3 && sz <= 8 {
			*size = sz
			f.client.SendCommand("Seek",
				strconv.Itoa(*size),
				strconv.Itoa(int(gameTime.Seconds())),
				strconv.Itoa(int(increment.Seconds())))
		}
	case "help":
		return fmt.Sprintf("[%s@level %d]: %s",
			*user, f.level, docURL)
	}
	return ""
}

func (f *Friendly) HandleTell(who string, msg string) {
	bits := strings.SplitN(msg, " ", 2)
	cmd := bits[0]
	var arg string
	if len(bits) == 2 {
		arg = bits[1]
	}

	if reply := f.handleCommand(who, cmd, arg); reply != "" {
		f.client.Tell(who, reply)
	}
}

func (f *Friendly) HandleChat(room string, who string, msg string) {
	log.Printf("chat room=%q from=%q msg=%q", room, who, msg)
	cmd, arg := parseCommand(msg)
	if cmd == "" {
		return
	}
	if reply := f.handleCommand(who, cmd, arg); reply != "" {
		f.client.Shout(room, reply)
	}
}

func (f *Friendly) AIConfig() ai.MinimaxConfig {
	cfg := ai.MinimaxConfig{
		Size:  f.g.Size,
		Debug: *debug,

		NoSort:   !*sort,
		TableMem: *tableMem,
		MultiCut: *multicut,
	}
	cfg.Depth, cfg.Evaluate = f.levelSettings(f.g.Size, f.level)

	return cfg
}

var (
	easyWeights = ai.Weights{
		TopFlat: 100,
	}
	medWeights = ai.Weights{
		TopFlat:          200,
		Standing:         100,
		Capstone:         150,
		FlatCaptives:     ai.FlatScores{Hard: 50},
		StandingCaptives: ai.FlatScores{Hard: 50},
		CapstoneCaptives: ai.FlatScores{Hard: 50},
		Groups:           [8]int{0, 0, 0, 100, 200, 300, 310, 320},
	}
)

func constw(w ai.Weights) func(int) *ai.Weights {
	return func(int) *ai.Weights { return &w }
}

func indexw(ws []ai.Weights) func(int) *ai.Weights {
	return func(sz int) *ai.Weights {
		if sz < len(ws) {
			return &ws[sz]
		}
		panic("bad weights/size")
	}
}

var levels = []struct {
	depth   int
	weights func(size int) *ai.Weights
}{
	{2, constw(easyWeights)},
	{2, constw(medWeights)},
	{2, indexw(ai.DefaultWeights)},
	{3, constw(easyWeights)},
	{3, constw(medWeights)},
	{4, constw(medWeights)},
	{3, indexw(ai.DefaultWeights)},
	{5, constw(easyWeights)},
	{5, constw(medWeights)},
	{4, indexw(ai.DefaultWeights)},
	{5, indexw(ai.DefaultWeights)},
	{7, indexw(ai.DefaultWeights)},
	{0, indexw(ai.DefaultWeights)},
}

func (f *Friendly) levelSettings(size int, level int) (int, ai.EvaluationFunc) {
	if level == 0 {
		level = 3
	}
	if level > len(levels) {
		level = len(levels)
	}
	s := levels[level-1]
	return s.depth, ai.MakeEvaluator(size, s.weights(size))
}

func (f *Friendly) AcceptUndo() bool {
	return true
}
<|endoftext|>


<commit_before>// Copyright 2010 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.

// This file contains tests for the printf checker.

package testdata

import (
	"fmt"
	"os"
	"unsafe" // just for test case printing unsafe.Pointer
)

func UnsafePointerPrintfTest() {
	var up unsafe.Pointer
	fmt.Printf("%p, %x %X", up, up, up)
}

// Error methods that do not satisfy the Error interface and should be checked.
type errorTest1 int

func (errorTest1) Error(...interface{}) string {
	return "hi"
}

type errorTest2 int // Analogous to testing's *T type.
func (errorTest2) Error(...interface{}) {
}

type errorTest3 int

func (errorTest3) Error() { // No return value.
}

type errorTest4 int

func (errorTest4) Error() int { // Different return type.
	return 3
}

type errorTest5 int

func (errorTest5) error() { // niladic; don't complain if no args (was bug)
}

// This function never executes, but it serves as a simple test for the program.
// Test with make test.
func PrintfTests() {
	var b bool
	var i int
	var r rune
	var s string
	var x float64
	var p *int
	var imap map[int]int
	var fslice []float64
	var c complex64
	// Some good format/argtypes
	fmt.Printf("")
	fmt.Printf("%b %b %b", 3, i, x)
	fmt.Printf("%c %c %c %c", 3, i, 'x', r)
	fmt.Printf("%d %d %d", 3, i, imap)
	fmt.Printf("%e %e %e %e", 3e9, x, fslice, c)
	fmt.Printf("%E %E %E %E", 3e9, x, fslice, c)
	fmt.Printf("%f %f %f %f", 3e9, x, fslice, c)
	fmt.Printf("%F %F %F %F", 3e9, x, fslice, c)
	fmt.Printf("%g %g %g %g", 3e9, x, fslice, c)
	fmt.Printf("%G %G %G %G", 3e9, x, fslice, c)
	fmt.Printf("%o %o", 3, i)
	fmt.Printf("%p %p", p, nil)
	fmt.Printf("%q %q %q %q", 3, i, 'x', r)
	fmt.Printf("%s %s %s", "hi", s, []byte{65})
	fmt.Printf("%t %t", true, b)
	fmt.Printf("%T %T", 3, i)
	fmt.Printf("%U %U", 3, i)
	fmt.Printf("%v %v", 3, i)
	fmt.Printf("%x %x %x %x", 3, i, "hi", s)
	fmt.Printf("%X %X %X %X", 3, i, "hi", s)
	fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
	fmt.Printf("%s", &stringerv)
	fmt.Printf("%v", &stringerv)
	fmt.Printf("%T", &stringerv)
	fmt.Printf("%v", notstringerv)
	fmt.Printf("%T", notstringerv)
	fmt.Printf("%q", stringerarrayv)
	fmt.Printf("%v", stringerarrayv)
	fmt.Printf("%s", stringerarrayv)
	fmt.Printf("%v", notstringerarrayv)
	fmt.Printf("%T", notstringerarrayv)
	fmt.Printf("%*%", 2)               // Ridiculous but allowed.
	fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say.

	fmt.Printf("%g", 1+2i)
	// Some bad format/argTypes
	fmt.Printf("%b", "hi")                     // ERROR "arg .hi. for printf verb %b of wrong type"
	fmt.Printf("%b", c)                        // ERROR "arg c for printf verb %b of wrong type"
	fmt.Printf("%b", 1+2i)                     // ERROR "arg 1 \+ 2i for printf verb %b of wrong type"
	fmt.Printf("%c", 2.3)                      // ERROR "arg 2.3 for printf verb %c of wrong type"
	fmt.Printf("%d", 2.3)                      // ERROR "arg 2.3 for printf verb %d of wrong type"
	fmt.Printf("%e", "hi")                     // ERROR "arg .hi. for printf verb %e of wrong type"
	fmt.Printf("%E", true)                     // ERROR "arg true for printf verb %E of wrong type"
	fmt.Printf("%f", "hi")                     // ERROR "arg .hi. for printf verb %f of wrong type"
	fmt.Printf("%F", 'x')                      // ERROR "arg 'x' for printf verb %F of wrong type"
	fmt.Printf("%g", "hi")                     // ERROR "arg .hi. for printf verb %g of wrong type"
	fmt.Printf("%g", imap)                     // ERROR "arg imap for printf verb %g of wrong type"
	fmt.Printf("%G", i)                        // ERROR "arg i for printf verb %G of wrong type"
	fmt.Printf("%o", x)                        // ERROR "arg x for printf verb %o of wrong type"
	fmt.Printf("%p", 23)                       // ERROR "arg 23 for printf verb %p of wrong type"
	fmt.Printf("%q", x)                        // ERROR "arg x for printf verb %q of wrong type"
	fmt.Printf("%s", b)                        // ERROR "arg b for printf verb %s of wrong type"
	fmt.Printf("%s", byte(65))                 // ERROR "arg byte\(65\) for printf verb %s of wrong type"
	fmt.Printf("%t", 23)                       // ERROR "arg 23 for printf verb %t of wrong type"
	fmt.Printf("%U", x)                        // ERROR "arg x for printf verb %U of wrong type"
	fmt.Printf("%x", nil)                      // ERROR "arg nil for printf verb %x of wrong type"
	fmt.Printf("%X", 2.3)                      // ERROR "arg 2.3 for printf verb %X of wrong type"
	fmt.Printf("%s", stringerv)                // ERROR "arg stringerv for printf verb %s of wrong type"
	fmt.Printf("%t", stringerv)                // ERROR "arg stringerv for printf verb %t of wrong type"
	fmt.Printf("%q", notstringerv)             // ERROR "arg notstringerv for printf verb %q of wrong type"
	fmt.Printf("%t", notstringerv)             // ERROR "arg notstringerv for printf verb %t of wrong type"
	fmt.Printf("%t", stringerarrayv)           // ERROR "arg stringerarrayv for printf verb %t of wrong type"
	fmt.Printf("%t", notstringerarrayv)        // ERROR "arg notstringerarrayv for printf verb %t of wrong type"
	fmt.Printf("%q", notstringerarrayv)        // ERROR "arg notstringerarrayv for printf verb %q of wrong type"
	fmt.Printf("%s", nonemptyinterface)        // NOTERROR "for printf verb %s of wrong type" (Disabled temporarily because of bug in IsAssignableTo)
	fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
	fmt.Println()                              // not an error
	fmt.Println("%s", "hi")                    // ERROR "possible formatting directive in Println call"
	fmt.Printf("%s", "hi", 3)                  // ERROR "wrong number of args for format in Printf call"
	fmt.Sprintf("%"+("s"), "hi", 3)            // ERROR "wrong number of args for format in Sprintf call"
	fmt.Printf("%s%%%d", "hi", 3)              // correct
	fmt.Printf("%08s", "woo")                  // correct
	fmt.Printf("% 8s", "woo")                  // correct
	fmt.Printf("%.*d", 3, 3)                   // correct
	fmt.Printf("%.*d", 3, 3, 3)                // ERROR "wrong number of args for format in Printf call"
	fmt.Printf("%.*d", "hi", 3)                // ERROR "arg .hi. for \* in printf format not of type int"
	fmt.Printf("%.*d", i, 3)                   // correct
	fmt.Printf("%.*d", s, 3)                   // ERROR "arg s for \* in printf format not of type int"
	fmt.Printf("%*%", 0.22)                    // ERROR "arg 0.22 for \* in printf format not of type int"
	fmt.Printf("%q %q", multi()...)            // ok
	fmt.Printf("%#q", `blah`)                  // ok
	printf("now is the time", "buddy")         // ERROR "no formatting directive"
	Printf("now is the time", "buddy")         // ERROR "no formatting directive"
	Printf("hi")                               // ok
	const format = "%s %s\n"
	Printf(format, "hi", "there")
	Printf(format, "hi") // ERROR "missing argument for Printf verb %s: need 2, have 1"
	f := new(stringer)
	f.Warn(0, "%s", "hello", 3)  // ERROR "possible formatting directive in Warn call"
	f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call"
	f.Warnf(0, "%r", "hello")    // ERROR "unrecognized printf verb"
	f.Warnf(0, "%#s", "hello")   // ERROR "unrecognized printf flag"
	Printf("d%", 2)              // ERROR "missing verb at end of format string in Printf call"
	Printf("%d", percentDV)
	Printf("%d", &percentDV)
	Printf("%d", notPercentDV)  // ERROR "arg notPercentDV for printf verb %d of wrong type"
	Printf("%d", &notPercentDV) // ERROR "arg &notPercentDV for printf verb %d of wrong type"
	Printf("%p", &notPercentDV) // Works regardless: we print it as a pointer.
	Printf("%s", percentSV)
	Printf("%s", &percentSV)
	// Good argument reorderings.
	Printf("%[1]d", 3)
	Printf("%[1]*d", 3, 1)
	Printf("%[2]*[1]d", 1, 3)
	Printf("%[2]*.[1]*[3]d", 2, 3, 4)
	fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly.
	// Bad argument reorderings.
	Printf("%[xd", 3)                    // ERROR "illegal syntax for printf argument index"
	Printf("%[x]d", 3)                   // ERROR "illegal syntax for printf argument index"
	Printf("%[3]*s", "hi", 2)            // ERROR "missing argument for Printf indirect \*: need 3, have 2"
	fmt.Sprintf("%[3]d", 2)              // ERROR "missing argument for Sprintf verb %d: need 3, have 1"
	Printf("%[2]*.[1]*[3]d", 2, "hi", 4) // ERROR "arg .hi. for \* in printf format not of type int"
	// Something that satisfies the error interface.
	var e error
	fmt.Println(e.Error()) // ok
	// Something that looks like an error interface but isn't, such as the (*T).Error method
	// in the testing package.
	var et1 errorTest1
	fmt.Println(et1.Error())        // ERROR "no args in Error call"
	fmt.Println(et1.Error("hi"))    // ok
	fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call"
	var et2 errorTest2
	et2.Error()        // ERROR "no args in Error call"
	et2.Error("hi")    // ok, not an error method.
	et2.Error("%d", 3) // ERROR "possible formatting directive in Error call"
	var et3 errorTest3
	et3.Error() // ok, not an error method.
	var et4 errorTest4
	et4.Error() // ok, not an error method.
	var et5 errorTest5
	et5.error() // ok, not an error method.
}

// Printf is used by the test so we must declare it.
func Printf(format string, args ...interface{}) {
	panic("don't call - testing only")
}

// printf is used by the test so we must declare it.
func printf(format string, args ...interface{}) {
	panic("don't call - testing only")
}

// multi is used by the test.
func multi() []interface{} {
	panic("don't call - testing only")
}

type stringer float64

var stringerv stringer

func (*stringer) String() string {
	return "string"
}

func (*stringer) Warn(int, ...interface{}) string {
	return "warn"
}

func (*stringer) Warnf(int, string, ...interface{}) string {
	return "warnf"
}

type notstringer struct {
	f float64
}

var notstringerv notstringer

type stringerarray [4]float64

func (stringerarray) String() string {
	return "string"
}

var stringerarrayv stringerarray

type notstringerarray [4]float64

var notstringerarrayv notstringerarray

var nonemptyinterface = interface {
	f()
}(nil)

// A data type we can print with "%d".
type percentDStruct struct {
	a int
	b []byte
	c *float64
}

var percentDV percentDStruct

// A data type we cannot print correctly with "%d".
type notPercentDStruct struct {
	a int
	b []byte
	c bool
}

var notPercentDV notPercentDStruct

// A data type we can print with "%s".
type percentSStruct struct {
	a string
	b []byte
	c stringerarray
}

var percentSV percentSStruct
<commit_msg>go.tools/cmd/vet: enable test (fix build)<commit_after>// Copyright 2010 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.

// This file contains tests for the printf checker.

package testdata

import (
	"fmt"
	"os"
	"unsafe" // just for test case printing unsafe.Pointer
)

func UnsafePointerPrintfTest() {
	var up unsafe.Pointer
	fmt.Printf("%p, %x %X", up, up, up)
}

// Error methods that do not satisfy the Error interface and should be checked.
type errorTest1 int

func (errorTest1) Error(...interface{}) string {
	return "hi"
}

type errorTest2 int // Analogous to testing's *T type.
func (errorTest2) Error(...interface{}) {
}

type errorTest3 int

func (errorTest3) Error() { // No return value.
}

type errorTest4 int

func (errorTest4) Error() int { // Different return type.
	return 3
}

type errorTest5 int

func (errorTest5) error() { // niladic; don't complain if no args (was bug)
}

// This function never executes, but it serves as a simple test for the program.
// Test with make test.
func PrintfTests() {
	var b bool
	var i int
	var r rune
	var s string
	var x float64
	var p *int
	var imap map[int]int
	var fslice []float64
	var c complex64
	// Some good format/argtypes
	fmt.Printf("")
	fmt.Printf("%b %b %b", 3, i, x)
	fmt.Printf("%c %c %c %c", 3, i, 'x', r)
	fmt.Printf("%d %d %d", 3, i, imap)
	fmt.Printf("%e %e %e %e", 3e9, x, fslice, c)
	fmt.Printf("%E %E %E %E", 3e9, x, fslice, c)
	fmt.Printf("%f %f %f %f", 3e9, x, fslice, c)
	fmt.Printf("%F %F %F %F", 3e9, x, fslice, c)
	fmt.Printf("%g %g %g %g", 3e9, x, fslice, c)
	fmt.Printf("%G %G %G %G", 3e9, x, fslice, c)
	fmt.Printf("%o %o", 3, i)
	fmt.Printf("%p %p", p, nil)
	fmt.Printf("%q %q %q %q", 3, i, 'x', r)
	fmt.Printf("%s %s %s", "hi", s, []byte{65})
	fmt.Printf("%t %t", true, b)
	fmt.Printf("%T %T", 3, i)
	fmt.Printf("%U %U", 3, i)
	fmt.Printf("%v %v", 3, i)
	fmt.Printf("%x %x %x %x", 3, i, "hi", s)
	fmt.Printf("%X %X %X %X", 3, i, "hi", s)
	fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
	fmt.Printf("%s", &stringerv)
	fmt.Printf("%v", &stringerv)
	fmt.Printf("%T", &stringerv)
	fmt.Printf("%v", notstringerv)
	fmt.Printf("%T", notstringerv)
	fmt.Printf("%q", stringerarrayv)
	fmt.Printf("%v", stringerarrayv)
	fmt.Printf("%s", stringerarrayv)
	fmt.Printf("%v", notstringerarrayv)
	fmt.Printf("%T", notstringerarrayv)
	fmt.Printf("%*%", 2)               // Ridiculous but allowed.
	fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say.

	fmt.Printf("%g", 1+2i)
	// Some bad format/argTypes
	fmt.Printf("%b", "hi")                     // ERROR "arg .hi. for printf verb %b of wrong type"
	fmt.Printf("%b", c)                        // ERROR "arg c for printf verb %b of wrong type"
	fmt.Printf("%b", 1+2i)                     // ERROR "arg 1 \+ 2i for printf verb %b of wrong type"
	fmt.Printf("%c", 2.3)                      // ERROR "arg 2.3 for printf verb %c of wrong type"
	fmt.Printf("%d", 2.3)                      // ERROR "arg 2.3 for printf verb %d of wrong type"
	fmt.Printf("%e", "hi")                     // ERROR "arg .hi. for printf verb %e of wrong type"
	fmt.Printf("%E", true)                     // ERROR "arg true for printf verb %E of wrong type"
	fmt.Printf("%f", "hi")                     // ERROR "arg .hi. for printf verb %f of wrong type"
	fmt.Printf("%F", 'x')                      // ERROR "arg 'x' for printf verb %F of wrong type"
	fmt.Printf("%g", "hi")                     // ERROR "arg .hi. for printf verb %g of wrong type"
	fmt.Printf("%g", imap)                     // ERROR "arg imap for printf verb %g of wrong type"
	fmt.Printf("%G", i)                        // ERROR "arg i for printf verb %G of wrong type"
	fmt.Printf("%o", x)                        // ERROR "arg x for printf verb %o of wrong type"
	fmt.Printf("%p", 23)                       // ERROR "arg 23 for printf verb %p of wrong type"
	fmt.Printf("%q", x)                        // ERROR "arg x for printf verb %q of wrong type"
	fmt.Printf("%s", b)                        // ERROR "arg b for printf verb %s of wrong type"
	fmt.Printf("%s", byte(65))                 // ERROR "arg byte\(65\) for printf verb %s of wrong type"
	fmt.Printf("%t", 23)                       // ERROR "arg 23 for printf verb %t of wrong type"
	fmt.Printf("%U", x)                        // ERROR "arg x for printf verb %U of wrong type"
	fmt.Printf("%x", nil)                      // ERROR "arg nil for printf verb %x of wrong type"
	fmt.Printf("%X", 2.3)                      // ERROR "arg 2.3 for printf verb %X of wrong type"
	fmt.Printf("%s", stringerv)                // ERROR "arg stringerv for printf verb %s of wrong type"
	fmt.Printf("%t", stringerv)                // ERROR "arg stringerv for printf verb %t of wrong type"
	fmt.Printf("%q", notstringerv)             // ERROR "arg notstringerv for printf verb %q of wrong type"
	fmt.Printf("%t", notstringerv)             // ERROR "arg notstringerv for printf verb %t of wrong type"
	fmt.Printf("%t", stringerarrayv)           // ERROR "arg stringerarrayv for printf verb %t of wrong type"
	fmt.Printf("%t", notstringerarrayv)        // ERROR "arg notstringerarrayv for printf verb %t of wrong type"
	fmt.Printf("%q", notstringerarrayv)        // ERROR "arg notstringerarrayv for printf verb %q of wrong type"
	fmt.Printf("%s", nonemptyinterface)        // ERROR "for printf verb %s of wrong type" (Disabled temporarily because of bug in IsAssignableTo)
	fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
	fmt.Println()                              // not an error
	fmt.Println("%s", "hi")                    // ERROR "possible formatting directive in Println call"
	fmt.Printf("%s", "hi", 3)                  // ERROR "wrong number of args for format in Printf call"
	fmt.Sprintf("%"+("s"), "hi", 3)            // ERROR "wrong number of args for format in Sprintf call"
	fmt.Printf("%s%%%d", "hi", 3)              // correct
	fmt.Printf("%08s", "woo")                  // correct
	fmt.Printf("% 8s", "woo")                  // correct
	fmt.Printf("%.*d", 3, 3)                   // correct
	fmt.Printf("%.*d", 3, 3, 3)                // ERROR "wrong number of args for format in Printf call"
	fmt.Printf("%.*d", "hi", 3)                // ERROR "arg .hi. for \* in printf format not of type int"
	fmt.Printf("%.*d", i, 3)                   // correct
	fmt.Printf("%.*d", s, 3)                   // ERROR "arg s for \* in printf format not of type int"
	fmt.Printf("%*%", 0.22)                    // ERROR "arg 0.22 for \* in printf format not of type int"
	fmt.Printf("%q %q", multi()...)            // ok
	fmt.Printf("%#q", `blah`)                  // ok
	printf("now is the time", "buddy")         // ERROR "no formatting directive"
	Printf("now is the time", "buddy")         // ERROR "no formatting directive"
	Printf("hi")                               // ok
	const format = "%s %s\n"
	Printf(format, "hi", "there")
	Printf(format, "hi") // ERROR "missing argument for Printf verb %s: need 2, have 1"
	f := new(stringer)
	f.Warn(0, "%s", "hello", 3)  // ERROR "possible formatting directive in Warn call"
	f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call"
	f.Warnf(0, "%r", "hello")    // ERROR "unrecognized printf verb"
	f.Warnf(0, "%#s", "hello")   // ERROR "unrecognized printf flag"
	Printf("d%", 2)              // ERROR "missing verb at end of format string in Printf call"
	Printf("%d", percentDV)
	Printf("%d", &percentDV)
	Printf("%d", notPercentDV)  // ERROR "arg notPercentDV for printf verb %d of wrong type"
	Printf("%d", &notPercentDV) // ERROR "arg &notPercentDV for printf verb %d of wrong type"
	Printf("%p", &notPercentDV) // Works regardless: we print it as a pointer.
	Printf("%s", percentSV)
	Printf("%s", &percentSV)
	// Good argument reorderings.
	Printf("%[1]d", 3)
	Printf("%[1]*d", 3, 1)
	Printf("%[2]*[1]d", 1, 3)
	Printf("%[2]*.[1]*[3]d", 2, 3, 4)
	fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly.
	// Bad argument reorderings.
	Printf("%[xd", 3)                    // ERROR "illegal syntax for printf argument index"
	Printf("%[x]d", 3)                   // ERROR "illegal syntax for printf argument index"
	Printf("%[3]*s", "hi", 2)            // ERROR "missing argument for Printf indirect \*: need 3, have 2"
	fmt.Sprintf("%[3]d", 2)              // ERROR "missing argument for Sprintf verb %d: need 3, have 1"
	Printf("%[2]*.[1]*[3]d", 2, "hi", 4) // ERROR "arg .hi. for \* in printf format not of type int"
	// Something that satisfies the error interface.
	var e error
	fmt.Println(e.Error()) // ok
	// Something that looks like an error interface but isn't, such as the (*T).Error method
	// in the testing package.
	var et1 errorTest1
	fmt.Println(et1.Error())        // ERROR "no args in Error call"
	fmt.Println(et1.Error("hi"))    // ok
	fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call"
	var et2 errorTest2
	et2.Error()        // ERROR "no args in Error call"
	et2.Error("hi")    // ok, not an error method.
	et2.Error("%d", 3) // ERROR "possible formatting directive in Error call"
	var et3 errorTest3
	et3.Error() // ok, not an error method.
	var et4 errorTest4
	et4.Error() // ok, not an error method.
	var et5 errorTest5
	et5.error() // ok, not an error method.
}

// Printf is used by the test so we must declare it.
func Printf(format string, args ...interface{}) {
	panic("don't call - testing only")
}

// printf is used by the test so we must declare it.
func printf(format string, args ...interface{}) {
	panic("don't call - testing only")
}

// multi is used by the test.
func multi() []interface{} {
	panic("don't call - testing only")
}

type stringer float64

var stringerv stringer

func (*stringer) String() string {
	return "string"
}

func (*stringer) Warn(int, ...interface{}) string {
	return "warn"
}

func (*stringer) Warnf(int, string, ...interface{}) string {
	return "warnf"
}

type notstringer struct {
	f float64
}

var notstringerv notstringer

type stringerarray [4]float64

func (stringerarray) String() string {
	return "string"
}

var stringerarrayv stringerarray

type notstringerarray [4]float64

var notstringerarrayv notstringerarray

var nonemptyinterface = interface {
	f()
}(nil)

// A data type we can print with "%d".
type percentDStruct struct {
	a int
	b []byte
	c *float64
}

var percentDV percentDStruct

// A data type we cannot print correctly with "%d".
type notPercentDStruct struct {
	a int
	b []byte
	c bool
}

var notPercentDV notPercentDStruct

// A data type we can print with "%s".
type percentSStruct struct {
	a string
	b []byte
	c stringerarray
}

var percentSV percentSStruct
<|endoftext|>


<commit_before>package insure

import (
	"github.com/dedis/crypto/abstract"
	"github.com/dedis/crypto/config"
	"github.com/dedis/crypto/poly"
	"github.com/dedis/crypto/random"
	)

/* This file provides an implementation of the Policy interface via
 * the struct LifePolicy. Check the other files in this package for more
 * on the Policy interface and the life insurance protocol in general.
 *
 * To create a policy:
 * newPolicy, ok := new(LifePolicy).TakeOutPolicy(MyKeyPair,
 *    ListOfPotentialServers, functionForSelectingServers,
 *    MinimumNumberOfSharesToReconstructSecret, NumberOfInsurers)
 *
 * For safety measures, the function returns nil if the policy fails to be
 * created along with an updated status in ok.
 */

type LifePolicy struct {
	// Private Key that is being insured.
        privateKey  abstract.Secret

        // A list of the public keys of the insurers of this policy
	insurersList []abstract.Point

	// Digitial Signatures that serve as "proof of insurance"
	// TODO: Determine what type of proof I want to use.
        proofList   int
}

/* This function selects a set of servers to serve as insurers.
 * This is an extremely rudimentary version that selects the first
 * n servers from the list.
 *
 * Arguments:
 *    serverList = the list of servers to choose from
 *    n          = the number of servers to choose
 *
 * Returns:
 *   The list of servers to server as insurers or nil if not enough servers
 *   Whether or not the function terminated successfully
 *      (i.e. whether there are at least n elements in the array)
 */

func selectInsurersBasic(serverList []abstract.Point, n int) ([]abstract.Point, bool) {
	if n < len(serverList) {
		return nil, false
	}

	return serverList[:n], true
}

// Returns the private key that is being insured.
func (lp *LifePolicy) GetPrivateKey() abstract.Secret {
	return lp.privateKey
}

// Returns the list of insurers for the policy.
func (lp *LifePolicy) GetInsurers() []abstract.Point {
	return lp.insurersList
}

// Returns the certificates of the insurers for each policy.
func (lp *LifePolicy) GetPolicyProof() int {
	return lp.proofList
}

/* This method is responsible for "taking out" the insurance policy. The
 * function takes the server's private key, divides it up into
 * shares using Shamir Secret Sharing, distribute these shares to trustees,
 * and then provides a "receipt list" of digital signatures proving that
 * the server has indeed selected insurers.
 *
 * Arguments:
 *   keyPair         = the public/private key of the server
 *   serverList      = a list of the public keys of possible insurers
 *   selectInsurers  = a function for selecting insurers
 *   t               = the minimum number of shares to reconstruct the secret
 *   n               = the total shares to be distributed
 *
 *
 * Note: If selectInsurers is null, the policy will resort to a default
 * selection function.
 */

func (lp *LifePolicy) TakeOutPolicy(keyPair config.KeyPair, serverList []abstract.Point,
	selectInsurers func([]abstract.Point, int) ([]abstract.Point, bool),
	t int, n int) (*LifePolicy, bool) {

	// Initialize the policy.
	ok := true
	lp.privateKey = keyPair.Secret

	// If we have no selectInsurers function, use the basic algorithm.
	if selectInsurers == nil {
		lp.insurersList, ok = selectInsurersBasic(serverList, n)
		if !ok {
			return nil, ok
		}
	} else {
		// Otherwise use the function provided.
		lp.insurersList, ok = selectInsurers(serverList, n)
		if !ok {
			return nil, ok
		}
	}
	//TODO: Use bytes maybe?
	//lp.proofList = make([][]byte, len(lp.insurersList))

	// Create a new polynomial from the private key where t
        // shares are needed to reconstruct the secret. Then, split it
	// into secret shares and create the public polynomial.
	pripoly := new(poly.PriPoly).Pick(keyPair.Suite, t,
		keyPair.Secret, random.Stream)
	prishares := new(poly.PriShares).Split(pripoly, n)
	pubPoly   := new(poly.PubPoly).Commit(pripoly, keyPair.Public)

	// TODO: Send the shares off to the insurers

	// TODO: Receive digital signatures from the others.

	return lp, ok
}

<commit_msg>gofmt<commit_after>package insure

import (
	"github.com/dedis/crypto/abstract"
	"github.com/dedis/crypto/config"
	"github.com/dedis/crypto/poly"
	"github.com/dedis/crypto/random"
)

/* This file provides an implementation of the Policy interface via
 * the struct LifePolicy. Check the other files in this package for more
 * on the Policy interface and the life insurance protocol in general.
 *
 * To create a policy:
 * newPolicy, ok := new(LifePolicy).TakeOutPolicy(MyKeyPair,
 *    ListOfPotentialServers, functionForSelectingServers,
 *    MinimumNumberOfSharesToReconstructSecret, NumberOfInsurers)
 *
 * For safety measures, the function returns nil if the policy fails to be
 * created along with an updated status in ok.
 */

type LifePolicy struct {
	// Private Key that is being insured.
	privateKey abstract.Secret

	// A list of the public keys of the insurers of this policy
	insurersList []abstract.Point

	// Digitial Signatures that serve as "proof of insurance"
	// TODO: Determine what type of proof I want to use.
	proofList int
}

/* This function selects a set of servers to serve as insurers.
 * This is an extremely rudimentary version that selects the first
 * n servers from the list.
 *
 * Arguments:
 *    serverList = the list of servers to choose from
 *    n          = the number of servers to choose
 *
 * Returns:
 *   The list of servers to server as insurers or nil if not enough servers
 *   Whether or not the function terminated successfully
 *      (i.e. whether there are at least n elements in the array)
 */

func selectInsurersBasic(serverList []abstract.Point, n int) ([]abstract.Point, bool) {
	if n < len(serverList) {
		return nil, false
	}

	return serverList[:n], true
}

// Returns the private key that is being insured.
func (lp *LifePolicy) GetPrivateKey() abstract.Secret {
	return lp.privateKey
}

// Returns the list of insurers for the policy.
func (lp *LifePolicy) GetInsurers() []abstract.Point {
	return lp.insurersList
}

// Returns the certificates of the insurers for each policy.
func (lp *LifePolicy) GetPolicyProof() int {
	return lp.proofList
}

/* This method is responsible for "taking out" the insurance policy. The
 * function takes the server's private key, divides it up into
 * shares using Shamir Secret Sharing, distribute these shares to trustees,
 * and then provides a "receipt list" of digital signatures proving that
 * the server has indeed selected insurers.
 *
 * Arguments:
 *   keyPair         = the public/private key of the server
 *   serverList      = a list of the public keys of possible insurers
 *   selectInsurers  = a function for selecting insurers
 *   t               = the minimum number of shares to reconstruct the secret
 *   n               = the total shares to be distributed
 *
 *
 * Note: If selectInsurers is null, the policy will resort to a default
 * selection function.
 */

func (lp *LifePolicy) TakeOutPolicy(keyPair config.KeyPair, serverList []abstract.Point,
	selectInsurers func([]abstract.Point, int) ([]abstract.Point, bool),
	t int, n int) (*LifePolicy, bool) {

	// Initialize the policy.
	ok := true
	lp.privateKey = keyPair.Secret

	// If we have no selectInsurers function, use the basic algorithm.
	if selectInsurers == nil {
		lp.insurersList, ok = selectInsurersBasic(serverList, n)
		if !ok {
			return nil, ok
		}
	} else {
		// Otherwise use the function provided.
		lp.insurersList, ok = selectInsurers(serverList, n)
		if !ok {
			return nil, ok
		}
	}
	//TODO: Use bytes maybe?
	//lp.proofList = make([][]byte, len(lp.insurersList))

	// Create a new polynomial from the private key where t
	// shares are needed to reconstruct the secret. Then, split it
	// into secret shares and create the public polynomial.
	pripoly := new(poly.PriPoly).Pick(keyPair.Suite, t,
		keyPair.Secret, random.Stream)
	prishares := new(poly.PriShares).Split(pripoly, n)
	pubPoly := new(poly.PubPoly).Commit(pripoly, keyPair.Public)

	// TODO: Send the shares off to the insurers

	// TODO: Receive digital signatures from the others.

	return lp, ok
}
<|endoftext|>


<commit_before>// MIT License
//
// Copyright (c) 2018 Stefan Wichmann
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/stefanwichmann/go.hue"
)

// HueBridge represents the Philips Hue bridge in
// your system.
// It is used to communicate with all devices.
type HueBridge struct {
	bridge   hue.Bridge
	BridgeIP string
	Username string
	Version  int
}

const hueBridgeAppName = "kelvin"

// InitializeBridge creates and returns an initialized HueBridge.
// If you have a valid configuration this will be used. Otherwise a local
// discovery will be started, followed by a user registration on your bridge.
func (bridge *HueBridge) InitializeBridge(configuration *Configuration) error {
	err := bridge.discover(configuration.Bridge.IP)
	if err != nil {
		return err
	}
	configuration.Bridge.IP = bridge.BridgeIP

	if configuration.Bridge.Username != "" {
		log.Debugf("⌘ Found bridge username in configuration: %s", configuration.Bridge.Username)
		bridge.Username = configuration.Bridge.Username
	} else {
		log.Debugf("⌘ No username found in bridge configuration. Starting registration...")
		err := bridge.register()
		if err != nil {
			return err
		}
		log.Debugf("⌘ Saving new username in bridge configuration: %s", bridge.Username)
		configuration.Bridge.Username = bridge.Username
	}

	log.Debugf("⌘ Connecting to bridge %s with username %s", bridge.BridgeIP, bridge.Username)
	err = bridge.connect()
	if err != nil {
		return err
	}
	log.Println("⌘ Connection to bridge established")
	go bridge.validateSofwareVersion()
	err = bridge.printDevices()
	if err != nil {
		return err
	}

	err = bridge.populateSchedule(configuration)
	return err
}

// Lights return all known lights on your bridge.
func (bridge *HueBridge) Lights() ([]*Light, error) {
	var lights []*Light
	hueLights, err := bridge.bridge.GetAllLights()
	if err != nil {
		return lights, err
	}

	for _, hueLight := range hueLights {
		var light Light
		light.ID, err = strconv.Atoi(hueLight.Id)
		if err != nil {
			return lights, err
		}

		light.HueLight.HueLight = *hueLight
		light.initialize()

		lights = append(lights, &light)
	}

	sort.Slice(lights, func(i, j int) bool { return lights[i].ID < lights[j].ID })
	return lights, nil
}

func (bridge *HueBridge) printDevices() error {
	lights, err := bridge.Lights()
	if err != nil {
		return err
	}

	log.Printf("⌘ Devices found on current bridge:")
	log.Printf("| %-32s | %3v | %-5v | %-8v | %-11v | %-5v |", "Name", "ID", "On", "Dimmable", "Temperature", "Color")
	for _, light := range lights {
		log.Printf("| %-32s | %3v | %-5v | %-8v | %-11v | %-5v |", light.Name, light.ID, light.On, light.HueLight.Dimmable, light.HueLight.SupportsColorTemperature, light.HueLight.SupportsXYColor)
	}
	return nil
}

func (bridge *HueBridge) discover(ip string) error {
	if ip != "" {
		// we have a known IP address. Validate if it points to a reachable bridge
		bridge.BridgeIP = ip
		err := bridge.validateBridge()
		if err == nil {
			return nil
		}
	}
	log.Debugf("⌘ Starting bridge discovery")
	bridges, err := hue.DiscoverBridges(false)
	if err != nil {
		bridge.BridgeIP = ""
		return err
	}
	if len(bridges) == 0 {
		bridge.BridgeIP = ""
		return errors.New("Bridge discovery failed. Please configure manually in config.json")
	}
	for _, candidate := range bridges {
		bridge.BridgeIP = candidate.IpAddr
		err := bridge.validateBridge()
		if err == nil {
			log.Printf("⌘ Found bridge at %s", bridge.BridgeIP)
			return nil
		}
	}
	bridge.BridgeIP = ""
	return errors.New("Bridge discovery failed. Please configure manually in config.json")
}

func (bridge *HueBridge) register() error {
	if bridge.BridgeIP == "" {
		return errors.New("Registration at bridge not possible because no IP is configured. Start discovery first or enter manually")
	}

	bridge.bridge = *hue.NewBridge(bridge.BridgeIP, "")
	log.Printf("⌘ Starting user registration.")
	log.Warningf("⌘ PLEASE PUSH THE BLUE BUTTON ON YOUR HUE BRIDGE")
	for {
		time.Sleep(5 * time.Second)

		// try user creation, will fail if the button wasn't pressed.
		err := bridge.bridge.CreateUser(hueBridgeAppName)
		if err != nil {
			return err
		}

		if bridge.bridge.Username != "" {
			// registration successful
			bridge.Username = bridge.bridge.Username
			log.Printf("⌘ User registration successful.")
			return nil
		}
	}
}

func (bridge *HueBridge) connect() error {
	if bridge.BridgeIP == "" {
		return errors.New("No bridge IP configured")
	}

	if bridge.Username == "" {
		return errors.New("No username on bridge configured")
	}
	bridge.bridge = *hue.NewBridge(bridge.BridgeIP, bridge.Username)

	// Test bridge
	configuration, err := bridge.bridge.Configuration()
	if err != nil {
		return err
	}

	// Enable HTTPS if supported
	// TODO HTTPS supported on Model BSB001?
	swversion, err := strconv.Atoi(configuration.SoftwareVersion)
	if err != nil {
		return err
	}
	if configuration.ModelId == "BSB002" && swversion >= 1802201122 {
		bridge.bridge.EnableHTTPS(true)
	}

	return nil
}

func (bridge *HueBridge) populateSchedule(configuration *Configuration) error {
	if len(configuration.Schedules) == 0 {
		return errors.New("Configuration does not contain any schedules to populate")
	}

	// Do we have associated lights?
	for _, schedule := range configuration.Schedules {
		if len(schedule.AssociatedDeviceIDs) > 0 {
			log.Debugf("⌘ Configuration contains at least one schedule with associated lights.")
			return nil // At least one schedule is configured
		}
	}

	// No schedule has associated lights
	log.Debugf("⌘ Configuration contains no schedule with associated lights. Initializing first schedule with all lights.")
	lights, err := bridge.Lights()
	if err != nil {
		return err
	}
	var lightIDs []int
	for _, light := range lights {
		lightIDs = append(lightIDs, light.ID)
	}
	configuration.Schedules[0].AssociatedDeviceIDs = lightIDs
	return nil
}

func (bridge *HueBridge) validateSofwareVersion() {
	configuration, err := bridge.bridge.Configuration()
	if err != nil {
		log.Warningf("⌘ Could not validate bridge software version: %v", err)
		return
	}

	swversion, err := strconv.Atoi(configuration.SoftwareVersion)
	if err != nil {
		log.Warningf("⌘ Could not validate bridge software version: %v", err)
		return
	}
	log.Debugf("⌘ Bridge is running software version %s", configuration.SoftwareVersion)

	if (bridge.Version == 1 && swversion < 1041302) || (bridge.Version == 2 && swversion < 1806051111) {
		log.Warningf("⌘ Your hue bridge is running an old software version. Please update using the hue app to ensure Kelvin will run smoothly.")
	} else {
		log.Debugf("⌘ Bridge software is up to date")
	}
}

func (bridge *HueBridge) validateBridge() error {
	if bridge.BridgeIP == "" {
		return errors.New("No bridge configured. Could not validate")
	}
	resp, err := http.Get("http://" + bridge.BridgeIP + "/description.xml")
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return fmt.Errorf("Could not read bridge description: %v", err)
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("Could not read bridge description: %v", err)
	}
	if strings.Contains(string(data), "<modelNumber>929000226503</modelNumber>") {
		bridge.Version = 1
		return nil
	}
	if strings.Contains(string(data), "<modelNumber>BSB002</modelNumber>") {
		bridge.Version = 2
		return nil
	}
	return fmt.Errorf("Bridge validation failed")
}
<commit_msg>Update last known bridge version<commit_after>// MIT License
//
// Copyright (c) 2018 Stefan Wichmann
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/stefanwichmann/go.hue"
)

// HueBridge represents the Philips Hue bridge in
// your system.
// It is used to communicate with all devices.
type HueBridge struct {
	bridge   hue.Bridge
	BridgeIP string
	Username string
	Version  int
}

const hueBridgeAppName = "kelvin"

// InitializeBridge creates and returns an initialized HueBridge.
// If you have a valid configuration this will be used. Otherwise a local
// discovery will be started, followed by a user registration on your bridge.
func (bridge *HueBridge) InitializeBridge(configuration *Configuration) error {
	err := bridge.discover(configuration.Bridge.IP)
	if err != nil {
		return err
	}
	configuration.Bridge.IP = bridge.BridgeIP

	if configuration.Bridge.Username != "" {
		log.Debugf("⌘ Found bridge username in configuration: %s", configuration.Bridge.Username)
		bridge.Username = configuration.Bridge.Username
	} else {
		log.Debugf("⌘ No username found in bridge configuration. Starting registration...")
		err := bridge.register()
		if err != nil {
			return err
		}
		log.Debugf("⌘ Saving new username in bridge configuration: %s", bridge.Username)
		configuration.Bridge.Username = bridge.Username
	}

	log.Debugf("⌘ Connecting to bridge %s with username %s", bridge.BridgeIP, bridge.Username)
	err = bridge.connect()
	if err != nil {
		return err
	}
	log.Println("⌘ Connection to bridge established")
	go bridge.validateSofwareVersion()
	err = bridge.printDevices()
	if err != nil {
		return err
	}

	err = bridge.populateSchedule(configuration)
	return err
}

// Lights return all known lights on your bridge.
func (bridge *HueBridge) Lights() ([]*Light, error) {
	var lights []*Light
	hueLights, err := bridge.bridge.GetAllLights()
	if err != nil {
		return lights, err
	}

	for _, hueLight := range hueLights {
		var light Light
		light.ID, err = strconv.Atoi(hueLight.Id)
		if err != nil {
			return lights, err
		}

		light.HueLight.HueLight = *hueLight
		light.initialize()

		lights = append(lights, &light)
	}

	sort.Slice(lights, func(i, j int) bool { return lights[i].ID < lights[j].ID })
	return lights, nil
}

func (bridge *HueBridge) printDevices() error {
	lights, err := bridge.Lights()
	if err != nil {
		return err
	}

	log.Printf("⌘ Devices found on current bridge:")
	log.Printf("| %-32s | %3v | %-5v | %-8v | %-11v | %-5v |", "Name", "ID", "On", "Dimmable", "Temperature", "Color")
	for _, light := range lights {
		log.Printf("| %-32s | %3v | %-5v | %-8v | %-11v | %-5v |", light.Name, light.ID, light.On, light.HueLight.Dimmable, light.HueLight.SupportsColorTemperature, light.HueLight.SupportsXYColor)
	}
	return nil
}

func (bridge *HueBridge) discover(ip string) error {
	if ip != "" {
		// we have a known IP address. Validate if it points to a reachable bridge
		bridge.BridgeIP = ip
		err := bridge.validateBridge()
		if err == nil {
			return nil
		}
	}
	log.Debugf("⌘ Starting bridge discovery")
	bridges, err := hue.DiscoverBridges(false)
	if err != nil {
		bridge.BridgeIP = ""
		return err
	}
	if len(bridges) == 0 {
		bridge.BridgeIP = ""
		return errors.New("Bridge discovery failed. Please configure manually in config.json")
	}
	for _, candidate := range bridges {
		bridge.BridgeIP = candidate.IpAddr
		err := bridge.validateBridge()
		if err == nil {
			log.Printf("⌘ Found bridge at %s", bridge.BridgeIP)
			return nil
		}
	}
	bridge.BridgeIP = ""
	return errors.New("Bridge discovery failed. Please configure manually in config.json")
}

func (bridge *HueBridge) register() error {
	if bridge.BridgeIP == "" {
		return errors.New("Registration at bridge not possible because no IP is configured. Start discovery first or enter manually")
	}

	bridge.bridge = *hue.NewBridge(bridge.BridgeIP, "")
	log.Printf("⌘ Starting user registration.")
	log.Warningf("⌘ PLEASE PUSH THE BLUE BUTTON ON YOUR HUE BRIDGE")
	for {
		time.Sleep(5 * time.Second)

		// try user creation, will fail if the button wasn't pressed.
		err := bridge.bridge.CreateUser(hueBridgeAppName)
		if err != nil {
			return err
		}

		if bridge.bridge.Username != "" {
			// registration successful
			bridge.Username = bridge.bridge.Username
			log.Printf("⌘ User registration successful.")
			return nil
		}
	}
}

func (bridge *HueBridge) connect() error {
	if bridge.BridgeIP == "" {
		return errors.New("No bridge IP configured")
	}

	if bridge.Username == "" {
		return errors.New("No username on bridge configured")
	}
	bridge.bridge = *hue.NewBridge(bridge.BridgeIP, bridge.Username)

	// Test bridge
	configuration, err := bridge.bridge.Configuration()
	if err != nil {
		return err
	}

	// Enable HTTPS if supported
	// TODO HTTPS supported on Model BSB001?
	swversion, err := strconv.Atoi(configuration.SoftwareVersion)
	if err != nil {
		return err
	}
	if configuration.ModelId == "BSB002" && swversion >= 1802201122 {
		bridge.bridge.EnableHTTPS(true)
	}

	return nil
}

func (bridge *HueBridge) populateSchedule(configuration *Configuration) error {
	if len(configuration.Schedules) == 0 {
		return errors.New("Configuration does not contain any schedules to populate")
	}

	// Do we have associated lights?
	for _, schedule := range configuration.Schedules {
		if len(schedule.AssociatedDeviceIDs) > 0 {
			log.Debugf("⌘ Configuration contains at least one schedule with associated lights.")
			return nil // At least one schedule is configured
		}
	}

	// No schedule has associated lights
	log.Debugf("⌘ Configuration contains no schedule with associated lights. Initializing first schedule with all lights.")
	lights, err := bridge.Lights()
	if err != nil {
		return err
	}
	var lightIDs []int
	for _, light := range lights {
		lightIDs = append(lightIDs, light.ID)
	}
	configuration.Schedules[0].AssociatedDeviceIDs = lightIDs
	return nil
}

func (bridge *HueBridge) validateSofwareVersion() {
	configuration, err := bridge.bridge.Configuration()
	if err != nil {
		log.Warningf("⌘ Could not validate bridge software version: %v", err)
		return
	}

	swversion, err := strconv.Atoi(configuration.SoftwareVersion)
	if err != nil {
		log.Warningf("⌘ Could not validate bridge software version: %v", err)
		return
	}
	log.Debugf("⌘ Bridge is running software version %s", configuration.SoftwareVersion)

	if (bridge.Version == 1 && swversion < 1041302) || (bridge.Version == 2 && swversion < 1809121051) {
		log.Warningf("⌘ Your hue bridge is running an old software version. Please update using the hue app to ensure Kelvin will run smoothly.")
	} else {
		log.Debugf("⌘ Bridge software is up to date")
	}
}

func (bridge *HueBridge) validateBridge() error {
	if bridge.BridgeIP == "" {
		return errors.New("No bridge configured. Could not validate")
	}
	resp, err := http.Get("http://" + bridge.BridgeIP + "/description.xml")
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return fmt.Errorf("Could not read bridge description: %v", err)
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("Could not read bridge description: %v", err)
	}
	if strings.Contains(string(data), "<modelNumber>929000226503</modelNumber>") {
		bridge.Version = 1
		return nil
	}
	if strings.Contains(string(data), "<modelNumber>BSB002</modelNumber>") {
		bridge.Version = 2
		return nil
	}
	return fmt.Errorf("Bridge validation failed")
}
<|endoftext|>


<commit_before>package MQTTg

import (
	"fmt"
	"net"
	"strconv"
	"time"
)

type Broker struct {
	MyAddr *net.TCPAddr
	// TODO: check whether not good to use addr as key
	Clients   map[string]*BrokerSideClient //map[clientID]*BrokerSideClient
	TopicRoot *TopicNode
}

func (self *Broker) Start() error {
	addr, err := GetLocalAddr()
	fmt.Println(addr)
	if err != nil {
		return err
	}
	self.MyAddr = addr
	listener, err := net.ListenTCP("tcp4", addr)
	if err != nil {
		// TODO: use channel to return error
		return err
	}
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			// TODO: use channel to return error
			EmitError(err)
			continue
		}
		bc := NewBrokerSideClient(&Transport{conn}, self)
		go bc.ReadLoop(bc) // TODO: use single Loop function
		go bc.WriteLoop()
	}
}

func (self *BrokerSideClient) disconnectProcessing() (err error) {
	w := self.Will
	broker := self.Broker
	if w != nil {
		if w.Retain {
			broker.TopicRoot.ApplyRetain(w.Topic, w.QoS, w.Message)
		}
		nodes, _ := broker.TopicRoot.GetTopicNodes(w.Topic, true)
		for subscriberID, reqQoS := range nodes[0].Subscribers {
			subscriber, _ := broker.Clients[subscriberID]
			var id uint16 = 0
			var err error
			if w.QoS > 0 {
				id, err = subscriber.getUsablePacketID()
				if err != nil {
					panic(err)
				}
			}
			qos := w.QoS
			if reqQoS < w.QoS {
				//downgrade the QoS
				qos = reqQoS
			}

			pub := NewPublishMessage(false, qos, w.Retain, w.Topic, id, []uint8(w.Message))
			subscriber.WriteChan <- pub
		}
	}
	if self.IsConnecting {
		self.KeepAliveTimer.Stop()
		if self.CleanSession {
			delete(broker.Clients, self.ID)
		}
	}
	err = self.disconnectBase()
	return err
}

func (self *Broker) ApplyDummyClientID() string {
	return "DummyClientID:" + strconv.Itoa(len(self.Clients)+1)
}

type BrokerSideClient struct {
	*ClientInfo
	SubTopics []*SubscribeTopic
	Broker    *Broker
}

func NewBrokerSideClient(ct *Transport, broker *Broker) *BrokerSideClient {
	return &BrokerSideClient{
		ClientInfo: &ClientInfo{
			Ct:             ct,
			IsConnecting:   false,
			ID:             "",
			User:           nil,
			KeepAlive:      0,
			Will:           nil,
			PacketIDMap:    make(map[uint16]Message, 0),
			CleanSession:   false,
			KeepAliveTimer: time.NewTimer(0),
			Duration:       0,
			WriteChan:      make(chan Message),
		},
		SubTopics: make([]*SubscribeTopic, 0),
		Broker:    broker,
	}
}

func (self *BrokerSideClient) RunClientTimer() {
	<-self.KeepAliveTimer.C
	EmitError(CLIENT_TIMED_OUT)
	self.disconnectProcessing()
	// TODO: logging?
}

func (self *BrokerSideClient) setPreviousSession(prevSession *BrokerSideClient) {
	self.SubTopics = prevSession.SubTopics

	self.PacketIDMap = prevSession.PacketIDMap
	self.CleanSession = prevSession.CleanSession
	self.Will = prevSession.Will
	self.Duration = prevSession.Duration
	self.KeepAliveTimer = time.NewTimer(self.Duration)
	self.KeepAlive = prevSession.KeepAlive
	// TODO: authorize here
	self.User = prevSession.User
}

func (self *BrokerSideClient) recvConnectMessage(m *ConnectMessage) (err error) {
	// NOTICE: when connection error is sent to client, self.Ct.SendMessage()
	//         should be used for avoiding Isconnecting validation
	if m.Protocol.Name != MQTT_3_1_1.Name {
		// server MAY disconnect
		self.disconnectProcessing()
		return INVALID_PROTOCOL_NAME
	}

	if m.Protocol.Level != MQTT_3_1_1.Level {
		// CHECK: Is false correct?
		err = self.Ct.SendMessage(NewConnackMessage(false, UnacceptableProtocolVersion))
		self.disconnectProcessing()
		return INVALID_PROTOCOL_LEVEL
	}

	c, ok := self.Broker.Clients[m.ClientID]
	if ok && c.IsConnecting {
		// TODO: this might cause problem
		// TODO; which should be disconnected, connecting one? or trying to connect one?
		err = self.Ct.SendMessage(NewConnackMessage(false, IdentifierRejected))
		self.disconnectProcessing()
		return CLIENT_ID_IS_USED_ALREADY
	}
	cleanSession := m.Flags&CleanSession_Flag == CleanSession_Flag
	if ok && !cleanSession {
		self.setPreviousSession(c)
	} else if !cleanSession && len(m.ClientID) == 0 {
		err = self.Ct.SendMessage(NewConnackMessage(false, IdentifierRejected))
		self.disconnectProcessing()
		return CLEANSESSION_MUST_BE_TRUE
	}

	sessionPresent := ok
	if cleanSession || !ok {
		// TODO: need to manage QoS base processing
		self.Duration = time.Duration(float32(m.KeepAlive)*1.5) * time.Second
		if len(m.ClientID) == 0 {
			m.ClientID = self.Broker.ApplyDummyClientID()
		}
		self.ID = m.ClientID
		self.User = m.User
		self.KeepAlive = m.KeepAlive
		self.Will = m.Will
		self.CleanSession = cleanSession
		self.KeepAliveTimer = time.NewTimer(self.Duration)
		sessionPresent = false
	}
	self.Broker.Clients[m.ClientID] = self

	if m.Flags&Will_Flag == Will_Flag {
		self.Will = m.Will
		// TODO: consider QoS and Retain as broker need
	} else {

	}

	if m.KeepAlive != 0 {
		go self.RunClientTimer()
	}
	self.IsConnecting = true
	connack := NewConnackMessage(sessionPresent, Accepted)
	self.WriteChan <- connack
	self.Redelivery()
	return err
}

func (self *BrokerSideClient) recvConnackMessage(m *ConnackMessage) (err error) {
	return INVALID_MESSAGE_CAME
}

func (self *BrokerSideClient) recvPublishMessage(m *PublishMessage) (err error) {
	if m.Dup {
		// re-delivered
	} else {
		// first time delivery
	}

	if m.Retain {
		// store tehe application message to designated topic
		data := string(m.Payload)
		if m.QoS == 0 && len(data) > 0 {
			// TODO: warnning, in this case data cannot be stored.
			// discard retained message
			data = ""
		}
		self.Broker.TopicRoot.ApplyRetain(m.TopicName, m.QoS, data)
	}

	nodes, err := self.Broker.TopicRoot.GetTopicNodes(m.TopicName, true)
	if err != nil {
		return err
	}
	for subscriberID, reqQoS := range nodes[0].Subscribers {
		subscriber, _ := self.Broker.Clients[subscriberID]
		var id uint16 = 0
		var err error
		if m.QoS > 0 {
			id, err = subscriber.getUsablePacketID()
			if err != nil {
				panic(err)
			}
		}
		qos := m.QoS
		if reqQoS < m.QoS {
			// downgrade the QoS
			qos = reqQoS
		}

		pub := NewPublishMessage(false, qos, false, m.TopicName, id, m.Payload)
		subscriber.WriteChan <- pub
	}

	switch m.QoS {
	// in any case, Dub must be 0
	case 0:
		if m.PacketID != 0 {
			return PACKET_ID_SHOULD_BE_ZERO
		}
	case 1:
		puback := NewPubackMessage(m.PacketID)
		self.WriteChan <- puback
	case 2:
		pubrec := NewPubrecMessage(m.PacketID)
		self.WriteChan <- pubrec
	}
	return err
}

func (self *BrokerSideClient) recvPubackMessage(m *PubackMessage) (err error) {
	// acknowledge the sent Publish packet
	if m.PacketID > 0 {
		err = self.AckMessage(m.PacketID)
	}
	return err
}

func (self *BrokerSideClient) recvPubrecMessage(m *PubrecMessage) (err error) {
	// acknowledge the sent Publish packet
	err = self.AckMessage(m.PacketID)
	if err != nil {
		return err
	}
	pubrel := NewPubrelMessage(m.PacketID)
	self.WriteChan <- pubrel
	return err
}

func (self *BrokerSideClient) recvPubrelMessage(m *PubrelMessage) (err error) {
	// acknowledge the sent Pubrel packet
	err = self.AckMessage(m.PacketID)
	if err != nil {
		return err
	}
	pubcomp := NewPubcompMessage(m.PacketID)
	self.WriteChan <- pubcomp
	return err
}

func (self *BrokerSideClient) recvPubcompMessage(m *PubcompMessage) (err error) {
	// acknowledge the sent Pubrel packet
	err = self.AckMessage(m.PacketID)
	return err
}

func (self *BrokerSideClient) recvSubscribeMessage(m *SubscribeMessage) (err error) {
	// TODO: check The wild card is permitted
	returnCodes := make([]SubscribeReturnCode, 0)
	for _, subTopic := range m.SubscribeTopics {
		// TODO: need to validate wheter there are same topics or not
		edges, err := self.Broker.TopicRoot.GetTopicNodes(subTopic.Topic, true)
		codes := make([]SubscribeReturnCode, len(edges))
		if err != nil {
			for i, _ := range codes {
				codes[i] = SubscribeFailure
			}
		} else {
			for i, edge := range edges {
				edge.Subscribers[self.ID] = subTopic.QoS
				codes[i] = SubscribeReturnCode(subTopic.QoS)
				self.SubTopics = append(self.SubTopics,
					&SubscribeTopic{SubscribeAck,
						edge.FullPath,
						uint8(subTopic.QoS),
					})
				if len(edge.RetainMessage) > 0 {
					// publish retain
					// TODO: check all arguments
					var id uint16
					if edge.RetainQoS > 0 {
						id, err = self.getUsablePacketID()
						if err != nil {
							return err
						}
					}
					qos := edge.RetainQoS
					if subTopic.QoS < edge.RetainQoS {
						qos = subTopic.QoS //downgrade the QoS
					}
					pub := NewPublishMessage(false, qos, true, edge.FullPath, id, []uint8(edge.RetainMessage))
					self.WriteChan <- pub
					EmitError(err)
				}
			}
		}
		returnCodes = append(returnCodes, codes...)
	}
	// TODO: check whether the number of return codes are correct?
	suback := NewSubackMessage(m.PacketID, returnCodes)
	self.WriteChan <- suback
	return err
}

func (self *BrokerSideClient) recvSubackMessage(m *SubackMessage) (err error) {
	return INVALID_MESSAGE_CAME
}
func (self *BrokerSideClient) recvUnsubscribeMessage(m *UnsubscribeMessage) (err error) {
	if len(m.TopicNames) == 0 {
		// protocol violation
	}

	for _, name := range m.TopicNames {
		self.Broker.TopicRoot.DeleteSubscriber(self.ID, name)
	}
	// TODO: optimize here
	result := []*SubscribeTopic{}
	for _, t := range self.SubTopics {
		del := false
		for _, name := range m.TopicNames {
			if string(t.Topic) == string(name) {
				del = true
			}
		}
		if !del {
			result = append(result, t)
		}
	}
	self.SubTopics = result
	unsuback := NewUnsubackMessage(m.PacketID)

	self.WriteChan <- unsuback
	return err
}
func (self *BrokerSideClient) recvUnsubackMessage(m *UnsubackMessage) (err error) {
	return INVALID_MESSAGE_CAME
}

func (self *BrokerSideClient) recvPingreqMessage(m *PingreqMessage) (err error) {
	// Pingresp
	// TODO: calc elapsed time from previous pingreq.
	//       and store the time to duration of Transport
	pingresp := NewPingrespMessage()
	self.WriteChan <- pingresp
	if self.KeepAlive != 0 {
		self.ResetTimer()
		go self.RunClientTimer()
	}
	return err
}

func (self *BrokerSideClient) recvPingrespMessage(m *PingrespMessage) (err error) {
	return INVALID_MESSAGE_CAME
}

func (self *BrokerSideClient) recvDisconnectMessage(m *DisconnectMessage) (err error) {
	self.Will = nil
	self.disconnectProcessing()
	// close the client
	return err
}
<commit_msg>gather same processing to one function<commit_after>package MQTTg

import (
	"fmt"
	"net"
	"strconv"
	"time"
)

type Broker struct {
	MyAddr *net.TCPAddr
	// TODO: check whether not good to use addr as key
	Clients   map[string]*BrokerSideClient //map[clientID]*BrokerSideClient
	TopicRoot *TopicNode
}

func (self *Broker) Start() error {
	addr, err := GetLocalAddr()
	fmt.Println(addr)
	if err != nil {
		return err
	}
	self.MyAddr = addr
	listener, err := net.ListenTCP("tcp4", addr)
	if err != nil {
		// TODO: use channel to return error
		return err
	}
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			// TODO: use channel to return error
			EmitError(err)
			continue
		}
		bc := NewBrokerSideClient(&Transport{conn}, self)
		go bc.ReadLoop(bc) // TODO: use single Loop function
		go bc.WriteLoop()
	}
}

func (self *BrokerSideClient) disconnectProcessing() (err error) {
	w := self.Will
	broker := self.Broker
	if w != nil {
		if w.Retain {
			broker.TopicRoot.ApplyRetain(w.Topic, w.QoS, w.Message)
		}
		nodes, _ := broker.TopicRoot.GetTopicNodes(w.Topic, true)
		for subscriberID, reqQoS := range nodes[0].Subscribers {
			subscriber, _ := broker.Clients[subscriberID]
			self.Broker.checkQoSAndPublish(subscriber, w.QoS, reqQoS, w.Retain, w.Topic, []uint8(w.Message))
		}
	}
	if self.IsConnecting {
		self.KeepAliveTimer.Stop()
		if self.CleanSession {
			delete(broker.Clients, self.ID)
		}
	}
	err = self.disconnectBase()
	return err
}

func (self *Broker) checkQoSAndPublish(requestClient *BrokerSideClient, publisherQoS, requestedQoS uint8, retain bool, topic string, message []uint8) {
	var id uint16 = 0
	var err error
	qos := publisherQoS
	if requestedQoS < publisherQoS {
		// QoS downgrade
		qos = requestedQoS
	}
	if qos > 0 {
		id, err = requestClient.getUsablePacketID()
		if err != nil {
			panic(err)
		}
	}
	pub := NewPublishMessage(false, qos, retain, topic, id, message)
	requestClient.WriteChan <- pub
}

func (self *Broker) ApplyDummyClientID() string {
	return "DummyClientID:" + strconv.Itoa(len(self.Clients)+1)
}

type BrokerSideClient struct {
	*ClientInfo
	SubTopics []*SubscribeTopic
	Broker    *Broker
}

func NewBrokerSideClient(ct *Transport, broker *Broker) *BrokerSideClient {
	return &BrokerSideClient{
		ClientInfo: &ClientInfo{
			Ct:             ct,
			IsConnecting:   false,
			ID:             "",
			User:           nil,
			KeepAlive:      0,
			Will:           nil,
			PacketIDMap:    make(map[uint16]Message, 0),
			CleanSession:   false,
			KeepAliveTimer: time.NewTimer(0),
			Duration:       0,
			WriteChan:      make(chan Message),
		},
		SubTopics: make([]*SubscribeTopic, 0),
		Broker:    broker,
	}
}

func (self *BrokerSideClient) RunClientTimer() {
	<-self.KeepAliveTimer.C
	EmitError(CLIENT_TIMED_OUT)
	self.disconnectProcessing()
	// TODO: logging?
}

func (self *BrokerSideClient) setPreviousSession(prevSession *BrokerSideClient) {
	self.SubTopics = prevSession.SubTopics

	self.PacketIDMap = prevSession.PacketIDMap
	self.CleanSession = prevSession.CleanSession
	self.Will = prevSession.Will
	self.Duration = prevSession.Duration
	self.KeepAliveTimer = time.NewTimer(self.Duration)
	self.KeepAlive = prevSession.KeepAlive
	// TODO: authorize here
	self.User = prevSession.User
}

func (self *BrokerSideClient) recvConnectMessage(m *ConnectMessage) (err error) {
	// NOTICE: when connection error is sent to client, self.Ct.SendMessage()
	//         should be used for avoiding Isconnecting validation
	if m.Protocol.Name != MQTT_3_1_1.Name {
		// server MAY disconnect
		self.disconnectProcessing()
		return INVALID_PROTOCOL_NAME
	}

	if m.Protocol.Level != MQTT_3_1_1.Level {
		// CHECK: Is false correct?
		err = self.Ct.SendMessage(NewConnackMessage(false, UnacceptableProtocolVersion))
		self.disconnectProcessing()
		return INVALID_PROTOCOL_LEVEL
	}

	c, ok := self.Broker.Clients[m.ClientID]
	if ok && c.IsConnecting {
		// TODO: this might cause problem
		// TODO; which should be disconnected, connecting one? or trying to connect one?
		err = self.Ct.SendMessage(NewConnackMessage(false, IdentifierRejected))
		self.disconnectProcessing()
		return CLIENT_ID_IS_USED_ALREADY
	}
	cleanSession := m.Flags&CleanSession_Flag == CleanSession_Flag
	if ok && !cleanSession {
		self.setPreviousSession(c)
	} else if !cleanSession && len(m.ClientID) == 0 {
		err = self.Ct.SendMessage(NewConnackMessage(false, IdentifierRejected))
		self.disconnectProcessing()
		return CLEANSESSION_MUST_BE_TRUE
	}

	sessionPresent := ok
	if cleanSession || !ok {
		// TODO: need to manage QoS base processing
		self.Duration = time.Duration(float32(m.KeepAlive)*1.5) * time.Second
		if len(m.ClientID) == 0 {
			m.ClientID = self.Broker.ApplyDummyClientID()
		}
		self.ID = m.ClientID
		self.User = m.User
		self.KeepAlive = m.KeepAlive
		self.Will = m.Will
		self.CleanSession = cleanSession
		self.KeepAliveTimer = time.NewTimer(self.Duration)
		sessionPresent = false
	}
	self.Broker.Clients[m.ClientID] = self

	if m.Flags&Will_Flag == Will_Flag {
		self.Will = m.Will
		// TODO: consider QoS and Retain as broker need
	} else {

	}

	if m.KeepAlive != 0 {
		go self.RunClientTimer()
	}
	self.IsConnecting = true
	connack := NewConnackMessage(sessionPresent, Accepted)
	self.WriteChan <- connack
	self.Redelivery()
	return err
}

func (self *BrokerSideClient) recvConnackMessage(m *ConnackMessage) (err error) {
	return INVALID_MESSAGE_CAME
}

func (self *BrokerSideClient) recvPublishMessage(m *PublishMessage) (err error) {
	if m.Dup {
		// re-delivered
	} else {
		// first time delivery
	}

	if m.Retain {
		// store tehe application message to designated topic
		data := string(m.Payload)
		if m.QoS == 0 && len(data) > 0 {
			// TODO: warnning, in this case data cannot be stored.
			// discard retained message
			data = ""
		}
		self.Broker.TopicRoot.ApplyRetain(m.TopicName, m.QoS, data)
	}

	nodes, err := self.Broker.TopicRoot.GetTopicNodes(m.TopicName, true)
	if err != nil {
		return err
	}
	for subscriberID, reqQoS := range nodes[0].Subscribers {
		subscriber, _ := self.Broker.Clients[subscriberID]
		self.Broker.checkQoSAndPublish(subscriber, m.QoS, reqQoS, false, m.TopicName, m.Payload)
	}

	switch m.QoS {
	// in any case, Dub must be 0
	case 0:
		if m.PacketID != 0 {
			return PACKET_ID_SHOULD_BE_ZERO
		}
	case 1:
		puback := NewPubackMessage(m.PacketID)
		self.WriteChan <- puback
	case 2:
		pubrec := NewPubrecMessage(m.PacketID)
		self.WriteChan <- pubrec
	}
	return err
}

func (self *BrokerSideClient) recvPubackMessage(m *PubackMessage) (err error) {
	// acknowledge the sent Publish packet
	if m.PacketID > 0 {
		err = self.AckMessage(m.PacketID)
	}
	return err
}

func (self *BrokerSideClient) recvPubrecMessage(m *PubrecMessage) (err error) {
	// acknowledge the sent Publish packet
	err = self.AckMessage(m.PacketID)
	if err != nil {
		return err
	}
	pubrel := NewPubrelMessage(m.PacketID)
	self.WriteChan <- pubrel
	return err
}

func (self *BrokerSideClient) recvPubrelMessage(m *PubrelMessage) (err error) {
	// acknowledge the sent Pubrel packet
	err = self.AckMessage(m.PacketID)
	if err != nil {
		return err
	}
	pubcomp := NewPubcompMessage(m.PacketID)
	self.WriteChan <- pubcomp
	return err
}

func (self *BrokerSideClient) recvPubcompMessage(m *PubcompMessage) (err error) {
	// acknowledge the sent Pubrel packet
	err = self.AckMessage(m.PacketID)
	return err
}

func (self *BrokerSideClient) recvSubscribeMessage(m *SubscribeMessage) (err error) {
	// TODO: check The wild card is permitted
	returnCodes := make([]SubscribeReturnCode, 0)
	for _, subTopic := range m.SubscribeTopics {
		// TODO: need to validate wheter there are same topics or not
		edges, err := self.Broker.TopicRoot.GetTopicNodes(subTopic.Topic, true)
		codes := make([]SubscribeReturnCode, len(edges))
		if err != nil {
			for i, _ := range codes {
				codes[i] = SubscribeFailure
			}
		} else {
			for i, edge := range edges {
				edge.Subscribers[self.ID] = subTopic.QoS
				codes[i] = SubscribeReturnCode(subTopic.QoS)
				self.SubTopics = append(self.SubTopics,
					&SubscribeTopic{SubscribeAck,
						edge.FullPath,
						uint8(subTopic.QoS),
					})
				if len(edge.RetainMessage) > 0 {
					// publish retain
					// TODO: check all arguments
					self.Broker.checkQoSAndPublish(self, edge.RetainQoS, subTopic.QoS, true, edge.FullPath, []uint8(edge.RetainMessage))
					EmitError(err)
				}
			}
		}
		returnCodes = append(returnCodes, codes...)
	}
	// TODO: check whether the number of return codes are correct?
	suback := NewSubackMessage(m.PacketID, returnCodes)
	self.WriteChan <- suback
	return err
}

func (self *BrokerSideClient) recvSubackMessage(m *SubackMessage) (err error) {
	return INVALID_MESSAGE_CAME
}
func (self *BrokerSideClient) recvUnsubscribeMessage(m *UnsubscribeMessage) (err error) {
	if len(m.TopicNames) == 0 {
		// protocol violation
	}

	for _, name := range m.TopicNames {
		self.Broker.TopicRoot.DeleteSubscriber(self.ID, name)
	}
	// TODO: optimize here
	result := []*SubscribeTopic{}
	for _, t := range self.SubTopics {
		del := false
		for _, name := range m.TopicNames {
			if string(t.Topic) == string(name) {
				del = true
			}
		}
		if !del {
			result = append(result, t)
		}
	}
	self.SubTopics = result
	unsuback := NewUnsubackMessage(m.PacketID)

	self.WriteChan <- unsuback
	return err
}
func (self *BrokerSideClient) recvUnsubackMessage(m *UnsubackMessage) (err error) {
	return INVALID_MESSAGE_CAME
}

func (self *BrokerSideClient) recvPingreqMessage(m *PingreqMessage) (err error) {
	// Pingresp
	// TODO: calc elapsed time from previous pingreq.
	//       and store the time to duration of Transport
	pingresp := NewPingrespMessage()
	self.WriteChan <- pingresp
	if self.KeepAlive != 0 {
		self.ResetTimer()
		go self.RunClientTimer()
	}
	return err
}

func (self *BrokerSideClient) recvPingrespMessage(m *PingrespMessage) (err error) {
	return INVALID_MESSAGE_CAME
}

func (self *BrokerSideClient) recvDisconnectMessage(m *DisconnectMessage) (err error) {
	self.Will = nil
	self.disconnectProcessing()
	// close the client
	return err
}
<|endoftext|>


<commit_before>package vt10x

import (
	"io"
	"strings"
	"testing"
)

func extractStr(t *State, x0, x1, row int) string {
	var s []rune
	for i := x0; i <= x1; i++ {
		c, _, _ := t.Cell(i, row)
		s = append(s, c)
	}
	return string(s)
}

func TestPlainChars(t *testing.T) {
	var st State
	term, err := Create(&st, nil)
	if err != nil {
		t.Fatal(err)
	}
	expected := "Hello world!"
	_, err = term.Write([]byte(expected))
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}
	actual := extractStr(&st, 0, len(expected)-1, 0)
	if expected != actual {
		t.Fatal(actual)
	}
}

func TestNewline(t *testing.T) {
	var st State
	term, err := Create(&st, nil)
	if err != nil {
		t.Fatal(err)
	}
	expected := "Hello world!\n...and more."
	_, err = term.Write([]byte("\033[20h")) // set CRLF mode
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}
	_, err = term.Write([]byte(expected))
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}

	split := strings.Split(expected, "\n")
	actual := extractStr(&st, 0, len(split[0])-1, 0)
	actual += "\n"
	actual += extractStr(&st, 0, len(split[1])-1, 1)
	if expected != actual {
		t.Fatal(actual)
	}

	// A newline with a color set should not make the next line that color,
	// which used to happen if it caused a scroll event.
	st.moveTo(0, st.rows-1)
	_, err = term.Write([]byte("\033[1;37m\n$ \033[m"))
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}
	_, fg, bg := st.Cell(st.Cursor())
	if fg != DefaultFG {
		t.Fatal(st.cur.x, st.cur.y, fg, bg)
	}
}
<commit_msg>Add test for DSR CPR<commit_after>package vt10x

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"testing"

	expect "github.com/Netflix/go-expect"
	"github.com/stretchr/testify/require"
	"golang.org/x/crypto/ssh/terminal"
)

func extractStr(t *State, x0, x1, row int) string {
	var s []rune
	for i := x0; i <= x1; i++ {
		c, _, _ := t.Cell(i, row)
		s = append(s, c)
	}
	return string(s)
}

func TestPlainChars(t *testing.T) {
	var st State
	term, err := Create(&st, nil)
	if err != nil {
		t.Fatal(err)
	}
	expected := "Hello world!"
	_, err = term.Write([]byte(expected))
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}
	actual := extractStr(&st, 0, len(expected)-1, 0)
	if expected != actual {
		t.Fatal(actual)
	}
}

func TestNewline(t *testing.T) {
	var st State
	term, err := Create(&st, nil)
	if err != nil {
		t.Fatal(err)
	}
	expected := "Hello world!\n...and more."
	_, err = term.Write([]byte("\033[20h")) // set CRLF mode
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}
	_, err = term.Write([]byte(expected))
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}

	split := strings.Split(expected, "\n")
	actual := extractStr(&st, 0, len(split[0])-1, 0)
	actual += "\n"
	actual += extractStr(&st, 0, len(split[1])-1, 1)
	if expected != actual {
		t.Fatal(actual)
	}

	// A newline with a color set should not make the next line that color,
	// which used to happen if it caused a scroll event.
	st.moveTo(0, st.rows-1)
	_, err = term.Write([]byte("\033[1;37m\n$ \033[m"))
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}
	_, fg, bg := st.Cell(st.Cursor())
	if fg != DefaultFG {
		t.Fatal(st.cur.x, st.cur.y, fg, bg)
	}
}

var (
	dsrPattern = regexp.MustCompile(`(\d+);(\d+)`)
)

type Coord struct {
	row int
	col int
}

func TestVTCPR(t *testing.T) {
	c, err := expect.NewConsole()
	require.NoError(t, err)
	defer c.Close()

	var state State
	term, err := Create(&state, c)
	require.NoError(t, err)
	defer term.Close()

	go func() {
		for {
			err := term.Parse()
			if err != nil {
				t.Log(err)
				break
			}
		}
	}()

	coord, err := cpr(c.Tty())
	require.NoError(t, err)
	require.Equal(t, 1, coord.row)
	require.Equal(t, 1, coord.col)
}

// cpr is an example application that requests for the cursor position report.
func cpr(tty *os.File) (*Coord, error) {
	oldState, err := terminal.MakeRaw(int(tty.Fd()))
	if err != nil {
		return nil, err
	}

	defer terminal.Restore(int(tty.Fd()), oldState)

	// ANSI escape sequence for DSR - Device Status Report
	// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
	fmt.Fprint(tty, "\x1b[6n")

	// Reports the cursor position (CPR) to the application as (as though typed at
	// the keyboard) ESC[n;mR, where n is the row and m is the column.
	reader := bufio.NewReader(tty)
	text, err := reader.ReadSlice('R')
	if err != nil {
		return nil, err
	}

	matches := dsrPattern.FindStringSubmatch(string(text))
	if len(matches) != 3 {
		return nil, fmt.Errorf("incorrect number of matches: %d", len(matches))
	}

	col, err := strconv.Atoi(matches[2])
	if err != nil {
		return nil, err
	}

	row, err := strconv.Atoi(matches[1])
	if err != nil {
		return nil, err
	}

	return &Coord{row, col}, nil
}
<|endoftext|>


<commit_before>package metadata

/*****************************************
 * This file was generated by SalsaFlow. *
 * Please do not modify it manually.     *
 *****************************************/

const Version = "0.14.0-dev"
<commit_msg>Bump version to 0.15.0-dev<commit_after>package metadata

/*****************************************
 * This file was generated by SalsaFlow. *
 * Please do not modify it manually.     *
 *****************************************/

const Version = "0.15.0-dev"
<|endoftext|>