<commit_before>package orderer

import (
	"fmt"
	"os"
	"sync"
	"time"

	"google.golang.org/grpc"

	"github.com/ellcrys/util"
	"github.com/ncodes/cocoon/core/scheduler"
	logging "github.com/op/go-logging"
)

var discoveryLog = logging.MustGetLogger("orderer.discovery")

// Discovery defines a structure for fetching a list of addresses of orderers
// accessible in the cluster.
type Discovery struct {
	mu           *sync.Mutex
	orderersAddr []string
	ticker       *time.Ticker
	OnUpdateFunc func(addrs []string)
}

// NewDiscovery creates a new discovery object.
func NewDiscovery() *Discovery {
	return &Discovery{
		mu: &sync.Mutex{},
	}
}

// Discover fetches a list of orderer service addresses
// via consul service discovery API.
// For development purpose, If DEV_ORDERER_ADDR is set,
// it will fetch the orderer address from the env variable.
func (od *Discovery) discover() error {

	var err error

	if len(os.Getenv("DEV_ORDERER_ADDR")) > 0 {
		od.orderersAddr = []string{os.Getenv("DEV_ORDERER_ADDR")}
		return nil
	}

	ds := scheduler.NomadServiceDiscovery{
		ConsulAddr: util.Env("CONSUL_ADDR", "localhost:8500"),
		Protocol:   "http",
	}

	_orderers, err := ds.GetByID("orderers", nil)
	if err != nil {
		return err
	}

	var orderers []string
	for _, orderer := range _orderers {
		orderers = append(orderers, fmt.Sprintf("%s:%d", orderer.IP, int(orderer.Port)))
	}

	od.mu.Lock()
	od.orderersAddr = orderers
	od.mu.Unlock()
	return nil
}

// Discover starts a ticker that discovers and updates the list
// of orderer addresses. It will perform the discovery immediately
// and will return error if it fails, otherwise nil is returned and
// subsequent discovery will be performed periodically
func (od *Discovery) Discover() error {

	// run immediately
	if err := od.discover(); err != nil {
		return err
	}

	// run on interval
	od.ticker = time.NewTicker(15 * time.Second)
	for _ = range od.ticker.C {
		err := od.discover()
		if err != nil {
			discoveryLog.Error(err.Error())
			if od.OnUpdateFunc != nil {
				od.OnUpdateFunc(od.GetAddrs())
			}
		}
	}
	return nil
}

// GetAddrs returns the list of discovered addresses
func (od *Discovery) GetAddrs() []string {
	return od.orderersAddr
}

// GetGRPConn dials a random orderer address and returns a
// grpc connection. If no orderer address has been discovered, nil and are error are returned.
func (od *Discovery) GetGRPConn() (*grpc.ClientConn, error) {

	var selected string

	od.mu.Lock()
	if len(od.orderersAddr) == 0 {
		return nil, fmt.Errorf("no known orderer address")
	}

	if len(od.orderersAddr) == 1 {
		selected = od.orderersAddr[0]
	} else {
		selected = od.orderersAddr[util.RandNum(0, len(od.orderersAddr))]
	}

	client, err := grpc.Dial(selected, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}

	od.mu.Unlock()

	return client, nil
}

// Stop stops the discovery ticker
func (od *Discovery) Stop() {
	od.ticker.Stop()
}
<commit_msg>debug<commit_after>package orderer

import (
	"fmt"
	"os"
	"sync"
	"time"

	"google.golang.org/grpc"

	"github.com/ellcrys/util"
	"github.com/ncodes/cocoon/core/scheduler"
	logging "github.com/op/go-logging"
)

var discoveryLog = logging.MustGetLogger("orderer.discovery")

// Discovery defines a structure for fetching a list of addresses of orderers
// accessible in the cluster.
type Discovery struct {
	mu           *sync.Mutex
	orderersAddr []string
	ticker       *time.Ticker
	OnUpdateFunc func(addrs []string)
}

// NewDiscovery creates a new discovery object.
func NewDiscovery() *Discovery {
	return &Discovery{
		mu: &sync.Mutex{},
	}
}

// Discover fetches a list of orderer service addresses
// via consul service discovery API.
// For development purpose, If DEV_ORDERER_ADDR is set,
// it will fetch the orderer address from the env variable.
func (od *Discovery) discover() error {

	var err error

	if len(os.Getenv("DEV_ORDERER_ADDR")) > 0 {
		od.orderersAddr = []string{os.Getenv("DEV_ORDERER_ADDR")}
		return nil
	}

	ds := scheduler.NomadServiceDiscovery{
		ConsulAddr: util.Env("CONSUL_ADDR", "localhost:8500"),
		Protocol:   "http",
	}

	_orderers, err := ds.GetByID("orderers", nil)
	if err != nil {
		return err
	}

	var orderers []string
	for _, orderer := range _orderers {
		orderers = append(orderers, fmt.Sprintf("%s:%d", orderer.IP, int(orderer.Port)))
	}

	od.mu.Lock()
	od.orderersAddr = orderers
	od.mu.Unlock()
	return nil
}

// Discover starts a ticker that discovers and updates the list
// of orderer addresses. It will perform the discovery immediately
// and will return error if it fails, otherwise nil is returned and
// subsequent discovery will be performed periodically
func (od *Discovery) Discover() error {

	// run immediately
	if err := od.discover(); err != nil {
		return err
	}

	// run on interval
	od.ticker = time.NewTicker(15 * time.Second)
	for _ = range od.ticker.C {
		err := od.discover()
		if err != nil {
			discoveryLog.Error(err.Error())
			if od.OnUpdateFunc != nil {
				od.OnUpdateFunc(od.GetAddrs())
			}
		}
	}
	return nil
}

// GetAddrs returns the list of discovered addresses
func (od *Discovery) GetAddrs() []string {
	return od.orderersAddr
}

// GetGRPConn dials a random orderer address and returns a
// grpc connection. If no orderer address has been discovered, nil and are error are returned.
func (od *Discovery) GetGRPConn() (*grpc.ClientConn, error) {

	var selected string
	log.Info("Here")
	od.mu.Lock()
	defer od.mu.Unlock()
	log.Info("Here 2")
	if len(od.orderersAddr) == 0 {
		return nil, fmt.Errorf("no known orderer address")
	}
	log.Info("Here 3")
	if len(od.orderersAddr) == 1 {
		selected = od.orderersAddr[0]
	} else {
		selected = od.orderersAddr[util.RandNum(0, len(od.orderersAddr))]
	}
	log.Info("Here 4")
	client, err := grpc.Dial(selected, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}

	od.mu.Unlock()
	log.Info("Here 5")

	return client, nil
}

// Stop stops the discovery ticker
func (od *Discovery) Stop() {
	od.ticker.Stop()
}
<|endoftext|>


<commit_before>package wikiio

import (
	. "github.com/OUCC/syaro/logger"
	"github.com/OUCC/syaro/setting"
	"github.com/OUCC/syaro/util"

	"gopkg.in/fsnotify.v1"

	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var (
	WikiRoot    *WikiFile
	searchIndex map[string][]*WikiFile
	watcher     *fsnotify.Watcher
)

var (
	ErrNotExist = errors.New("file not exist")
	ErrNotFound = errors.New("file not found")
)

func InitWatcher() {
	const HIDDEN_DIR = "/."

	var err error
	watcher, err = fsnotify.NewWatcher()
	if err != nil {
		Log.Fatal(err)
	}

	// event loop for watcher
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				Log.Debug("%s", event)
				switch {
				case event.Op&fsnotify.Create != 0:
					Log.Info("New file Created (%s)", event.Name)
					BuildIndex()
					Log.Info("File index refreshed")

				case event.Op&fsnotify.Remove != 0:
					Log.Info("File removed (%s)", event.Name)
					BuildIndex()
					Log.Info("File index refreshed")
				}

			case err := <-watcher.Errors:
				Log.Fatal(err)
			}
		}
	}()

	filepath.Walk(setting.WikiRoot, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			Log.Error(err.Error())
		}

		if info.IsDir() && !strings.Contains(path, HIDDEN_DIR) {
			watcher.Add(path)
			Log.Debug("%s added to watcher", path)
		}

		return nil
	})
}

func CloseWatcher() {
	watcher.Close()
}

// must be called after setting.WikiRoot is set
func BuildIndex() {
	Log.Debug("Index building start")

	info, err := os.Stat(setting.WikiRoot)
	if err != nil {
		Log.Fatal(err)
	}

	WikiRoot = &WikiFile{
		parentDir: nil,
		wikiPath:  "/",
		fileInfo:  info,
	}
	searchIndex = make(map[string][]*WikiFile)

	walkfunc(WikiRoot)

	Log.Debug("Index building end")
}

// func for recursive
func walkfunc(dir *WikiFile) {
	infos, _ := ioutil.ReadDir(filepath.Join(setting.WikiRoot, dir.WikiPath()))

	dir.files = make([]*WikiFile, 0, len(infos))
	for _, info := range infos {
		// skip hidden file
		if info.Name()[:1] == "." {
			continue
		}

		file := &WikiFile{
			parentDir: dir,
			wikiPath:  filepath.Join(dir.WikiPath(), info.Name()),
			fileInfo:  info,
		}
		dir.files = append(dir.files, file)

		// register to searchIndex
		elem, present := searchIndex[file.Name()]
		if present {
			searchIndex[file.Name()] = append(elem, file)
		} else {
			searchIndex[file.Name()] = []*WikiFile{file}
		}

		elem, present = searchIndex[file.NameWithoutExt()]
		if present {
			searchIndex[file.NameWithoutExt()] = append(elem, file)
		} else {
			searchIndex[file.NameWithoutExt()] = []*WikiFile{file}
		}

		if info.IsDir() {
			walkfunc(file)
		}
	}
}

func Load(wpath string) (*WikiFile, error) {
	Log.Debug("wikiio.Load(%s)", wpath)

	// wiki root
	if wpath == "/" || wpath == "." || wpath == "" {
		return WikiRoot, nil
	}

	sl := strings.Split(wpath, "/")
	ret := WikiRoot
	for _, s := range sl {
		if s == "" {
			continue
		}

		tmp := ret
		for _, f := range ret.Files() {
			if f.Name() == s || util.RemoveExt(f.Name()) == s {
				ret = f
				break
			}
		}
		// not found
		if ret == tmp {
			Log.Debug("wikiio.Load: not exist")
			return nil, ErrNotExist
		}
	}

	return ret, nil
}

func Search(name string) ([]*WikiFile, error) {
	Log.Debug("wikiio.Search(%s)", name)
	files, present := searchIndex[name]
	if !present {
		Log.Debug("not found")
		return nil, ErrNotFound
	}

	// for debug output
	found := make([]string, len(files))
	for i := 0; i < len(found); i++ {
		found[i] = files[i].WikiPath()
	}
	Log.Debug("found %v", found)

	return files, nil
}

func Create(wpath string) error {
	Log.Debug("wikiio.Create(%s)", wpath)

	const initialText = "New Page\n========\n"

	// check if file is already exists
	file, _ := Load(wpath)
	if file != nil {
		// if exists, return error
		return os.ErrExist
	}

	if !util.IsMarkdown(wpath) {
		wpath += ".md"
	}

	path := filepath.Join(setting.WikiRoot, wpath)
	os.MkdirAll(filepath.Dir(path), 0755)
	err := ioutil.WriteFile(path, []byte(initialText), 0644)
	if err != nil {
		Log.Debug(err.Error())
		return err
	}

	return nil
}

func Rename(oldpath string, newpath string) error {
	Log.Debug("wikiio.Rename(%s, %s)", oldpath, newpath)

	f, err := Load(oldpath)
	if err != nil {
		return err
	}

	if !util.IsMarkdown(newpath) {
		newpath += ".md"
	}

	path := filepath.Join(setting.WikiRoot, newpath)
	os.MkdirAll(filepath.Dir(path), 0755)
	err = os.Rename(f.FilePath(), path)
	if err != nil {
		Log.Debug("can't rename: %s", err)
		return err
	}

	return nil
}
<commit_msg>[MOD] make walkfunc anonymous recursive function<commit_after>package wikiio

import (
	. "github.com/OUCC/syaro/logger"
	"github.com/OUCC/syaro/setting"
	"github.com/OUCC/syaro/util"

	"gopkg.in/fsnotify.v1"

	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var (
	WikiRoot    *WikiFile
	searchIndex map[string][]*WikiFile
	watcher     *fsnotify.Watcher
)

var (
	ErrNotExist = errors.New("file not exist")
	ErrNotFound = errors.New("file not found")
)

func InitWatcher() {
	const HIDDEN_DIR = "/."

	var err error
	watcher, err = fsnotify.NewWatcher()
	if err != nil {
		Log.Fatal(err)
	}

	// event loop for watcher
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				Log.Debug("%s", event)
				switch {
				case event.Op&fsnotify.Create != 0:
					Log.Info("New file Created (%s)", event.Name)
					BuildIndex()
					Log.Info("File index refreshed")

				case event.Op&fsnotify.Remove != 0:
					Log.Info("File removed (%s)", event.Name)
					BuildIndex()
					Log.Info("File index refreshed")
				}

			case err := <-watcher.Errors:
				Log.Fatal(err)
			}
		}
	}()

	filepath.Walk(setting.WikiRoot, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			Log.Error(err.Error())
		}

		if info.IsDir() && !strings.Contains(path, HIDDEN_DIR) {
			watcher.Add(path)
			Log.Debug("%s added to watcher", path)
		}

		return nil
	})
}

func CloseWatcher() {
	watcher.Close()
}

// must be called after setting.WikiRoot is set
func BuildIndex() {
	Log.Debug("Index building start")

	info, err := os.Stat(setting.WikiRoot)
	if err != nil {
		Log.Fatal(err)
	}

	WikiRoot = &WikiFile{
		parentDir: nil,
		wikiPath:  "/",
		fileInfo:  info,
	}
	searchIndex = make(map[string][]*WikiFile)

	// anonymous recursive function
	var walkfunc func(*WikiFile)
	walkfunc = func(dir *WikiFile) {
		infos, _ := ioutil.ReadDir(filepath.Join(setting.WikiRoot, dir.WikiPath()))

		dir.files = make([]*WikiFile, 0, len(infos))
		for _, info := range infos {
			// skip hidden file
			if info.Name()[:1] == "." {
				continue
			}

			file := &WikiFile{
				parentDir: dir,
				wikiPath:  filepath.Join(dir.WikiPath(), info.Name()),
				fileInfo:  info,
			}
			dir.files = append(dir.files, file)

			// register to searchIndex
			elem, present := searchIndex[file.Name()]
			if present {
				searchIndex[file.Name()] = append(elem, file)
			} else {
				searchIndex[file.Name()] = []*WikiFile{file}
			}

			elem, present = searchIndex[file.NameWithoutExt()]
			if present {
				searchIndex[file.NameWithoutExt()] = append(elem, file)
			} else {
				searchIndex[file.NameWithoutExt()] = []*WikiFile{file}
			}

			if info.IsDir() {
				walkfunc(file)
			}
		}
	}
	walkfunc(WikiRoot)

	Log.Debug("Index building end")
}

func Load(wpath string) (*WikiFile, error) {
	Log.Debug("wikiio.Load(%s)", wpath)

	// wiki root
	if wpath == "/" || wpath == "." || wpath == "" {
		return WikiRoot, nil
	}

	sl := strings.Split(wpath, "/")
	ret := WikiRoot
	for _, s := range sl {
		if s == "" {
			continue
		}

		tmp := ret
		for _, f := range ret.Files() {
			if f.Name() == s || util.RemoveExt(f.Name()) == s {
				ret = f
				break
			}
		}
		// not found
		if ret == tmp {
			Log.Debug("wikiio.Load: not exist")
			return nil, ErrNotExist
		}
	}

	return ret, nil
}

func Search(name string) ([]*WikiFile, error) {
	Log.Debug("wikiio.Search(%s)", name)
	files, present := searchIndex[name]
	if !present {
		Log.Debug("not found")
		return nil, ErrNotFound
	}

	// for debug output
	found := make([]string, len(files))
	for i := 0; i < len(found); i++ {
		found[i] = files[i].WikiPath()
	}
	Log.Debug("found %v", found)

	return files, nil
}

func Create(wpath string) error {
	Log.Debug("wikiio.Create(%s)", wpath)

	const initialText = "New Page\n========\n"

	// check if file is already exists
	file, _ := Load(wpath)
	if file != nil {
		// if exists, return error
		return os.ErrExist
	}

	if !util.IsMarkdown(wpath) {
		wpath += ".md"
	}

	path := filepath.Join(setting.WikiRoot, wpath)
	os.MkdirAll(filepath.Dir(path), 0755)
	err := ioutil.WriteFile(path, []byte(initialText), 0644)
	if err != nil {
		Log.Debug(err.Error())
		return err
	}

	return nil
}

func Rename(oldpath string, newpath string) error {
	Log.Debug("wikiio.Rename(%s, %s)", oldpath, newpath)

	f, err := Load(oldpath)
	if err != nil {
		return err
	}

	if !util.IsMarkdown(newpath) {
		newpath += ".md"
	}

	path := filepath.Join(setting.WikiRoot, newpath)
	os.MkdirAll(filepath.Dir(path), 0755)
	err = os.Rename(f.FilePath(), path)
	if err != nil {
		Log.Debug("can't rename: %s", err)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/tools/go/loader"

	"sourcegraph.com/sourcegraph/srclib-go/gog"
	"sourcegraph.com/sourcegraph/srclib-go/gog/definfo"
	defpkg "sourcegraph.com/sourcegraph/srclib-go/golang_def"
	"sourcegraph.com/sourcegraph/srclib/graph"
	"sourcegraph.com/sourcegraph/srclib/unit"
)

func init() {
	_, err := parser.AddCommand("graph",
		"graph a Go package",
		"Graph a Go package, producing all defs, refs, and docs.",
		&graphCmd,
	)
	if err != nil {
		log.Fatal(err)
	}

	// Check that we have the '-i' flag.
	cmd := exec.Command("go", "help", "build")
	o, err := cmd.Output()
	if err != nil {
		log.Fatal(err)
	}
	usage := strings.Split(string(o), "\n")[0] // The usage is on the first line.
	matched, err := regexp.MatchString("-i", usage)
	if err != nil {
		log.Fatal(err)
	}
	if !matched {
		log.Fatal("'go build' does not have the '-i' flag. Please upgrade to go1.3+.")
	}
}

type GraphCmd struct{}

var graphCmd GraphCmd

// allowErrorsInGoGet is whether the grapher should continue after
// if `go get` fails.
var allowErrorsInGoGet = true

func (c *GraphCmd) Execute(args []string) error {
	var unit *unit.SourceUnit
	if err := json.NewDecoder(os.Stdin).Decode(&unit); err != nil {
		return err
	}
	if err := os.Stdin.Close(); err != nil {
		return err
	}

	if err := unmarshalTypedConfig(unit.Config); err != nil {
		return err
	}
	if err := config.apply(); err != nil {
		return err
	}

	if os.Getenv("IN_DOCKER_CONTAINER") != "" || os.Getenv("SRCLIB_FETCH_DEPS") != "" {
		buildPkg, err := UnitDataAsBuildPackage(unit)
		if err != nil {
			return err
		}

		// Make a new primary GOPATH.
		mainGOPATHDir, err := ioutil.TempDir("", "gopath")
		if err != nil {
			return err
		}
		defer os.RemoveAll(mainGOPATHDir)
		if buildContext.GOPATH == "" {
			buildContext.GOPATH = mainGOPATHDir
		} else {
			buildContext.GOPATH = mainGOPATHDir + string(filepath.ListSeparator) + buildContext.GOPATH
		}

		// Set up GOPATH so it has this repo.
		log.Printf("Setting up a new GOPATH at %s", mainGOPATHDir)
		dir := filepath.Join(mainGOPATHDir, "src", string(unit.Repo))
		if err := os.MkdirAll(filepath.Dir(dir), 0700); err != nil {
			return err
		}
		log.Printf("Creating symlink to oldname %q at newname %q.", cwd, dir)
		if err := os.Symlink(cwd, dir); err != nil {
			return err
		}

		// For every GOPATH that was in the Srcfile (or autodetected),
		// move it to a writable dir. (/src is not writable.)
		if config.GOPATH != "" {
			dirs := filepath.SplitList(buildContext.GOPATH)
			for i, dir := range dirs {
				if dir == mainGOPATHDir || dir == os.Getenv("GOPATH") {
					continue
				}

				oldSrcDir := filepath.Join(dir, "src")
				newGOPATH := filepath.Join(os.TempDir(), "gopath-"+strconv.Itoa(i)+"-"+filepath.Base(dir))
				newSrcDir := filepath.Join(newGOPATH, "src")
				log.Printf("Creating symlink for non-primary GOPATH to oldname %q at newname %q.", oldSrcDir, newSrcDir)
				if err := os.MkdirAll(filepath.Dir(newSrcDir), 0700); err != nil {
					return err
				}
				if err := os.Symlink(oldSrcDir, newSrcDir); err != nil {
					return err
				}
				dirs[i] = newGOPATH
				effectiveConfigGOPATHs = append(effectiveConfigGOPATHs, newSrcDir)
			}
			buildContext.GOPATH = strings.Join(dirs, string(filepath.ListSeparator))
		}

		log.Printf("Changing directory to %q.", dir)
		if err := os.Chdir(dir); err != nil {
			return err
		}
		dockerCWD = cwd

		if config.GOROOT == "" {
			cwd = dir
		}

		// Get and install deps. But don't get deps that:
		//
		// * are in this repo; if we call `go get` on this repo, we will either try to check out a different
		//   version or fail with 'stale checkout?' because the .dockerignore doesn't copy the .git dir.
		// * are "C" as in `import "C"` for cgo
		// * have no slashes in their import path (which occurs when graphing the Go stdlib, for pkgs like "fmt");
		//   we'll just assume that these packages are never remote and do not need `go get`ting.
		var externalDeps []string
		for _, dep := range unit.Dependencies {
			importPath := dep.(string)
			if !strings.HasPrefix(importPath, string(unit.Repo)) && importPath != "C" && strings.Count(filepath.Clean(importPath), string(filepath.Separator)) > 0 {
				externalDeps = append(externalDeps, importPath)
			}
		}
		deps := append([]string{"." + string(filepath.Separator) + buildPkg.Dir}, externalDeps...)
		for _, dep := range deps {
			cmd := exec.Command(goBinaryName, "get", "-d", "-t", "-v", dep)
			cmd.Args = append(cmd.Args)
			cmd.Env = config.env()
			cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
			log.Printf("%v (env vars: %v).", cmd.Args, cmd.Env)
			if err := cmd.Run(); err != nil {
				if allowErrorsInGoGet {
					log.Printf("%v failed: %s (continuing)", cmd.Args, err)
				} else {
					return err
				}
			}
		}
		log.Printf("Finished downloading dependencies.")
	}

	out, err := Graph(unit)
	if err != nil {
		return err
	}

	// Make paths relative to repo.
	for _, gs := range out.Defs {
		if gs.File == "" {
			log.Printf("no file %+v", gs)
		}
		if gs.File != "" {
			gs.File = relPath(cwd, gs.File)
		}
	}
	for _, gr := range out.Refs {
		if gr.File != "" {
			gr.File = relPath(cwd, gr.File)
		}
	}
	for _, gd := range out.Docs {
		if gd.File != "" {
			gd.File = relPath(cwd, gd.File)
		}
	}

	if err := json.NewEncoder(os.Stdout).Encode(out); err != nil {
		return err
	}
	return nil
}

func relPath(base, path string) string {
	rp, err := filepath.Rel(evalSymlinks(base), evalSymlinks(path))
	if err != nil {
		log.Fatalf("Failed to make path %q relative to %q: %s", path, base, err)
	}

	// TODO(sqs): hack
	if strings.HasPrefix(rp, filepath.Join("..", "..", "..")+string(filepath.Separator)) && dockerCWD != "" {
		rp, err = filepath.Rel(dockerCWD, path)
		if err != nil {
			log.Fatalf("Failed to make path %q relative to %q: %s", path, cwd, err)
		}
	}

	// TODO(sqs): hack
	if prefix := filepath.Join("..", "tmp", "gopath-"); strings.HasPrefix(rp, prefix) {
		rp = rp[len(prefix)+2:]
	}

	return filepath.ToSlash(rp)
}

func Graph(unit *unit.SourceUnit) (*graph.Output, error) {
	pkg, err := UnitDataAsBuildPackage(unit)
	if err != nil {
		return nil, err
	}

	o, err := doGraph(pkg)
	if err != nil {
		return nil, err
	}

	o2 := graph.Output{}

	uri := string(unit.Repo)

	for _, gs := range o.Defs {
		d, err := convertGoDef(gs, uri)
		if err != nil {
			return nil, err
		}
		if d != nil {
			o2.Defs = append(o2.Defs, d)
		}
	}
	for _, gr := range o.Refs {
		r, err := convertGoRef(gr, uri)
		if err != nil {
			return nil, err
		}
		if r != nil {
			o2.Refs = append(o2.Refs, r)
		}
	}
	for _, gd := range o.Docs {
		d, err := convertGoDoc(gd, uri)
		if err != nil {
			return nil, err
		}
		if d != nil {
			o2.Docs = append(o2.Docs, d)
		}
	}

	return &o2, nil
}

func convertGoDef(gs *gog.Def, repoURI string) (*graph.Def, error) {
	resolvedTarget, err := ResolveDep(gs.DefKey.PackageImportPath, repoURI)
	if err != nil {
		return nil, err
	}
	path := filepath.ToSlash(pathOrDot(filepath.Join(gs.Path...)))
	treePath := treePath(strings.Replace(string(path), ".go", "", -1))
	if !graph.IsValidTreePath(treePath) {
		return nil, fmt.Errorf("'%s' is not a valid tree-path", treePath)
	}

	def := &graph.Def{
		DefKey: graph.DefKey{
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
			Path:     path,
		},
		TreePath: treePath,

		Name: gs.Name,
		Kind: definfo.GeneralKindMap[gs.Kind],

		File:     filepath.ToSlash(gs.File),
		DefStart: gs.DeclSpan[0],
		DefEnd:   gs.DeclSpan[1],

		Exported: gs.DefInfo.Exported,
		Local:    !gs.DefInfo.Exported && !gs.DefInfo.PkgScope,
		Test:     strings.HasSuffix(gs.File, "_test.go"),
	}

	d := defpkg.DefData{
		PackageImportPath: gs.DefKey.PackageImportPath,
		DefInfo:           gs.DefInfo,
	}
	def.Data, err = json.Marshal(d)
	if err != nil {
		return nil, err
	}

	if def.File == "" {
		// some cgo defs have empty File; omit them
		return nil, nil
	}

	return def, nil
}

func convertGoRef(gr *gog.Ref, repoURI string) (*graph.Ref, error) {
	resolvedTarget, err := ResolveDep(gr.Def.PackageImportPath, repoURI)
	if err != nil {
		return nil, err
	}
	if resolvedTarget == nil {
		return nil, nil
	}

	return &graph.Ref{
		DefRepo:     filepath.ToSlash(uriOrEmpty(resolvedTarget.ToRepoCloneURL)),
		DefPath:     filepath.ToSlash(pathOrDot(filepath.Join(gr.Def.Path...))),
		DefUnit:     resolvedTarget.ToUnit,
		DefUnitType: resolvedTarget.ToUnitType,
		Def:         gr.IsDef,
		File:        filepath.ToSlash(gr.File),
		Start:       gr.Span[0],
		End:         gr.Span[1],
	}, nil
}

func convertGoDoc(gd *gog.Doc, repoURI string) (*graph.Doc, error) {
	var key graph.DefKey
	if gd.DefKey != nil {
		resolvedTarget, err := ResolveDep(gd.PackageImportPath, repoURI)
		if err != nil {
			return nil, err
		}
		key = graph.DefKey{
			Path:     filepath.ToSlash(pathOrDot(filepath.Join(gd.Path...))),
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
		}
	}
	return &graph.Doc{
		DefKey: key,
		Format: gd.Format,
		Data:   gd.Data,
		File:   filepath.ToSlash(gd.File),
		Start:  gd.Span[0],
		End:    gd.Span[1],
	}, nil
}

func uriOrEmpty(cloneURL string) string {
	if cloneURL == "" {
		return ""
	}
	return graph.MakeURI(cloneURL)
}

func pathOrDot(path string) string {
	if path == "" {
		return "."
	}
	return path
}

func treePath(path string) string {
	if path == "" || path == "." {
		return string(".")
	}
	return "./" + path
}

// allowErrorsInGraph is whether the grapher should continue after
// encountering "reasonably common" errors (such as compile errors).
var allowErrorsInGraph = true

func doGraph(pkg *build.Package) (*gog.Output, error) {
	importPath := pkg.ImportPath
	importUnsafe := importPath == "unsafe"

	// Special-case: if this is a Cgo package, treat the CgoFiles as GoFiles or
	// else the character offsets will be junk.
	//
	// See https://codereview.appspot.com/86140043.
	loaderConfig.Build.CgoEnabled = false
	build.Default = *loaderConfig.Build
	if len(pkg.CgoFiles) > 0 {
		var allGoFiles []string
		allGoFiles = append(allGoFiles, pkg.GoFiles...)
		allGoFiles = append(allGoFiles, pkg.CgoFiles...)
		allGoFiles = append(allGoFiles, pkg.TestGoFiles...)
		for i, f := range allGoFiles {
			allGoFiles[i] = filepath.Join(cwd, pkg.Dir, f)
		}
		loaderConfig.CreateFromFilenames(pkg.ImportPath, allGoFiles...)
	} else {
		// Normal import
		loaderConfig.ImportWithTests(importPath)
	}

	if importUnsafe {
		// Special-case "unsafe" because go/loader does not let you load it
		// directly.
		if loaderConfig.ImportPkgs == nil {
			loaderConfig.ImportPkgs = make(map[string]bool)
		}
		loaderConfig.ImportPkgs["unsafe"] = true
	}

	prog, err := loaderConfig.Load()
	if err != nil {
		return nil, err
	}

	g := gog.New(prog)

	var pkgs []*loader.PackageInfo
	for _, pkg := range prog.Created {
		if strings.HasSuffix(pkg.Pkg.Name(), "_test") {
			// ignore xtest packages
			continue
		}
		pkgs = append(pkgs, pkg)
	}
	for _, pkg := range prog.Imported {
		pkgs = append(pkgs, pkg)
	}

	for _, pkg := range pkgs {
		if err := g.Graph(pkg); err != nil {
			return nil, err
		}
	}

	return &g.Output, nil
}
<commit_msg>fix failures on subsequent builds of Godeps or custom-GOPATH repos<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/tools/go/loader"

	"sourcegraph.com/sourcegraph/srclib-go/gog"
	"sourcegraph.com/sourcegraph/srclib-go/gog/definfo"
	defpkg "sourcegraph.com/sourcegraph/srclib-go/golang_def"
	"sourcegraph.com/sourcegraph/srclib/graph"
	"sourcegraph.com/sourcegraph/srclib/unit"
)

func init() {
	_, err := parser.AddCommand("graph",
		"graph a Go package",
		"Graph a Go package, producing all defs, refs, and docs.",
		&graphCmd,
	)
	if err != nil {
		log.Fatal(err)
	}

	// Check that we have the '-i' flag.
	cmd := exec.Command("go", "help", "build")
	o, err := cmd.Output()
	if err != nil {
		log.Fatal(err)
	}
	usage := strings.Split(string(o), "\n")[0] // The usage is on the first line.
	matched, err := regexp.MatchString("-i", usage)
	if err != nil {
		log.Fatal(err)
	}
	if !matched {
		log.Fatal("'go build' does not have the '-i' flag. Please upgrade to go1.3+.")
	}
}

type GraphCmd struct{}

var graphCmd GraphCmd

// allowErrorsInGoGet is whether the grapher should continue after
// if `go get` fails.
var allowErrorsInGoGet = true

func (c *GraphCmd) Execute(args []string) error {
	var unit *unit.SourceUnit
	if err := json.NewDecoder(os.Stdin).Decode(&unit); err != nil {
		return err
	}
	if err := os.Stdin.Close(); err != nil {
		return err
	}

	if err := unmarshalTypedConfig(unit.Config); err != nil {
		return err
	}
	if err := config.apply(); err != nil {
		return err
	}

	if os.Getenv("IN_DOCKER_CONTAINER") != "" || os.Getenv("SRCLIB_FETCH_DEPS") != "" {
		buildPkg, err := UnitDataAsBuildPackage(unit)
		if err != nil {
			return err
		}

		// Make a new primary GOPATH.
		mainGOPATHDir, err := ioutil.TempDir("", "gopath")
		if err != nil {
			return err
		}
		defer os.RemoveAll(mainGOPATHDir)
		if buildContext.GOPATH == "" {
			buildContext.GOPATH = mainGOPATHDir
		} else {
			buildContext.GOPATH = mainGOPATHDir + string(filepath.ListSeparator) + buildContext.GOPATH
		}

		// Set up GOPATH so it has this repo.
		log.Printf("Setting up a new GOPATH at %s", mainGOPATHDir)
		dir := filepath.Join(mainGOPATHDir, "src", string(unit.Repo))
		if err := os.MkdirAll(filepath.Dir(dir), 0700); err != nil {
			return err
		}
		log.Printf("Creating symlink to oldname %q at newname %q.", cwd, dir)
		if err := os.Symlink(cwd, dir); err != nil {
			return err
		}

		// For every GOPATH that was in the Srcfile (or autodetected),
		// move it to a writable dir. (/src is not writable.)
		if config.GOPATH != "" {
			dirs := filepath.SplitList(buildContext.GOPATH)
			for i, dir := range dirs {
				if dir == mainGOPATHDir || dir == os.Getenv("GOPATH") {
					continue
				}

				oldSrcDir := filepath.Join(dir, "src")
				newGOPATH, err := ioutil.TempDir("", "gopath-"+strconv.Itoa(i)+"-"+filepath.Base(dir))
				if err != nil {
					return err
				}
				newSrcDir := filepath.Join(newGOPATH, "src")
				log.Printf("Creating symlink for non-primary GOPATH to oldname %q at newname %q.", oldSrcDir, newSrcDir)
				if err := os.MkdirAll(filepath.Dir(newSrcDir), 0700); err != nil {
					return err
				}
				if err := os.Symlink(oldSrcDir, newSrcDir); err != nil {
					return err
				}
				dirs[i] = newGOPATH
				effectiveConfigGOPATHs = append(effectiveConfigGOPATHs, newSrcDir)
			}
			buildContext.GOPATH = strings.Join(dirs, string(filepath.ListSeparator))
		}

		log.Printf("Changing directory to %q.", dir)
		if err := os.Chdir(dir); err != nil {
			return err
		}
		dockerCWD = cwd

		if config.GOROOT == "" {
			cwd = dir
		}

		// Get and install deps. But don't get deps that:
		//
		// * are in this repo; if we call `go get` on this repo, we will either try to check out a different
		//   version or fail with 'stale checkout?' because the .dockerignore doesn't copy the .git dir.
		// * are "C" as in `import "C"` for cgo
		// * have no slashes in their import path (which occurs when graphing the Go stdlib, for pkgs like "fmt");
		//   we'll just assume that these packages are never remote and do not need `go get`ting.
		var externalDeps []string
		for _, dep := range unit.Dependencies {
			importPath := dep.(string)
			if !strings.HasPrefix(importPath, string(unit.Repo)) && importPath != "C" && strings.Count(filepath.Clean(importPath), string(filepath.Separator)) > 0 {
				externalDeps = append(externalDeps, importPath)
			}
		}
		deps := append([]string{"." + string(filepath.Separator) + buildPkg.Dir}, externalDeps...)
		for _, dep := range deps {
			cmd := exec.Command(goBinaryName, "get", "-d", "-t", "-v", dep)
			cmd.Args = append(cmd.Args)
			cmd.Env = config.env()
			cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
			log.Printf("%v (env vars: %v).", cmd.Args, cmd.Env)
			if err := cmd.Run(); err != nil {
				if allowErrorsInGoGet {
					log.Printf("%v failed: %s (continuing)", cmd.Args, err)
				} else {
					return err
				}
			}
		}
		log.Printf("Finished downloading dependencies.")
	}

	out, err := Graph(unit)
	if err != nil {
		return err
	}

	// Make paths relative to repo.
	for _, gs := range out.Defs {
		if gs.File == "" {
			log.Printf("no file %+v", gs)
		}
		if gs.File != "" {
			gs.File = relPath(cwd, gs.File)
		}
	}
	for _, gr := range out.Refs {
		if gr.File != "" {
			gr.File = relPath(cwd, gr.File)
		}
	}
	for _, gd := range out.Docs {
		if gd.File != "" {
			gd.File = relPath(cwd, gd.File)
		}
	}

	if err := json.NewEncoder(os.Stdout).Encode(out); err != nil {
		return err
	}
	return nil
}

func relPath(base, path string) string {
	rp, err := filepath.Rel(evalSymlinks(base), evalSymlinks(path))
	if err != nil {
		log.Fatalf("Failed to make path %q relative to %q: %s", path, base, err)
	}

	// TODO(sqs): hack
	if strings.HasPrefix(rp, filepath.Join("..", "..", "..")+string(filepath.Separator)) && dockerCWD != "" {
		rp, err = filepath.Rel(dockerCWD, path)
		if err != nil {
			log.Fatalf("Failed to make path %q relative to %q: %s", path, cwd, err)
		}
	}

	// TODO(sqs): hack
	if prefix := filepath.Join("..", "tmp", "gopath-"); strings.HasPrefix(rp, prefix) {
		rp = rp[len(prefix)+2:]
	}

	return filepath.ToSlash(rp)
}

func Graph(unit *unit.SourceUnit) (*graph.Output, error) {
	pkg, err := UnitDataAsBuildPackage(unit)
	if err != nil {
		return nil, err
	}

	o, err := doGraph(pkg)
	if err != nil {
		return nil, err
	}

	o2 := graph.Output{}

	uri := string(unit.Repo)

	for _, gs := range o.Defs {
		d, err := convertGoDef(gs, uri)
		if err != nil {
			return nil, err
		}
		if d != nil {
			o2.Defs = append(o2.Defs, d)
		}
	}
	for _, gr := range o.Refs {
		r, err := convertGoRef(gr, uri)
		if err != nil {
			return nil, err
		}
		if r != nil {
			o2.Refs = append(o2.Refs, r)
		}
	}
	for _, gd := range o.Docs {
		d, err := convertGoDoc(gd, uri)
		if err != nil {
			return nil, err
		}
		if d != nil {
			o2.Docs = append(o2.Docs, d)
		}
	}

	return &o2, nil
}

func convertGoDef(gs *gog.Def, repoURI string) (*graph.Def, error) {
	resolvedTarget, err := ResolveDep(gs.DefKey.PackageImportPath, repoURI)
	if err != nil {
		return nil, err
	}
	path := filepath.ToSlash(pathOrDot(filepath.Join(gs.Path...)))
	treePath := treePath(strings.Replace(string(path), ".go", "", -1))
	if !graph.IsValidTreePath(treePath) {
		return nil, fmt.Errorf("'%s' is not a valid tree-path", treePath)
	}

	def := &graph.Def{
		DefKey: graph.DefKey{
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
			Path:     path,
		},
		TreePath: treePath,

		Name: gs.Name,
		Kind: definfo.GeneralKindMap[gs.Kind],

		File:     filepath.ToSlash(gs.File),
		DefStart: gs.DeclSpan[0],
		DefEnd:   gs.DeclSpan[1],

		Exported: gs.DefInfo.Exported,
		Local:    !gs.DefInfo.Exported && !gs.DefInfo.PkgScope,
		Test:     strings.HasSuffix(gs.File, "_test.go"),
	}

	d := defpkg.DefData{
		PackageImportPath: gs.DefKey.PackageImportPath,
		DefInfo:           gs.DefInfo,
	}
	def.Data, err = json.Marshal(d)
	if err != nil {
		return nil, err
	}

	if def.File == "" {
		// some cgo defs have empty File; omit them
		return nil, nil
	}

	return def, nil
}

func convertGoRef(gr *gog.Ref, repoURI string) (*graph.Ref, error) {
	resolvedTarget, err := ResolveDep(gr.Def.PackageImportPath, repoURI)
	if err != nil {
		return nil, err
	}
	if resolvedTarget == nil {
		return nil, nil
	}

	return &graph.Ref{
		DefRepo:     filepath.ToSlash(uriOrEmpty(resolvedTarget.ToRepoCloneURL)),
		DefPath:     filepath.ToSlash(pathOrDot(filepath.Join(gr.Def.Path...))),
		DefUnit:     resolvedTarget.ToUnit,
		DefUnitType: resolvedTarget.ToUnitType,
		Def:         gr.IsDef,
		File:        filepath.ToSlash(gr.File),
		Start:       gr.Span[0],
		End:         gr.Span[1],
	}, nil
}

func convertGoDoc(gd *gog.Doc, repoURI string) (*graph.Doc, error) {
	var key graph.DefKey
	if gd.DefKey != nil {
		resolvedTarget, err := ResolveDep(gd.PackageImportPath, repoURI)
		if err != nil {
			return nil, err
		}
		key = graph.DefKey{
			Path:     filepath.ToSlash(pathOrDot(filepath.Join(gd.Path...))),
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
		}
	}
	return &graph.Doc{
		DefKey: key,
		Format: gd.Format,
		Data:   gd.Data,
		File:   filepath.ToSlash(gd.File),
		Start:  gd.Span[0],
		End:    gd.Span[1],
	}, nil
}

func uriOrEmpty(cloneURL string) string {
	if cloneURL == "" {
		return ""
	}
	return graph.MakeURI(cloneURL)
}

func pathOrDot(path string) string {
	if path == "" {
		return "."
	}
	return path
}

func treePath(path string) string {
	if path == "" || path == "." {
		return string(".")
	}
	return "./" + path
}

// allowErrorsInGraph is whether the grapher should continue after
// encountering "reasonably common" errors (such as compile errors).
var allowErrorsInGraph = true

func doGraph(pkg *build.Package) (*gog.Output, error) {
	importPath := pkg.ImportPath
	importUnsafe := importPath == "unsafe"

	// Special-case: if this is a Cgo package, treat the CgoFiles as GoFiles or
	// else the character offsets will be junk.
	//
	// See https://codereview.appspot.com/86140043.
	loaderConfig.Build.CgoEnabled = false
	build.Default = *loaderConfig.Build
	if len(pkg.CgoFiles) > 0 {
		var allGoFiles []string
		allGoFiles = append(allGoFiles, pkg.GoFiles...)
		allGoFiles = append(allGoFiles, pkg.CgoFiles...)
		allGoFiles = append(allGoFiles, pkg.TestGoFiles...)
		for i, f := range allGoFiles {
			allGoFiles[i] = filepath.Join(cwd, pkg.Dir, f)
		}
		loaderConfig.CreateFromFilenames(pkg.ImportPath, allGoFiles...)
	} else {
		// Normal import
		loaderConfig.ImportWithTests(importPath)
	}

	if importUnsafe {
		// Special-case "unsafe" because go/loader does not let you load it
		// directly.
		if loaderConfig.ImportPkgs == nil {
			loaderConfig.ImportPkgs = make(map[string]bool)
		}
		loaderConfig.ImportPkgs["unsafe"] = true
	}

	prog, err := loaderConfig.Load()
	if err != nil {
		return nil, err
	}

	g := gog.New(prog)

	var pkgs []*loader.PackageInfo
	for _, pkg := range prog.Created {
		if strings.HasSuffix(pkg.Pkg.Name(), "_test") {
			// ignore xtest packages
			continue
		}
		pkgs = append(pkgs, pkg)
	}
	for _, pkg := range prog.Imported {
		pkgs = append(pkgs, pkg)
	}

	for _, pkg := range pkgs {
		if err := g.Graph(pkg); err != nil {
			return nil, err
		}
	}

	return &g.Output, nil
}
<|endoftext|>


<commit_before>/*
   Copyright 2009-2012 Phil Pennock

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package sks_spider

import (
	"strings"
)

import (
	btree "github.com/philpennock/sks-deps/btree"
	// gotgo
	// in-dir: gotgo -o btree.go btree.got string
	// top: go install github.com/philpennock/sks-deps/btree
)

// This is not memory efficient but for this few hosts, does not need to be

type HostGraph struct {
	maxLen   int
	aliases  AliasMap
	outbound map[string]btree.SortedSet
	inbound  map[string]btree.SortedSet
}

func btreeStringLess(a, b string) bool {
	return a < b
}

// This is horrid, would ideally create a second instantiation of btree not
// using strings.
func btreeHostLess(a, b string) bool {
	tmp := strings.Split(a, ".")
	ReverseStringSlice(tmp)
	revA := strings.Join(tmp, ".")
	tmp = strings.Split(b, ".")
	ReverseStringSlice(tmp)
	revB := strings.Join(tmp, ".")
	return revA < revB
}

func NewHostGraph(count int, aliasMap AliasMap) *HostGraph {
	outbound := make(map[string]btree.SortedSet, count)
	inbound := make(map[string]btree.SortedSet, count)
	return &HostGraph{maxLen: count, aliases: aliasMap, outbound: outbound, inbound: inbound}
}

func (hg *HostGraph) addHost(name string, info *SksNode) {
	if _, ok := hg.outbound[name]; !ok {
		hg.outbound[name] = btree.NewTree(btreeStringLess)
	}
	if _, ok := hg.inbound[name]; !ok {
		hg.inbound[name] = btree.NewTree(btreeStringLess)
	}
	for _, peerAsGiven := range info.GossipPeerList {
		var peerCanonical string
		if canon, ok := hg.aliases[strings.ToLower(peerAsGiven)]; ok {
			peerCanonical = canon
		} else {
			lowered := strings.ToLower(peerAsGiven)
			peerCanonical = lowered
			// peer is down, have no node, but still have outbound link:
			hg.aliases[lowered] = lowered
			if peerAsGiven != lowered {
				hg.aliases[peerAsGiven] = lowered
			}
		}
		hg.outbound[name].Insert(peerCanonical)
		if _, ok := hg.inbound[peerCanonical]; !ok {
			hg.inbound[peerCanonical] = btree.NewTree(btreeStringLess)
		}
		hg.inbound[peerCanonical].Insert(name)
	}
}

// inbounds can exist where there's no outbound because servers are down and we just have links to them
// I don't want to deal with nil's elsewhere
func (hg *HostGraph) fixOutbounds() {
	for k := range hg.inbound {
		for hn := range hg.inbound[k].Data() {
			if _, ok := hg.outbound[hn]; !ok {
				hg.outbound[hn] = btree.NewTree(btreeStringLess)
			}
		}
	}
}

func (hg *HostGraph) Outbound(name string) <-chan string {
	return hg.outbound[strings.ToLower(name)].Data()
}

func (hg *HostGraph) Inbound(name string) <-chan string {
	return hg.inbound[strings.ToLower(name)].Data()
}

func (hg *HostGraph) ExistsLink(from, to string) bool {
	realFrom, okFrom := hg.aliases[strings.ToLower(from)]
	realTo, okTo := hg.aliases[strings.ToLower(to)]
	if !okFrom || !okTo {
		Log.Printf("Bad link query, internal bug: %s %v -> %s %v", from, okFrom, to, okTo)
		return false
	}
	return hg.inbound[realTo].Contains(realFrom)
}

func (hg *HostGraph) AllPeersOf(name string) []string {
	canonName, ok := hg.aliases[strings.ToLower(name)]
	if !ok {
		return []string{}
	}
	allPeers := btree.NewTree(btreeHostLess)
	if _, ok := hg.outbound[canonName]; ok {
		for out := range hg.outbound[canonName].Data() {
			allPeers.Insert(out)
		}
	} else {
		Log.Printf("Warning: missing hostgraph outbound for %q", canonName)
	}
	if _, ok := hg.inbound[canonName]; ok {
		for in := range hg.inbound[canonName].Data() {
			allPeers.Insert(in)
		}
	} else {
		Log.Printf("Warning: missing hostgraph inbound for %q", canonName)
	}
	sortedList := make([]string, allPeers.Len())
	i := 0
	for peer := range allPeers.Data() {
		sortedList[i] = peer
		i++
	}
	return sortedList
}

func (hg *HostGraph) Len() int {
	l1 := len(hg.outbound)
	l2 := len(hg.inbound)
	if l1 >= l2 {
		return l1
	}
	return l2
}

func GenerateGraph(names []string, sksnodes HostMap, aliases AliasMap) *HostGraph {
	graph := NewHostGraph(len(names), aliases)
	for _, hn := range names {
		hnLower := strings.ToLower(hn)
		graph.addHost(hnLower, sksnodes[hn])
	}
	graph.fixOutbounds()
	return graph
}

func (hg *HostGraph) LabelMutualWithBase(name string) string {
	baseCanon, ok := hg.aliases[*flSpiderStartHost]
	if !ok {
		panic("no known alias for start host")
	}
	canon, ok := hg.aliases[name]
	switch {
	case !ok:
		// can't be mutual, we don't even know the name
		return "No"
	case canon == baseCanon:
		return "n/a"
	case hg.ExistsLink(canon, baseCanon) && hg.ExistsLink(baseCanon, canon):
		return "Yes"
	default:
		return "No"
	}
	panic("not reached")
}
<commit_msg>go version-dependent "unreachable code" fix.<commit_after>/*
   Copyright 2009-2012 Phil Pennock

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package sks_spider

import (
	"strings"
)

import (
	btree "github.com/philpennock/sks-deps/btree"
	// gotgo
	// in-dir: gotgo -o btree.go btree.got string
	// top: go install github.com/philpennock/sks-deps/btree
)

// This is not memory efficient but for this few hosts, does not need to be

type HostGraph struct {
	maxLen   int
	aliases  AliasMap
	outbound map[string]btree.SortedSet
	inbound  map[string]btree.SortedSet
}

func btreeStringLess(a, b string) bool {
	return a < b
}

// This is horrid, would ideally create a second instantiation of btree not
// using strings.
func btreeHostLess(a, b string) bool {
	tmp := strings.Split(a, ".")
	ReverseStringSlice(tmp)
	revA := strings.Join(tmp, ".")
	tmp = strings.Split(b, ".")
	ReverseStringSlice(tmp)
	revB := strings.Join(tmp, ".")
	return revA < revB
}

func NewHostGraph(count int, aliasMap AliasMap) *HostGraph {
	outbound := make(map[string]btree.SortedSet, count)
	inbound := make(map[string]btree.SortedSet, count)
	return &HostGraph{maxLen: count, aliases: aliasMap, outbound: outbound, inbound: inbound}
}

func (hg *HostGraph) addHost(name string, info *SksNode) {
	if _, ok := hg.outbound[name]; !ok {
		hg.outbound[name] = btree.NewTree(btreeStringLess)
	}
	if _, ok := hg.inbound[name]; !ok {
		hg.inbound[name] = btree.NewTree(btreeStringLess)
	}
	for _, peerAsGiven := range info.GossipPeerList {
		var peerCanonical string
		if canon, ok := hg.aliases[strings.ToLower(peerAsGiven)]; ok {
			peerCanonical = canon
		} else {
			lowered := strings.ToLower(peerAsGiven)
			peerCanonical = lowered
			// peer is down, have no node, but still have outbound link:
			hg.aliases[lowered] = lowered
			if peerAsGiven != lowered {
				hg.aliases[peerAsGiven] = lowered
			}
		}
		hg.outbound[name].Insert(peerCanonical)
		if _, ok := hg.inbound[peerCanonical]; !ok {
			hg.inbound[peerCanonical] = btree.NewTree(btreeStringLess)
		}
		hg.inbound[peerCanonical].Insert(name)
	}
}

// inbounds can exist where there's no outbound because servers are down and we just have links to them
// I don't want to deal with nil's elsewhere
func (hg *HostGraph) fixOutbounds() {
	for k := range hg.inbound {
		for hn := range hg.inbound[k].Data() {
			if _, ok := hg.outbound[hn]; !ok {
				hg.outbound[hn] = btree.NewTree(btreeStringLess)
			}
		}
	}
}

func (hg *HostGraph) Outbound(name string) <-chan string {
	return hg.outbound[strings.ToLower(name)].Data()
}

func (hg *HostGraph) Inbound(name string) <-chan string {
	return hg.inbound[strings.ToLower(name)].Data()
}

func (hg *HostGraph) ExistsLink(from, to string) bool {
	realFrom, okFrom := hg.aliases[strings.ToLower(from)]
	realTo, okTo := hg.aliases[strings.ToLower(to)]
	if !okFrom || !okTo {
		Log.Printf("Bad link query, internal bug: %s %v -> %s %v", from, okFrom, to, okTo)
		return false
	}
	return hg.inbound[realTo].Contains(realFrom)
}

func (hg *HostGraph) AllPeersOf(name string) []string {
	canonName, ok := hg.aliases[strings.ToLower(name)]
	if !ok {
		return []string{}
	}
	allPeers := btree.NewTree(btreeHostLess)
	if _, ok := hg.outbound[canonName]; ok {
		for out := range hg.outbound[canonName].Data() {
			allPeers.Insert(out)
		}
	} else {
		Log.Printf("Warning: missing hostgraph outbound for %q", canonName)
	}
	if _, ok := hg.inbound[canonName]; ok {
		for in := range hg.inbound[canonName].Data() {
			allPeers.Insert(in)
		}
	} else {
		Log.Printf("Warning: missing hostgraph inbound for %q", canonName)
	}
	sortedList := make([]string, allPeers.Len())
	i := 0
	for peer := range allPeers.Data() {
		sortedList[i] = peer
		i++
	}
	return sortedList
}

func (hg *HostGraph) Len() int {
	l1 := len(hg.outbound)
	l2 := len(hg.inbound)
	if l1 >= l2 {
		return l1
	}
	return l2
}

func GenerateGraph(names []string, sksnodes HostMap, aliases AliasMap) *HostGraph {
	graph := NewHostGraph(len(names), aliases)
	for _, hn := range names {
		hnLower := strings.ToLower(hn)
		graph.addHost(hnLower, sksnodes[hn])
	}
	graph.fixOutbounds()
	return graph
}

func (hg *HostGraph) LabelMutualWithBase(name string) string {
	baseCanon, ok := hg.aliases[*flSpiderStartHost]
	if !ok {
		panic("no known alias for start host")
	}
	canon, ok := hg.aliases[name]
	switch {
	case !ok:
		// can't be mutual, we don't even know the name
		return "No"
	case canon == baseCanon:
		return "n/a"
	case hg.ExistsLink(canon, baseCanon) && hg.ExistsLink(baseCanon, canon):
		return "Yes"
	default:
		return "No"
	}
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package graph

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"github.com/mitchellh/mapstructure"
)

const (
	//ACTIONCREATE : action create
	ACTIONCREATE = "create"
	// ACTIONUPDATE : action update
	ACTIONUPDATE = "update"
	// ACTIONDELETE : action delete
	ACTIONDELETE = "delete"
	// ACTIONFIND : action find
	ACTIONFIND = "find"
	// ACTIONGET : action get
	ACTIONGET = "get"
	// ACTIONNONE : action none
	ACTIONNONE = "none"
)

// Graph ...
type Graph struct {
	ID         string                 `json:"id"`
	Name       string                 `json:"name"`
	Action     string                 `json:"action"`
	Options    map[string]interface{} `json:"options"`
	Components []Component            `json:"components"`
	Changes    []Component            `json:"changes,omitempty"`
	Edges      []Edge                 `json:"edges,omitempty"`
}

// New returns a new graph
func New() *Graph {
	return &Graph{
		Components: make([]Component, 0),
		Edges:      make([]Edge, 0),
	}
}

// Component returns a component given the name matches
func (g *Graph) Component(component string) Component {
	for i, v := range g.Components {
		if v.GetID() == component {
			return g.Components[i]
		}
	}
	return nil
}

// ComponentAll returns a component from either changes or components given the name matches
func (g *Graph) ComponentAll(component string) Component {
	for i, v := range g.Changes {
		if v.GetID() == component {
			return g.Changes[i]
		}
	}
	for i, v := range g.Components {
		if v.GetID() == component {
			return g.Components[i]
		}
	}
	return nil
}

// HasComponent finds if the specified component exists
func (g *Graph) HasComponent(componentID string) bool {
	for _, v := range g.Components {
		if v.GetID() == componentID {
			return true
		}
	}
	return false
}

// AddComponent adds a component to the graphs vertices if it does not already exist
func (g *Graph) AddComponent(component Component) error {
	if g.HasComponent(component.GetID()) {
		return errors.New("Component already exists: " + component.GetID())
	}
	g.Components = append(g.Components, component)

	return nil
}

// UpdateComponent updates the graph
func (g *Graph) UpdateComponent(component Component) {
	for i := 0; i < len(g.Components); i++ {
		if g.Components[i].GetID() == component.GetID() {
			g.Components[i] = component
			return
		}
	}
}

// DeleteComponent deletes a component from the graph
func (g *Graph) DeleteComponent(component Component) {
	for i := len(g.Components) - 1; i >= 0; i-- {
		if g.Components[i].GetID() == component.GetID() {
			g.Components = append(g.Components[:i], g.Components[i+1:]...)
		}
	}
}

// DisconnectComponent removes a component from the graph. It will connect any neighbour/origin components together
func (g *Graph) DisconnectComponent(name string) error {
	origins := g.Origins(name)

	for i := len(g.Edges) - 1; i >= 0; i-- {
		// Remove any edges that connect to the disconnected component
		if g.Edges[i].Destination == name {
			g.Edges = append(g.Edges[:i], g.Edges[i+1:]...)
		}

		// Remove any neighbouring connections and reconnect them to origins
		if g.Edges[i].Source == name {
			for _, ov := range *origins {
				err := g.Connect(ov.GetID(), g.Edges[i].Destination)
				if err != nil {
					return err
				}
			}
			g.Edges = append(g.Edges[:i], g.Edges[i+1:]...)
		}
	}

	return nil
}

// connect is the internal method for connecting two verticies, it provides less checks than publicly exposed methods
func (g *Graph) connect(source, destination string) {
	if g.Connected(source, destination) != true {
		g.Edges = append(g.Edges, Edge{Source: source, Destination: destination, Length: 1})
	}
}

// Connect adds a dependency between two vertices
func (g *Graph) Connect(source, destination string) error {
	if !g.HasComponent(source) || !g.HasComponent(destination) {
		return errors.New("Could not connect Component, does not exist")
	}

	g.connect(source, destination)

	return nil
}

// ConnectMutually connects two vertices to eachother
func (g *Graph) ConnectMutually(source, destination string) error {
	err := g.Connect(source, destination)
	if err != nil {
		return err
	}
	return g.Connect(destination, source)
}

// ConnectSequential adds a dependency between two vertices. If the source has more than 1 neighbouring vertex, the destination vertex will be connected to that.
func (g *Graph) ConnectSequential(source, destination string) error {
	if !g.HasComponent(source) {
		source = "start"
	}

	if !g.HasComponent(destination) {
		return errors.New("Could not connect Component, does not exist")
	}

	c := g.Component(destination)
	gc := g.Neighbours(source).GetComponentGroup(c.GetGroup())

	// ensure that source does not get sent to itself (destination)
	for gc != nil {
		if destination == gc.GetID() {
			break
		}
		source = gc.GetID()
		gc = g.Neighbours(source).GetComponentGroup(c.GetGroup())
	}

	g.connect(source, destination)

	return nil
}

// Connected returns true if two components are connected
func (g *Graph) Connected(source, destination string) bool {
	for _, edge := range g.Edges {
		if edge.Source == source && edge.Destination == destination {
			return true
		}
	}

	return false
}

// GetComponents returns a component group that can be filtered
func (g *Graph) GetComponents() ComponentGroup {
	return g.Components
}

// GetChanges returns a component group that can be filtered
func (g *Graph) GetChanges() ComponentGroup {
	return g.Changes
}

// Neighbours returns all depencencies of a component
func (g *Graph) Neighbours(component string) *Neighbours {
	var n Neighbours

	for _, edge := range g.Edges {
		if edge.Source == component {
			n = append(n, g.Component(edge.Destination))
		}
	}

	return n.Unique()
}

// Origins returns all source components of a component
func (g *Graph) Origins(component string) *Neighbours {
	var n Neighbours

	for _, edge := range g.Edges {
		if edge.Destination == component {
			n = append(n, g.Component(edge.Source))
		}
	}

	return n.Unique()
}

// LengthBetween returns the length between two edges
func (g *Graph) LengthBetween(source, destination string) int {
	for _, e := range g.Edges {
		if e.Source == source && e.Destination == destination {
			return e.Length
		}
	}
	return -1
}

// Diff : diff two graphs, new, modified or removed components will be moved to Changes, and components will be
func (g *Graph) Diff(og *Graph) (*Graph, error) {
	// new temporary graph
	ng := New()

	for _, c := range g.Components {
		oc := og.Component(c.GetID())
		if oc != nil {
			if c.Diff(oc) {
				if c.GetAction() != ACTIONNONE {
					c.SetAction(ACTIONUPDATE)
				}
				c.SetState("waiting")
				ng.AddComponent(c)
			}
		} else {
			if c.GetAction() != ACTIONFIND && c.GetAction() != ACTIONNONE {
				c.SetAction(ACTIONCREATE)
			}
			c.SetState("waiting")
			ng.AddComponent(c)
		}
	}

	for _, oc := range og.Components {
		if oc.IsStateful() != true {
			continue
		}

		c := g.Component(oc.GetID())
		if c == nil {
			if oc.GetAction() != ACTIONNONE {
				oc.SetAction(ACTIONDELETE)
			}
			oc.SetState("waiting")
			ng.AddComponent(oc)
		}
	}

	// Move remove changed components with no action (action == none)
	unactionable := ng.transferUnactionable()

	// build the edges
	ng.SetDiffDependencies()

	// transfer old components
	ng.Changes = ng.Components
	ng.Components = og.Components

	// replace old unactionables with new unactionables
	ng.bulkReplace(unactionable)

	return ng, nil
}

// Graphviz outputs the graph in graphviz format
func (g *Graph) Graphviz() string {
	var output []string

	output = append(output, "digraph G {")

	for _, edge := range g.Edges {
		dest := g.ComponentAll(edge.Destination)
		if dest != nil {
			output = append(output, fmt.Sprintf("  \"%s\" -> \"%s\" [label=\"%s\"]", edge.Source, edge.Destination, dest.GetAction()))
		} else {
			output = append(output, fmt.Sprintf("  \"%s\" -> \"%s\"", edge.Source, edge.Destination))
		}
	}

	output = append(output, "}")

	return strings.Join(output, "\n")
}

// SetDiffDependencies rebuilds the graph's dependencies when diffing
func (g *Graph) SetDiffDependencies() {
	// This needs improvement

	g.Edges = make([]Edge, 0)

	for _, c := range g.Components {
		for _, dep := range c.Dependencies() {
			switch c.GetAction() {
			case ACTIONDELETE:
				if c.IsStateful() {
					g.Connect(c.GetID(), dep)
				}
			case ACTIONUPDATE:
				g.ConnectSequential(dep, c.GetID())
			case ACTIONCREATE, ACTIONFIND:
				g.Connect(dep, c.GetID())
			}
		}
	}

	g.SetStartFinish()
}

// SetStartFinish sets a start and finish point
func (g *Graph) SetStartFinish() {
	for _, c := range g.Components {
		o := g.Origins(c.GetID())
		n := g.Neighbours(c.GetID())

		if len(*o) < 1 {
			g.connect("start", c.GetID())
		}

		if len(*n) < 1 {
			g.connect(c.GetID(), "end")
		}
	}
}

// ToJSON serialises the graph as json
func (g *Graph) ToJSON() ([]byte, error) {
	return json.Marshal(g)
}

// Load loads a graph from json
func (g *Graph) Load(gg map[string]interface{}) error {

	components, ok := gg["components"].([]interface{})
	if ok {
		for i := 0; i < len(components); i++ {
			c := components[i].(map[string]interface{})
			components[i] = MapGenericComponent(c)
		}
	}

	changes, ok := gg["changes"].([]interface{})
	if ok {
		for i := 0; i < len(changes); i++ {
			c := changes[i].(map[string]interface{})
			changes[i] = MapGenericComponent(c)
		}
	}

	return mapstructure.Decode(gg, g)
}

func (g *Graph) transferUnactionable() []Component {
	var unactionable []Component

	for i := len(g.Components) - 1; i >= 0; i-- {
		if g.Components[i].GetAction() == "none" {
			unactionable = append(unactionable, g.Components[i])
			g.Components = append(g.Components[:i], g.Components[i+1:]...)
		}
	}

	return unactionable
}

func (g *Graph) bulkReplace(components []Component) {
	for _, c := range components {
		if g.Component(c.GetID()) != nil {
			g.DeleteComponent(c)
		}
		g.AddComponent(c)
	}
}

// func (g *Graph) DepthFirstSearch()

// func (g *Graph) CycleDetection()
<commit_msg>added username and user id to graph<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package graph

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"github.com/mitchellh/mapstructure"
)

const (
	//ACTIONCREATE : action create
	ACTIONCREATE = "create"
	// ACTIONUPDATE : action update
	ACTIONUPDATE = "update"
	// ACTIONDELETE : action delete
	ACTIONDELETE = "delete"
	// ACTIONFIND : action find
	ACTIONFIND = "find"
	// ACTIONGET : action get
	ACTIONGET = "get"
	// ACTIONNONE : action none
	ACTIONNONE = "none"
)

// Graph ...
type Graph struct {
	ID         string                 `json:"id"`
	Name       string                 `json:"name"`
	UserID     int                    `json:"user_id"`
	Username   string                 `json:"username"`
	Action     string                 `json:"action"`
	Options    map[string]interface{} `json:"options"`
	Components []Component            `json:"components"`
	Changes    []Component            `json:"changes,omitempty"`
	Edges      []Edge                 `json:"edges,omitempty"`
}

// New returns a new graph
func New() *Graph {
	return &Graph{
		Components: make([]Component, 0),
		Edges:      make([]Edge, 0),
	}
}

// Component returns a component given the name matches
func (g *Graph) Component(component string) Component {
	for i, v := range g.Components {
		if v.GetID() == component {
			return g.Components[i]
		}
	}
	return nil
}

// ComponentAll returns a component from either changes or components given the name matches
func (g *Graph) ComponentAll(component string) Component {
	for i, v := range g.Changes {
		if v.GetID() == component {
			return g.Changes[i]
		}
	}
	for i, v := range g.Components {
		if v.GetID() == component {
			return g.Components[i]
		}
	}
	return nil
}

// HasComponent finds if the specified component exists
func (g *Graph) HasComponent(componentID string) bool {
	for _, v := range g.Components {
		if v.GetID() == componentID {
			return true
		}
	}
	return false
}

// AddComponent adds a component to the graphs vertices if it does not already exist
func (g *Graph) AddComponent(component Component) error {
	if g.HasComponent(component.GetID()) {
		return errors.New("Component already exists: " + component.GetID())
	}
	g.Components = append(g.Components, component)

	return nil
}

// UpdateComponent updates the graph
func (g *Graph) UpdateComponent(component Component) {
	for i := 0; i < len(g.Components); i++ {
		if g.Components[i].GetID() == component.GetID() {
			g.Components[i] = component
			return
		}
	}
}

// DeleteComponent deletes a component from the graph
func (g *Graph) DeleteComponent(component Component) {
	for i := len(g.Components) - 1; i >= 0; i-- {
		if g.Components[i].GetID() == component.GetID() {
			g.Components = append(g.Components[:i], g.Components[i+1:]...)
		}
	}
}

// DisconnectComponent removes a component from the graph. It will connect any neighbour/origin components together
func (g *Graph) DisconnectComponent(name string) error {
	origins := g.Origins(name)

	for i := len(g.Edges) - 1; i >= 0; i-- {
		// Remove any edges that connect to the disconnected component
		if g.Edges[i].Destination == name {
			g.Edges = append(g.Edges[:i], g.Edges[i+1:]...)
		}

		// Remove any neighbouring connections and reconnect them to origins
		if g.Edges[i].Source == name {
			for _, ov := range *origins {
				err := g.Connect(ov.GetID(), g.Edges[i].Destination)
				if err != nil {
					return err
				}
			}
			g.Edges = append(g.Edges[:i], g.Edges[i+1:]...)
		}
	}

	return nil
}

// connect is the internal method for connecting two verticies, it provides less checks than publicly exposed methods
func (g *Graph) connect(source, destination string) {
	if g.Connected(source, destination) != true {
		g.Edges = append(g.Edges, Edge{Source: source, Destination: destination, Length: 1})
	}
}

// Connect adds a dependency between two vertices
func (g *Graph) Connect(source, destination string) error {
	if !g.HasComponent(source) || !g.HasComponent(destination) {
		return errors.New("Could not connect Component, does not exist")
	}

	g.connect(source, destination)

	return nil
}

// ConnectMutually connects two vertices to eachother
func (g *Graph) ConnectMutually(source, destination string) error {
	err := g.Connect(source, destination)
	if err != nil {
		return err
	}
	return g.Connect(destination, source)
}

// ConnectSequential adds a dependency between two vertices. If the source has more than 1 neighbouring vertex, the destination vertex will be connected to that.
func (g *Graph) ConnectSequential(source, destination string) error {
	if !g.HasComponent(source) {
		source = "start"
	}

	if !g.HasComponent(destination) {
		return errors.New("Could not connect Component, does not exist")
	}

	c := g.Component(destination)
	gc := g.Neighbours(source).GetComponentGroup(c.GetGroup())

	// ensure that source does not get sent to itself (destination)
	for gc != nil {
		if destination == gc.GetID() {
			break
		}
		source = gc.GetID()
		gc = g.Neighbours(source).GetComponentGroup(c.GetGroup())
	}

	g.connect(source, destination)

	return nil
}

// Connected returns true if two components are connected
func (g *Graph) Connected(source, destination string) bool {
	for _, edge := range g.Edges {
		if edge.Source == source && edge.Destination == destination {
			return true
		}
	}

	return false
}

// GetComponents returns a component group that can be filtered
func (g *Graph) GetComponents() ComponentGroup {
	return g.Components
}

// GetChanges returns a component group that can be filtered
func (g *Graph) GetChanges() ComponentGroup {
	return g.Changes
}

// Neighbours returns all depencencies of a component
func (g *Graph) Neighbours(component string) *Neighbours {
	var n Neighbours

	for _, edge := range g.Edges {
		if edge.Source == component {
			n = append(n, g.Component(edge.Destination))
		}
	}

	return n.Unique()
}

// Origins returns all source components of a component
func (g *Graph) Origins(component string) *Neighbours {
	var n Neighbours

	for _, edge := range g.Edges {
		if edge.Destination == component {
			n = append(n, g.Component(edge.Source))
		}
	}

	return n.Unique()
}

// LengthBetween returns the length between two edges
func (g *Graph) LengthBetween(source, destination string) int {
	for _, e := range g.Edges {
		if e.Source == source && e.Destination == destination {
			return e.Length
		}
	}
	return -1
}

// Diff : diff two graphs, new, modified or removed components will be moved to Changes, and components will be
func (g *Graph) Diff(og *Graph) (*Graph, error) {
	// new temporary graph
	ng := New()

	for _, c := range g.Components {
		oc := og.Component(c.GetID())
		if oc != nil {
			if c.Diff(oc) {
				if c.GetAction() != ACTIONNONE {
					c.SetAction(ACTIONUPDATE)
				}
				c.SetState("waiting")
				ng.AddComponent(c)
			}
		} else {
			if c.GetAction() != ACTIONFIND && c.GetAction() != ACTIONNONE {
				c.SetAction(ACTIONCREATE)
			}
			c.SetState("waiting")
			ng.AddComponent(c)
		}
	}

	for _, oc := range og.Components {
		if oc.IsStateful() != true {
			continue
		}

		c := g.Component(oc.GetID())
		if c == nil {
			if oc.GetAction() != ACTIONNONE {
				oc.SetAction(ACTIONDELETE)
			}
			oc.SetState("waiting")
			ng.AddComponent(oc)
		}
	}

	// Move remove changed components with no action (action == none)
	unactionable := ng.transferUnactionable()

	// build the edges
	ng.SetDiffDependencies()

	// transfer old components
	ng.Changes = ng.Components
	ng.Components = og.Components

	// replace old unactionables with new unactionables
	ng.bulkReplace(unactionable)

	return ng, nil
}

// Graphviz outputs the graph in graphviz format
func (g *Graph) Graphviz() string {
	var output []string

	output = append(output, "digraph G {")

	for _, edge := range g.Edges {
		dest := g.ComponentAll(edge.Destination)
		if dest != nil {
			output = append(output, fmt.Sprintf("  \"%s\" -> \"%s\" [label=\"%s\"]", edge.Source, edge.Destination, dest.GetAction()))
		} else {
			output = append(output, fmt.Sprintf("  \"%s\" -> \"%s\"", edge.Source, edge.Destination))
		}
	}

	output = append(output, "}")

	return strings.Join(output, "\n")
}

// SetDiffDependencies rebuilds the graph's dependencies when diffing
func (g *Graph) SetDiffDependencies() {
	// This needs improvement

	g.Edges = make([]Edge, 0)

	for _, c := range g.Components {
		for _, dep := range c.Dependencies() {
			switch c.GetAction() {
			case ACTIONDELETE:
				if c.IsStateful() {
					g.Connect(c.GetID(), dep)
				}
			case ACTIONUPDATE:
				g.ConnectSequential(dep, c.GetID())
			case ACTIONCREATE, ACTIONFIND:
				g.Connect(dep, c.GetID())
			}
		}
	}

	g.SetStartFinish()
}

// SetStartFinish sets a start and finish point
func (g *Graph) SetStartFinish() {
	for _, c := range g.Components {
		o := g.Origins(c.GetID())
		n := g.Neighbours(c.GetID())

		if len(*o) < 1 {
			g.connect("start", c.GetID())
		}

		if len(*n) < 1 {
			g.connect(c.GetID(), "end")
		}
	}
}

// ToJSON serialises the graph as json
func (g *Graph) ToJSON() ([]byte, error) {
	return json.Marshal(g)
}

// Load loads a graph from json
func (g *Graph) Load(gg map[string]interface{}) error {

	components, ok := gg["components"].([]interface{})
	if ok {
		for i := 0; i < len(components); i++ {
			c := components[i].(map[string]interface{})
			components[i] = MapGenericComponent(c)
		}
	}

	changes, ok := gg["changes"].([]interface{})
	if ok {
		for i := 0; i < len(changes); i++ {
			c := changes[i].(map[string]interface{})
			changes[i] = MapGenericComponent(c)
		}
	}

	return mapstructure.Decode(gg, g)
}

func (g *Graph) transferUnactionable() []Component {
	var unactionable []Component

	for i := len(g.Components) - 1; i >= 0; i-- {
		if g.Components[i].GetAction() == "none" {
			unactionable = append(unactionable, g.Components[i])
			g.Components = append(g.Components[:i], g.Components[i+1:]...)
		}
	}

	return unactionable
}

func (g *Graph) bulkReplace(components []Component) {
	for _, c := range components {
		if g.Component(c.GetID()) != nil {
			g.DeleteComponent(c)
		}
		g.AddComponent(c)
	}
}

// func (g *Graph) DepthFirstSearch()

// func (g *Graph) CycleDetection()
<|endoftext|>


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

package serial

import (
	"encoding/json"
	"errors"
	"io"
	"os"
)

// const

// Baud is the unit for the symbol rate. It describes the number of symbols transmitted per second.
type Baud uint32

const (
	// Baud4800 defines a transmission rate of 4800 symbols per second.
	Baud4800 = 4800
	// Baud9600 defines a transmission rate of 9600 symbols per second.
	Baud9600 = 9600
	// Baud19200 defines a transmission rate of 19200 symbols per second.
	Baud19200 = 19200
	// Baud38400 defines a transmission rate of 38400 symbols per second.
	Baud38400 = 38400
	// Baud57600 defines a transmission rate of 57600 symbols per second.
	Baud57600 = 57600
	// Baud115200 defines a transmission rate of 115200 symbols per second.
	Baud115200 = 115200
)

// DataBit is the number of bits representing a character.
type DataBit byte

const (
	// DataBit5 stands for a character length of five bits.
	DataBit5 = DataBit(iota + 5)
	// DataBit6 stands for a character length of six bits.
	DataBit6
	// DataBit7 stands for a character length of seven bits.
	DataBit7
	// DataBit8 stands for a character length of eight bits.
	DataBit8
)

// StopBit is the number of bits being send at the end of every character.
type StopBit byte

const (
	// StopBit1 represents a single bit being send as stopbit.
	StopBit1 = StopBit(iota + 1)
	// StopBit2 represents two bits being send as stopbit.
	StopBit2
)

// Parity is the method for detecting transmission errors.
type Parity byte

const (
	// ParityNone indicates that no error detection is being used.
	ParityNone = Parity(iota)
	// ParityEven indicates that a bit is added to even out the bit count.
	ParityEven
	// ParityOdd indicates that a bit is added to provide an odd bit count.
	ParityOdd
)

// TODO flow control

// var
var (
	errPort     = errors.New("serial configuration error: invalid port (unix: /dev/tty* , windows: COM*)")
	errBaud     = errors.New("serial configuration error: invalid baud rate (4800, 9600, 19200, 38400, 57600, 115200)")
	errDataBit  = errors.New("serial configuration error: invalid number of data bits (5, 6, 7, 8, 9)")
	errStopBit  = errors.New("serial configuration error: invalid number of stop bits (1, 2)")
	errParity   = errors.New("serial configuration error: invalid parity (0 - None, 1 - Even, 2 - Odd)")
	errConnOpen = errors.New("serial connection error: connection is not open")
)

// structs and its functions

// functions

// InitConnection provides a connection with the given parameters.
func InitConnection(port string, baudrate Baud, databit DataBit, stopbit StopBit, parity Parity) (*Connection, error) {
	return createConnection(port, baudrate, databit, stopbit, parity)
}

// LoadConnection provides a connection with the parameters being loaded from a json file.
func LoadConnection(path string) (*Connection, error) {
	var connection *Connection

	file, err := os.Open(path)
	if err != nil {
		return connection, err
	}

	dec := json.NewDecoder(file)
	for {

		if err := dec.Decode(&connection); err == io.EOF {
			break
		} else if err != nil {
			return connection, err
		}
	}

	return connection, connection.check()
}
<commit_msg>fixed build constraints<commit_after>// Copyright 2015 mvbjrn. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package serial

import (
	"encoding/json"
	"errors"
	"io"
	"os"
)

// const

// Baud is the unit for the symbol rate. It describes the number of symbols transmitted per second.
type Baud uint32

const (
	// Baud4800 defines a transmission rate of 4800 symbols per second.
	Baud4800 = 4800
	// Baud9600 defines a transmission rate of 9600 symbols per second.
	Baud9600 = 9600
	// Baud19200 defines a transmission rate of 19200 symbols per second.
	Baud19200 = 19200
	// Baud38400 defines a transmission rate of 38400 symbols per second.
	Baud38400 = 38400
	// Baud57600 defines a transmission rate of 57600 symbols per second.
	Baud57600 = 57600
	// Baud115200 defines a transmission rate of 115200 symbols per second.
	Baud115200 = 115200
)

// DataBit is the number of bits representing a character.
type DataBit byte

const (
	// DataBit5 stands for a character length of five bits.
	DataBit5 = DataBit(iota + 5)
	// DataBit6 stands for a character length of six bits.
	DataBit6
	// DataBit7 stands for a character length of seven bits.
	DataBit7
	// DataBit8 stands for a character length of eight bits.
	DataBit8
)

// StopBit is the number of bits being send at the end of every character.
type StopBit byte

const (
	// StopBit1 represents a single bit being send as stopbit.
	StopBit1 = StopBit(iota + 1)
	// StopBit2 represents two bits being send as stopbit.
	StopBit2
)

// Parity is the method for detecting transmission errors.
type Parity byte

const (
	// ParityNone indicates that no error detection is being used.
	ParityNone = Parity(iota)
	// ParityEven indicates that a bit is added to even out the bit count.
	ParityEven
	// ParityOdd indicates that a bit is added to provide an odd bit count.
	ParityOdd
)

// var
var (
	errPort     = errors.New("serial configuration error: invalid port (unix: /dev/tty* , windows: COM*)")
	errBaud     = errors.New("serial configuration error: invalid baud rate (4800, 9600, 19200, 38400, 57600, 115200)")
	errDataBit  = errors.New("serial configuration error: invalid number of data bits (5, 6, 7, 8, 9)")
	errStopBit  = errors.New("serial configuration error: invalid number of stop bits (1, 2)")
	errParity   = errors.New("serial configuration error: invalid parity (0 - None, 1 - Even, 2 - Odd)")
	errConnOpen = errors.New("serial connection error: connection is not open")
)

// structs and its functions

// functions

// InitConnection provides a connection with the given parameters.
func InitConnection(port string, baudrate Baud, databit DataBit, stopbit StopBit, parity Parity) (*Connection, error) {
	return createConnection(port, baudrate, databit, stopbit, parity)
}

// LoadConnection provides a connection with the parameters being loaded from a json file.
func LoadConnection(path string) (*Connection, error) {
	var connection *Connection

	file, err := os.Open(path)
	if err != nil {
		return connection, err
	}

	dec := json.NewDecoder(file)
	for {

		if err := dec.Decode(&connection); err == io.EOF {
			break
		} else if err != nil {
			return connection, err
		}
	}

	return connection, connection.check()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"io"
	"log"
	"os"
	"os/exec"
)

const (
	SERVFLAG = "-s"
	SERVCMD  = "go run $GOPATH/src/trago/server.go -s"
)

var (
	isServer bool
)

func main() {
	if isElem(os.Args, SERVFLAG) {
		isServer = true
		log.Println("running in server mode...")
	}

	if isServer {
		for {
			msg, err := bufio.NewReader(os.Stdin).ReadString('\n')
			if err == io.EOF {
				log.Println("got EOF, exiting...")
				break
			}

			log.Println("got message: " + msg)
		}
	} else {
		cmd := exec.Command("ssh", "localhost", SERVCMD)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		stdin, err := cmd.StdinPipe()
		if err != nil {
			log.Fatal(err)
		}

		if err := cmd.Start(); err != nil {
			log.Fatalf("error executing command: %v", err)
		}

		log.Println("waiting on input...")
		msg, _ := bufio.NewReader(os.Stdin).ReadBytes('\n')

		if _, err := stdin.Write(msg); err != nil {
			log.Fatal("error writing to pipe")
		}

		stdin.Close()			// looks like this sends EOF
	}
}

func isElem(haystack []string, needle string) bool {
	for _, elem := range haystack {
		if elem == needle {
			return true
		}
	}
	return false
}
<commit_msg>testing again<commit_after>package main

import (
	"bufio"
	"io"
	"log"
	"os"
	"os/exec"

	"github.com/ssbl/trago/db"
)

const (
	SERVFLAG = "-s"
	SERVCMD  = "go run $GOPATH/src/github.com/ssbl/trago/server.go -s"
)

var (
	isServer bool
)

func main() {
	if isElem(os.Args, SERVFLAG) {
		isServer = true
		log.Println("running in server mode...")
	}

	if isServer {
		for {
			msg, err := bufio.NewReader(os.Stdin).ReadString('\n')
			if err == io.EOF {
				log.Println("got EOF, exiting...")
				break
			} else if msg == "parse\n" {
				tradb, err := db.Parse()
				if err != nil {
					log.Fatal(err)
				}

				log.Print(tradb)
			} else {
				log.Print("got message: " + msg)
			}
		}
	} else {
		cmd := exec.Command("ssh", "localhost", SERVCMD)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		stdin, err := cmd.StdinPipe()
		if err != nil {
			log.Fatal(err)
		}

		if err := cmd.Start(); err != nil {
			log.Fatalf("error executing command: %v", err)
		}

		log.Println("waiting on input...")
		for {
			msg, err := bufio.NewReader(os.Stdin).ReadBytes('\n')
			if err == io.EOF {
				stdin.Close()
				break
			}

			if _, err := stdin.Write(msg); err != nil {
				log.Fatal("error writing to pipe")
			}
		}
	}
}

func isElem(haystack []string, needle string) bool {
	for _, elem := range haystack {
		if elem == needle {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"fmt"
	"io"
	"log"
	"net"
	"regexp"
	"strings"
)

var (
	nickRegexp    = regexp.MustCompile(`^[a-zA-Z\[\]_^{|}][a-zA-Z0-9\[\]_^{|}]*$`)
	channelRegexp = regexp.MustCompile(`^#[a-zA-Z0-9_\-]+$`)
)

func NewServer() *Server {
	return &Server{eventChan: make(chan Event),
		name:        "rosella",
		clientMap:   make(map[string]*Client),
		channelMap:  make(map[string]*Channel),
		operatorMap: make(map[string]string),
		motd:        "Welcome to IRC. Powered by Rosella."}
}

func (s *Server) Run() {
	for event := range s.eventChan {
		s.handleEvent(event)
	}
}

func (s *Server) HandleConnection(conn net.Conn) {
	client := &Client{server: s,
		connection: conn,
		outputChan: make(chan string),
		signalChan: make(chan signalCode, 3),
		channelMap: make(map[string]*Channel),
		connected:  true}

	go client.clientThread()
}

func (s *Server) handleEvent(e Event) {
	defer func(event Event) {
		err := recover()
		if err != nil {
			log.Printf("Recovered from error when handling event: %+v", event)
			log.Println(err)
		}
	}(e)

	switch e.event {
	case connected:
		//Client connected
		e.client.reply(rplMOTD, s.motd)
	case disconnected:
		//Client disconnected
	case command:
		//Client send a command
		fields := strings.Fields(e.input)
		if len(fields) < 1 {
			return
		}

		if strings.HasPrefix(fields[0], ":") {
			fields = fields[1:]
		}
		command := strings.ToUpper(fields[0])
		args := fields[1:]

		s.handleCommand(e.client, command, args)
	}
}

func (s *Server) handleCommand(client *Client, command string, args []string) {

	switch command {
	case "PING":
		client.reply(rplPong)
	case "INFO":
		client.reply(rplInfo, "Rosella IRCD github.com/eXeC64/Rosella")
	case "VERSION":
		client.reply(rplVersion, VERSION)
	case "NICK":
		if len(args) < 1 {
			client.reply(errNoNick)
			return
		}

		newNick := args[0]

		//Check newNick is of valid formatting (regex)
		if nickRegexp.MatchString(newNick) == false {
			client.reply(errInvalidNick, newNick)
			return
		}

		if _, exists := s.clientMap[strings.ToLower(newNick)]; exists {
			client.reply(errNickInUse, newNick)
			return
		}

		//Protect the server name from being used
		if strings.ToLower(newNick) == strings.ToLower(s.name) {
			client.reply(errNickInUse, newNick)
			return
		}

		client.setNick(newNick)

	case "USER":
		if client.nick == "" {
			client.reply(rplKill, "Your nickname is already being used", "")
			client.disconnect()
		} else {
			client.reply(rplWelcome)
			client.registered = true
		}

	case "JOIN":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 1 {
			client.reply(errMoreArgs)
			return
		}

		if args[0] == "0" {
			//Quit all channels
			for channel := range client.channelMap {
				client.partChannel(channel, "Disconnecting")
			}
			return
		}

		channels := strings.Split(args[0], ",")
		for _, channel := range channels {
			//Join the channel if it's valid
			if channelRegexp.MatchString(channel) {
				client.joinChannel(channel)
			}
		}

	case "PART":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 1 {
			client.reply(errMoreArgs)
			return
		}

		reason := strings.Join(args[1:], " ")

		channels := strings.Split(args[0], ",")
		for _, channel := range channels {
			//Part the channel if it's valid
			if channelRegexp.MatchString(channel) {
				client.partChannel(channel, reason)
			}
		}

	case "PRIVMSG":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 2 {
			client.reply(errMoreArgs)
			return
		}

		message := strings.Join(args[1:], " ")

		channel, chanExists := s.channelMap[strings.ToLower(args[0])]
		client2, clientExists := s.clientMap[strings.ToLower(args[0])]

		if chanExists {
			if channel.mode.noExternal {
				if _, inChannel := channel.clientMap[client.key]; !inChannel {
					//Not in channel, not allowed to send
					client.reply(errCannotSend, args[0])
					return
				}
			}
			if channel.mode.moderated {
				clientMode := channel.modeMap[client.key]
				if !clientMode.operator && !clientMode.voice {
					//It's moderated and we're not +v or +o, do nothing
					client.reply(errCannotSend, args[0])
					return
				}
			}
			for _, c := range channel.clientMap {
				if c != client {
					c.reply(rplMsg, client.nick, args[0], message)
				}
			}
		} else if clientExists {
			client.reply(rplMsg, client.nick, client2.nick, message)
		} else {
			client.reply(errNoSuchNick, args[0])
		}

	case "QUIT":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		client.disconnect()

	case "TOPIC":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 1 {
			client.reply(errMoreArgs)
			return
		}

		channel, exists := s.channelMap[strings.ToLower(args[0])]
		if exists == false {
			client.reply(errNoSuchNick, args[0])
			return
		}

		if len(args) == 1 {
			client.reply(rplTopic, channel.name, channel.topic)
			return
		}

		clientMode := channel.modeMap[client.key]
		if channel.mode.topicLocked && !clientMode.operator {
			client.reply(errNoPriv)
			return
		}

		if args[1] == ":" {
			channel.topic = ""
			for _, client := range channel.clientMap {
				client.reply(rplNoTopic, channel.name)
			}
		} else {
			topic := strings.Join(args[1:], " ")
			topic = strings.TrimPrefix(topic, ":")
			channel.topic = topic

			for _, client := range channel.clientMap {
				client.reply(rplTopic, channel.name, channel.topic)
			}
		}

	case "LIST":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) == 0 {
			chanList := make([]string, 0, len(s.channelMap))

			for channelName, channel := range s.channelMap {
				if channel.mode.secret {
					if _, inChannel := channel.clientMap[client.key]; !inChannel {
						//Not in the channel, skip
						continue
					}
				}
				listItem := fmt.Sprintf("%s %d :%s", channelName, len(channel.clientMap), channel.topic)
				chanList = append(chanList, listItem)
			}

			client.reply(rplList, chanList...)

		} else {
			channels := strings.Split(args[0], ",")
			chanList := make([]string, 0, len(channels))

			for _, channelName := range channels {
				if channel, exists := s.channelMap[strings.ToLower(channelName)]; exists {
					listItem := fmt.Sprintf("%s %d :%s", channelName, len(channel.clientMap), channel.topic)
					chanList = append(chanList, listItem)
				}
			}

			client.reply(rplList, chanList...)
		}
	case "OPER":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 2 {
			client.reply(errMoreArgs)
			return
		}

		username := args[0]
		password := args[1]

		if hashedPassword, exists := s.operatorMap[username]; exists {
			h := sha1.New()
			io.WriteString(h, password)
			pass := fmt.Sprintf("%x", h.Sum(nil))
			if hashedPassword == pass {
				client.operator = true
				client.reply(rplOper)
				return
			}
		}
		client.reply(errPassword)

	case "KILL":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if client.operator == false {
			client.reply(errNoPriv)
			return
		}

		if len(args) < 1 {
			client.reply(errMoreArgs)
			return
		}

		nick := args[0]

		reason := strings.Join(args[1:], " ")

		client, exists := s.clientMap[strings.ToLower(nick)]
		if !exists {
			client.reply(errNoSuchNick, nick)
			return
		}

		client.reply(rplKill, client.nick, reason)
		client.disconnect()

	case "KICK":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 2 {
			client.reply(errMoreArgs)
			return
		}

		channelKey := strings.ToLower(args[0])
		targetKey := strings.ToLower(args[1])

		channel, channelExists := s.channelMap[channelKey]
		if !channelExists {
			client.reply(errNoSuchNick, args[0])
			return
		}

		target, targetExists := channel.clientMap[targetKey]
		if !targetExists {
			client.reply(errNoSuchNick, args[1])
			return
		}

		clientMode := channel.modeMap[client.key]
		if !clientMode.operator && !client.operator {
			client.reply(errNoPriv)
			return
		}

		reason := strings.Join(args[2:], " ")

		//It worked
		for _, client := range channel.clientMap {
			client.reply(rplKick, client.nick, channel.name, target.nick, reason)
		}

		delete(channel.clientMap, targetKey)
		delete(channel.modeMap, targetKey)
		delete(target.channelMap, channelKey)

	case "MODE":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 1 {
			client.reply(errMoreArgs)
			return
		}

		channelKey := strings.ToLower(args[0])

		channel, channelExists := s.channelMap[channelKey]
		if !channelExists {
			client.reply(errNoSuchNick, args[0])
			return
		}
		mode := channel.mode

		if len(args) == 1 {
			//No more args, they just want the mode
			client.reply(rplChannelModeIs, args[0], mode.String(), "")
			return
		}

		if cm, ok := channel.modeMap[client.key]; !ok || !cm.operator {
			//Not a channel operator.

			//If they're not an irc operator either, they'll fail
			if !client.operator {
				client.reply(errNoPriv)
				return
			}
		}

		hasClient := false
		var oldClientMode, newClientMode *ClientMode
		var targetClient *Client
		if len(args) >= 3 {
			clientKey := strings.ToLower(args[2])
			oldClientMode, hasClient = channel.modeMap[clientKey]
			if hasClient {
				targetClient = channel.clientMap[clientKey]
				newClientMode = new(ClientMode)
				*newClientMode = *oldClientMode
			}
		}

		mod := strings.ToLower(args[1])
		if strings.HasPrefix(mod, "+") {
			for _, char := range mod {
				switch char {
				case 's':
					mode.secret = true
				case 't':
					mode.topicLocked = true
				case 'm':
					mode.moderated = true
				case 'n':
					mode.noExternal = true
				case 'o':
					if hasClient {
						newClientMode.operator = true
					}
				case 'v':
					if hasClient {
						newClientMode.voice = true
					}
				}
			}
		} else if strings.HasPrefix(mod, "-") {
			for _, char := range mod {
				switch char {
				case 's':
					mode.secret = false
				case 't':
					mode.topicLocked = false
				case 'm':
					mode.moderated = false
				case 'n':
					mode.noExternal = false
				case 'o':
					if hasClient {
						newClientMode.operator = false
					}
				case 'v':
					if hasClient {
						newClientMode.voice = false
					}
				}
			}
		}

		if hasClient {
			*oldClientMode = *newClientMode
		}
		channel.mode = mode

		for _, client := range channel.clientMap {
			if hasClient {
				client.reply(rplChannelModeIs, channel.name, args[1], targetClient.nick)
			} else {
				client.reply(rplChannelModeIs, channel.name, args[1], "")
			}
		}

	default:
		client.reply(errUnknownCommand, command)
	}
}
<commit_msg>Fixed private messaging between users<commit_after>package main

import (
	"crypto/sha1"
	"fmt"
	"io"
	"log"
	"net"
	"regexp"
	"strings"
)

var (
	nickRegexp    = regexp.MustCompile(`^[a-zA-Z\[\]_^{|}][a-zA-Z0-9\[\]_^{|}]*$`)
	channelRegexp = regexp.MustCompile(`^#[a-zA-Z0-9_\-]+$`)
)

func NewServer() *Server {
	return &Server{eventChan: make(chan Event),
		name:        "rosella",
		clientMap:   make(map[string]*Client),
		channelMap:  make(map[string]*Channel),
		operatorMap: make(map[string]string),
		motd:        "Welcome to IRC. Powered by Rosella."}
}

func (s *Server) Run() {
	for event := range s.eventChan {
		s.handleEvent(event)
	}
}

func (s *Server) HandleConnection(conn net.Conn) {
	client := &Client{server: s,
		connection: conn,
		outputChan: make(chan string),
		signalChan: make(chan signalCode, 3),
		channelMap: make(map[string]*Channel),
		connected:  true}

	go client.clientThread()
}

func (s *Server) handleEvent(e Event) {
	defer func(event Event) {
		err := recover()
		if err != nil {
			log.Printf("Recovered from error when handling event: %+v", event)
			log.Println(err)
		}
	}(e)

	switch e.event {
	case connected:
		//Client connected
		e.client.reply(rplMOTD, s.motd)
	case disconnected:
		//Client disconnected
	case command:
		//Client send a command
		fields := strings.Fields(e.input)
		if len(fields) < 1 {
			return
		}

		if strings.HasPrefix(fields[0], ":") {
			fields = fields[1:]
		}
		command := strings.ToUpper(fields[0])
		args := fields[1:]

		s.handleCommand(e.client, command, args)
	}
}

func (s *Server) handleCommand(client *Client, command string, args []string) {

	switch command {
	case "PING":
		client.reply(rplPong)
	case "INFO":
		client.reply(rplInfo, "Rosella IRCD github.com/eXeC64/Rosella")
	case "VERSION":
		client.reply(rplVersion, VERSION)
	case "NICK":
		if len(args) < 1 {
			client.reply(errNoNick)
			return
		}

		newNick := args[0]

		//Check newNick is of valid formatting (regex)
		if nickRegexp.MatchString(newNick) == false {
			client.reply(errInvalidNick, newNick)
			return
		}

		if _, exists := s.clientMap[strings.ToLower(newNick)]; exists {
			client.reply(errNickInUse, newNick)
			return
		}

		//Protect the server name from being used
		if strings.ToLower(newNick) == strings.ToLower(s.name) {
			client.reply(errNickInUse, newNick)
			return
		}

		client.setNick(newNick)

	case "USER":
		if client.nick == "" {
			client.reply(rplKill, "Your nickname is already being used", "")
			client.disconnect()
		} else {
			client.reply(rplWelcome)
			client.registered = true
		}

	case "JOIN":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 1 {
			client.reply(errMoreArgs)
			return
		}

		if args[0] == "0" {
			//Quit all channels
			for channel := range client.channelMap {
				client.partChannel(channel, "Disconnecting")
			}
			return
		}

		channels := strings.Split(args[0], ",")
		for _, channel := range channels {
			//Join the channel if it's valid
			if channelRegexp.MatchString(channel) {
				client.joinChannel(channel)
			}
		}

	case "PART":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 1 {
			client.reply(errMoreArgs)
			return
		}

		reason := strings.Join(args[1:], " ")

		channels := strings.Split(args[0], ",")
		for _, channel := range channels {
			//Part the channel if it's valid
			if channelRegexp.MatchString(channel) {
				client.partChannel(channel, reason)
			}
		}

	case "PRIVMSG":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 2 {
			client.reply(errMoreArgs)
			return
		}

		message := strings.Join(args[1:], " ")

		channel, chanExists := s.channelMap[strings.ToLower(args[0])]
		client2, clientExists := s.clientMap[strings.ToLower(args[0])]

		if chanExists {
			if channel.mode.noExternal {
				if _, inChannel := channel.clientMap[client.key]; !inChannel {
					//Not in channel, not allowed to send
					client.reply(errCannotSend, args[0])
					return
				}
			}
			if channel.mode.moderated {
				clientMode := channel.modeMap[client.key]
				if !clientMode.operator && !clientMode.voice {
					//It's moderated and we're not +v or +o, do nothing
					client.reply(errCannotSend, args[0])
					return
				}
			}
			for _, c := range channel.clientMap {
				if c != client {
					c.reply(rplMsg, client.nick, args[0], message)
				}
			}
		} else if clientExists {
			client2.reply(rplMsg, client.nick, client2.nick, message)
		} else {
			client.reply(errNoSuchNick, args[0])
		}

	case "QUIT":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		client.disconnect()

	case "TOPIC":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 1 {
			client.reply(errMoreArgs)
			return
		}

		channel, exists := s.channelMap[strings.ToLower(args[0])]
		if exists == false {
			client.reply(errNoSuchNick, args[0])
			return
		}

		if len(args) == 1 {
			client.reply(rplTopic, channel.name, channel.topic)
			return
		}

		clientMode := channel.modeMap[client.key]
		if channel.mode.topicLocked && !clientMode.operator {
			client.reply(errNoPriv)
			return
		}

		if args[1] == ":" {
			channel.topic = ""
			for _, client := range channel.clientMap {
				client.reply(rplNoTopic, channel.name)
			}
		} else {
			topic := strings.Join(args[1:], " ")
			topic = strings.TrimPrefix(topic, ":")
			channel.topic = topic

			for _, client := range channel.clientMap {
				client.reply(rplTopic, channel.name, channel.topic)
			}
		}

	case "LIST":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) == 0 {
			chanList := make([]string, 0, len(s.channelMap))

			for channelName, channel := range s.channelMap {
				if channel.mode.secret {
					if _, inChannel := channel.clientMap[client.key]; !inChannel {
						//Not in the channel, skip
						continue
					}
				}
				listItem := fmt.Sprintf("%s %d :%s", channelName, len(channel.clientMap), channel.topic)
				chanList = append(chanList, listItem)
			}

			client.reply(rplList, chanList...)

		} else {
			channels := strings.Split(args[0], ",")
			chanList := make([]string, 0, len(channels))

			for _, channelName := range channels {
				if channel, exists := s.channelMap[strings.ToLower(channelName)]; exists {
					listItem := fmt.Sprintf("%s %d :%s", channelName, len(channel.clientMap), channel.topic)
					chanList = append(chanList, listItem)
				}
			}

			client.reply(rplList, chanList...)
		}
	case "OPER":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 2 {
			client.reply(errMoreArgs)
			return
		}

		username := args[0]
		password := args[1]

		if hashedPassword, exists := s.operatorMap[username]; exists {
			h := sha1.New()
			io.WriteString(h, password)
			pass := fmt.Sprintf("%x", h.Sum(nil))
			if hashedPassword == pass {
				client.operator = true
				client.reply(rplOper)
				return
			}
		}
		client.reply(errPassword)

	case "KILL":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if client.operator == false {
			client.reply(errNoPriv)
			return
		}

		if len(args) < 1 {
			client.reply(errMoreArgs)
			return
		}

		nick := args[0]

		reason := strings.Join(args[1:], " ")

		client, exists := s.clientMap[strings.ToLower(nick)]
		if !exists {
			client.reply(errNoSuchNick, nick)
			return
		}

		client.reply(rplKill, client.nick, reason)
		client.disconnect()

	case "KICK":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 2 {
			client.reply(errMoreArgs)
			return
		}

		channelKey := strings.ToLower(args[0])
		targetKey := strings.ToLower(args[1])

		channel, channelExists := s.channelMap[channelKey]
		if !channelExists {
			client.reply(errNoSuchNick, args[0])
			return
		}

		target, targetExists := channel.clientMap[targetKey]
		if !targetExists {
			client.reply(errNoSuchNick, args[1])
			return
		}

		clientMode := channel.modeMap[client.key]
		if !clientMode.operator && !client.operator {
			client.reply(errNoPriv)
			return
		}

		reason := strings.Join(args[2:], " ")

		//It worked
		for _, client := range channel.clientMap {
			client.reply(rplKick, client.nick, channel.name, target.nick, reason)
		}

		delete(channel.clientMap, targetKey)
		delete(channel.modeMap, targetKey)
		delete(target.channelMap, channelKey)

	case "MODE":
		if client.registered == false {
			client.reply(errNotReg)
			return
		}

		if len(args) < 1 {
			client.reply(errMoreArgs)
			return
		}

		channelKey := strings.ToLower(args[0])

		channel, channelExists := s.channelMap[channelKey]
		if !channelExists {
			client.reply(errNoSuchNick, args[0])
			return
		}
		mode := channel.mode

		if len(args) == 1 {
			//No more args, they just want the mode
			client.reply(rplChannelModeIs, args[0], mode.String(), "")
			return
		}

		if cm, ok := channel.modeMap[client.key]; !ok || !cm.operator {
			//Not a channel operator.

			//If they're not an irc operator either, they'll fail
			if !client.operator {
				client.reply(errNoPriv)
				return
			}
		}

		hasClient := false
		var oldClientMode, newClientMode *ClientMode
		var targetClient *Client
		if len(args) >= 3 {
			clientKey := strings.ToLower(args[2])
			oldClientMode, hasClient = channel.modeMap[clientKey]
			if hasClient {
				targetClient = channel.clientMap[clientKey]
				newClientMode = new(ClientMode)
				*newClientMode = *oldClientMode
			}
		}

		mod := strings.ToLower(args[1])
		if strings.HasPrefix(mod, "+") {
			for _, char := range mod {
				switch char {
				case 's':
					mode.secret = true
				case 't':
					mode.topicLocked = true
				case 'm':
					mode.moderated = true
				case 'n':
					mode.noExternal = true
				case 'o':
					if hasClient {
						newClientMode.operator = true
					}
				case 'v':
					if hasClient {
						newClientMode.voice = true
					}
				}
			}
		} else if strings.HasPrefix(mod, "-") {
			for _, char := range mod {
				switch char {
				case 's':
					mode.secret = false
				case 't':
					mode.topicLocked = false
				case 'm':
					mode.moderated = false
				case 'n':
					mode.noExternal = false
				case 'o':
					if hasClient {
						newClientMode.operator = false
					}
				case 'v':
					if hasClient {
						newClientMode.voice = false
					}
				}
			}
		}

		if hasClient {
			*oldClientMode = *newClientMode
		}
		channel.mode = mode

		for _, client := range channel.clientMap {
			if hasClient {
				client.reply(rplChannelModeIs, channel.name, args[1], targetClient.nick)
			} else {
				client.reply(rplChannelModeIs, channel.name, args[1], "")
			}
		}

	default:
		client.reply(errUnknownCommand, command)
	}
}
<|endoftext|>