<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
)

// Repo represents a repository of information yaml files.
type Repo struct {
	Key      string
	Info     map[string]Info
	Control  map[string]Info
	Subrepos map[string]Repo
	root     string
	wg       sync.WaitGroup
}

func (r Repo) String() string {
	return fmt.Sprintf("R: %s (%d articles)", r.Key, len(r.Info))
}

// LoadRepos loads multiple repositories and stores them
func LoadRepos(p string) (repos map[string]Repo) {
	repos = make(map[string]Repo)
	p = getPath(p)
	wg := sync.WaitGroup{}

	files, _ := ioutil.ReadDir(p)
	for _, file := range files {
		fn := filepath.Join(p, file.Name())

		if _, err := os.Stat(filepath.Join(fn, "_repo.yaml")); os.IsNotExist(err) {
			log.Println(fmt.Sprintf("Skipping repo %s: no _repo.yaml found.", file.Name()))
			continue
		}

		wg.Add(1)
		go func() {
			defer wg.Done()
			repos[asKey(fn)] = NewRepo(fn)
		}()
	}

	wg.Wait()
	return
}

// UpdateRepos will run git pull on the repos
func UpdateRepos(repos map[string]Repo) {
	for key, repo := range repos {
		log.Printf("Updating %s...", key)
		repo.git("pull", "origin", "master")
	}
}

// AddRepo clones a new repository
func AddRepo(root, name, url string) {
	dir := filepath.Join(root, name)
	git("", "clone", url, dir)
	log.Print("Repository added!")
}

// NewRepo loads a repository on a path
func NewRepo(p string) (r Repo) {
	r = Repo{Key: asKey(p), root: getPath(p)}
	r.Info = make(map[string]Info)
	r.Control = make(map[string]Info)
	r.Subrepos = make(map[string]Repo)

	filepath.Walk(r.root, r.walk)
	r.wg.Wait()

	return
}

// ListRepos prints a sorted list of available repostiories.
func ListRepos(repos map[string]Repo) {
	keys := make([]string, 0, len(repos))
	for key := range repos {
		keys = append(keys, key)
	}

	sort.Strings(keys)
	for _, key := range keys {
		fmt.Println(key)
	}
}

// Keys returns a sorted list of the info keys in the repository
func (r *Repo) Keys() []string {
	keys := make([]string, 0, len(r.Info))
	for _, info := range r.Info {
		keys = append(keys, info.ID)
	}

	sort.Strings(keys)

	return keys
}

// Execute determine what to do:
//
// If the last argument in the command line from c.Args() given points to a
// repo, the index of the loop will be printed.
//
// If the last argument is an Info item, it will be executed.
func (r *Repo) Execute(c *cli.Context) {
	var repo Repo
	var info Info
	var ok bool
	repo = *r

	// The first argument is not needed since it was used to determine the
	// location to this very repo.
	args := c.Args()[1:]
	for _, arg := range args {
		// If we can find an info with the key provided, execute that right away!
		if info, ok = repo.Info[arg]; ok {
			info.Execute()
			return
		}

		// Otherwise, check if we have a subrepo matching the argument If we do,
		// `repo will be set to the new one, and the next iteration will check
		// deeper into the tree. If not, we need to break the loop.
		if repo, ok = repo.Subrepos[arg]; !ok {
			break
		}
	}

	for _, key := range repo.Keys() {
		fmt.Println(key)
	}
}

func (r *Repo) walk(path string, info os.FileInfo, err error) error {
	if err != nil {
		log.Println("walk error: ", err)
		return err
	}

	// Dotfile, like .git or whatever. Skip.
	if strings.HasPrefix(filepath.Base(path), ".") {
		return filepath.SkipDir
	}

	if info.IsDir() && r.isSubrepo(path) {
		r.wg.Add(1)
		go r.loadSubrepo(path)

		// Return SkipDir since the directory will be parsed by the
		// NewRepo call inside of loadSubrepo()
		return filepath.SkipDir

	} else if strings.HasSuffix(path, ".yaml") {
		r.wg.Add(1)
		go r.loadInfo(path)
	}

	return nil
}

func (r *Repo) loadInfo(path string) {
	defer r.wg.Done()

	info, err := LoadInfo(path)
	if err != nil {
		log.Println("Failed to load info: ", err)
	}

	// Control files start with an underscore and should not be stored as
	// normal Info documents.
	if strings.HasPrefix(asKey(path), "_") {
		r.Control[info.ID] = info
	} else {
		r.Info[info.ID] = info
	}
}

func (r *Repo) loadSubrepo(path string) {
	defer r.wg.Done()
	nr := NewRepo(path)
	r.Subrepos[nr.Key] = nr
}

func (r *Repo) isSubrepo(path string) bool {
	// This is the root...
	if r.root == path {
		return false
	}

	return true
}

// Helper to run git commands inside of a repository
func (r *Repo) git(args ...string) {
	git(r.root, args...)
}
<commit_msg>Add printing of subrepositories when listing a repo<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/fatih/color"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
)

// Repo represents a repository of information yaml files.
type Repo struct {
	Key      string
	Info     map[string]Info
	Control  map[string]Info
	Subrepos map[string]Repo
	root     string
	wg       sync.WaitGroup
}

func (r Repo) String() string {
	return fmt.Sprintf("R: %s (%d articles)", r.Key, len(r.Info))
}

// LoadRepos loads multiple repositories and stores them
func LoadRepos(p string) (repos map[string]Repo) {
	repos = make(map[string]Repo)
	p = getPath(p)
	wg := sync.WaitGroup{}

	files, _ := ioutil.ReadDir(p)
	for _, file := range files {
		fn := filepath.Join(p, file.Name())

		if _, err := os.Stat(filepath.Join(fn, "_repo.yaml")); os.IsNotExist(err) {
			log.Println(fmt.Sprintf("Skipping repo %s: no _repo.yaml found.", file.Name()))
			continue
		}

		wg.Add(1)
		go func() {
			defer wg.Done()
			repos[asKey(fn)] = NewRepo(fn)
		}()
	}

	wg.Wait()
	return
}

// UpdateRepos will run git pull on the repos
func UpdateRepos(repos map[string]Repo) {
	for key, repo := range repos {
		log.Printf("Updating %s...", key)
		repo.git("pull", "origin", "master")
	}
}

// AddRepo clones a new repository
func AddRepo(root, name, url string) {
	dir := filepath.Join(root, name)
	git("", "clone", url, dir)
	log.Print("Repository added!")
}

// NewRepo loads a repository on a path
func NewRepo(p string) (r Repo) {
	r = Repo{Key: asKey(p), root: getPath(p)}
	r.Info = make(map[string]Info)
	r.Control = make(map[string]Info)
	r.Subrepos = make(map[string]Repo)

	filepath.Walk(r.root, r.walk)
	r.wg.Wait()

	return
}

// ListRepos prints a sorted list of available repostiories.
func ListRepos(repos map[string]Repo) {
	keys := make([]string, 0, len(repos))
	for key := range repos {
		keys = append(keys, key)
	}

	sort.Strings(keys)
	for _, key := range keys {
		fmt.Println(key)
	}
}

// Keys returns a sorted list of the info keys in the repository
func (r *Repo) Keys() []string {
	keys := make([]string, 0, len(r.Info))
	for _, info := range r.Info {
		keys = append(keys, info.ID)
	}

	sort.Strings(keys)

	return keys
}

// SubrepoKeys returns a sorted list of the subrepo keys in the repository
func (r *Repo) SubrepoKeys() []string {
	keys := make([]string, 0, len(r.Subrepos))
	for _, sub := range r.Subrepos {
		keys = append(keys, sub.Key)
	}

	sort.Strings(keys)

	return keys
}

// Execute determine what to do:
//
// If the last argument in the command line from c.Args() given points to a
// repo, the index of the loop will be printed.
//
// If the last argument is an Info item, it will be executed.
func (r *Repo) Execute(c *cli.Context) {
	var repo Repo
	var info Info
	var ok bool
	repo = *r

	// The first argument is not needed since it was used to determine the
	// location to this very repo.
	args := c.Args()[1:]
	for _, arg := range args {
		// If we can find an info with the key provided, execute that right away!
		if info, ok = repo.Info[arg]; ok {
			info.Execute()
			return
		}

		// Otherwise, check if we have a subrepo matching the argument If we do,
		// `repo will be set to the new one, and the next iteration will check
		// deeper into the tree. If not, we need to break the loop.
		if repo, ok = repo.Subrepos[arg]; !ok {
			break
		}
	}

	// Nothing matched, or the end node is a repo. Print the contents,
	// beginning with a blue listing of subrepos
	blue := color.New(color.FgBlue, color.Bold).SprintfFunc()
	for _, key := range repo.SubrepoKeys() {
		fmt.Println(blue(key))
	}
	for _, key := range repo.Keys() {
		fmt.Println(key)
	}
}

func (r *Repo) walk(path string, info os.FileInfo, err error) error {
	if err != nil {
		log.Println("walk error: ", err)
		return err
	}

	// Dotfile, like .git or whatever. Skip.
	if strings.HasPrefix(filepath.Base(path), ".") {
		return filepath.SkipDir
	}

	if info.IsDir() && r.isSubrepo(path) {
		r.wg.Add(1)
		go r.loadSubrepo(path)

		// Return SkipDir since the directory will be parsed by the
		// NewRepo call inside of loadSubrepo()
		return filepath.SkipDir

	} else if strings.HasSuffix(path, ".yaml") {
		r.wg.Add(1)
		go r.loadInfo(path)
	}

	return nil
}

func (r *Repo) loadInfo(path string) {
	defer r.wg.Done()

	info, err := LoadInfo(path)
	if err != nil {
		log.Println("Failed to load info: ", err)
	}

	// Control files start with an underscore and should not be stored as
	// normal Info documents.
	if strings.HasPrefix(asKey(path), "_") {
		r.Control[info.ID] = info
	} else {
		r.Info[info.ID] = info
	}
}

func (r *Repo) loadSubrepo(path string) {
	defer r.wg.Done()
	nr := NewRepo(path)
	r.Subrepos[nr.Key] = nr
}

func (r *Repo) isSubrepo(path string) bool {
	// This is the root...
	if r.root == path {
		return false
	}

	return true
}

// Helper to run git commands inside of a repository
func (r *Repo) git(args ...string) {
	git(r.root, args...)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go/build"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"github.com/Masterminds/vcs"
	"gopkg.in/yaml.v2"
)

var repos = make(map[string]*Repo)

// Version compatibility string e.g. "~1.0.0" or "1.*"
type Version string

// NewRepo creates and initializes a Repo
func NewRepo(name string, parent *Repo) *Repo {
	if r, ok := repos[name]; ok {
		return r
	}
	r := &Repo{
		parent:       parent,
		Name:         name,
		manifestFile: "vgo.yaml",
	}
	repos[name] = r
	r.Install()
	return r
}

// Repo ...
type Repo struct {
	sync.RWMutex `yaml:"-"`

	meta         *build.Package `yaml:"-"`
	repo         vcs.Repo       `yaml:"-"`
	parent       *Repo          `yaml:"-"`
	hasManifest  bool           `yaml:"-"`
	manifestFile string         `yaml:"-"`
	installed    bool           `yaml:"-"`

	Name         string  `yaml:"name,omitempty"`
	Version      Version `yaml:"ver,omitempty"`
	Reference    string  `yaml:"ref,omitempty"`
	Dependencies []*Repo `yaml:"deps,omitempty"`
	URL          string  `yaml:"url,omitempty"`
	// UsedPkgs     Pkgs    `yaml:"-"`
}

// HasDep checks for the existence of a dependancy
func (r *Repo) HasDep(dep *Repo) bool {
	for _, d := range r.Dependencies {
		if d == dep {
			return true
		}
	}
	return false
}

// AddDep ...
func (r *Repo) AddDep(dep *Repo) {
	if r.HasDep(dep) {
		return
	}
	r.Dependencies = append(r.Dependencies, dep)
}

// Path calculates the install path for the repository
func (r *Repo) Path() string {
	if r.parent == nil {
		return cwd
	}
	return path.Join(r.parent.Path(), "vendor", r.Name)
}

// FQN resolves the fully qualified package name. This is the equivalent to the name that go uses dependant on it's context.
func (r *Repo) FQN() string {
	if r.IsInGoPath() && !r.IsRoot() {
		return filepath.Join(r.Root().FQN(), "vendor", r.Name)
	}
	if r.Name == "" {
		return "."
	}
	return r.Name
}

// Root returns the topmost package (typically this is the application package)
func (r *Repo) Root() *Repo {
	if r.parent == nil {
		return r
	}
	return r.parent.Root()
}

// IsRoot returns whether the pkg is the root pkg
func (r *Repo) IsRoot() bool {
	return r.parent == nil
}

// IsInGoPath returns whether project and all vendored packages are contained in the $GOPATH
func (r *Repo) IsInGoPath() bool {
	if r.parent != nil {
		return r.parent.IsInGoPath()
	}
	return strings.HasPrefix(r.Path(), gosrcpath)
}

// LoadManifest ...
func (r *Repo) LoadManifest() error {
	r.hasManifest = false
	data, err := ioutil.ReadFile(filepath.Join(r.Path(), r.manifestFile))
	if err != nil {
		return err
	}
	r.Lock()
	err = yaml.Unmarshal(data, r)
	r.Unlock()
	if err != nil {
		return err
	}
	r.hasManifest = true
	r.updateDepsParents()
	return nil
}

// updateDepsParents resolves the parent (caller) pkg for all dependencies recursively
func (r *Repo) updateDepsParents() {
	for _, d := range r.Dependencies {
		d.Lock()
		d.parent = r
		d.Unlock()
		d.updateDepsParents()
	}
}

// Find looks for a package in it's dependencies or parents dependencies recursively
func (r *Repo) Find(name string) *Repo {
	for _, d := range r.Dependencies {
		if (*d).Name == name {
			return d
		}
	}
	if r.parent != nil {
		return (*r.parent).Find(name)
	}
	return nil
}

// SaveManifest ...
func (r *Repo) SaveManifest() error {
	data, err := yaml.Marshal(r)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(r.Path(), r.manifestFile), data, os.FileMode(0644))
	if err != nil {
		return err
	}
	return nil
}

// Install the package
func (r *Repo) Install() error {
	if r.parent == nil {
		// don't touch the current working directory
		return nil
	}
	repo, err := r.VCS()
	if repo == nil {
		return fmt.Errorf("Could not resolve repo for %s with error %s", r.Name, err)
	}
	r.installed = repo.CheckLocal()
	if !r.installed {
		Logf("Installing %s", r.Name)
		err = repo.Get()
		if err != nil {
			Logf("Failed to install %s with error %s, %s", r.Name, err.Error(), r.Path())
		}
	}
	r.Checkout(false)
	return err
}

// InstallDeps install package dependencies
func (r *Repo) InstallDeps() (err error) {
	wg := sync.WaitGroup{}
	for _, dep := range r.Dependencies {
		d := dep
		wg.Add(1)
		// go func() {
		err = d.Install()
		if err != nil {
			Logf("Package %s could not be installed with error", err.Error())
		}
		wg.Done()
		// }()
	}
	wg.Wait()
	return
}

// RelPath returns the path to package relative to the root package
func (r *Repo) RelPath() string {
	return strings.TrimPrefix(cwd, r.Path())
}

// Checkout switches the package version to the commit nearest maching the Compat string
func (r *Repo) Checkout(update bool) error {
	if r.parent == nil {
		// don't touch the current working directory
		return nil
	}
	repo, err := r.VCS()
	if err != nil {
		return err
	}
	if repo.IsDirty() {
		Logf("Skipping checkout for %s. Dependency is dirty.", r.Name)
	}
	r.Lock()
	defer r.Unlock()
	version := r.Version
	if r.Reference != "" {
		version = Version(r.Reference)
	}
	r.installed = repo.CheckLocal()
	if !r.installed {
		Logf("Dependency %s not installed", r.Name)
		return fmt.Errorf("Dependency %s not installed", r.Name)
	}
	v := string(version)
	if len(v) > 0 && !repo.IsReference(v) {
		Logf("Version %s not found for dependency %s", v, r.Name)
		return fmt.Errorf("Version %s not found for dependency %s", v, r.Name)
	}
	err = repo.UpdateVersion(v)
	if err != nil {
		Logf("Checkout failed with error %s", err.Error())
		return err
	}
	if update {
		err = repo.Update()
		if err != nil {
			Logf("Checkout failed with error %s", err.Error())
			return err
		}
	}
	r.Reference, err = repo.Version()
	Logf("OK %s %s", r.Reference, r.Name)
	r.LoadManifest()
	r.InstallDeps()
	return err
}

// VCS resolves the vcs.Repo for the Repo
func (r *Repo) VCS() (repo vcs.Repo, err error) {
	r.Lock()
	defer r.Unlock()
	if r.repo != nil {
		repo = r.repo
		return
	}
	repoType := r.RepoType()
	repoURL := r.RepoURL()
	repoPath := r.Path()
	switch repoType {
	case vcs.Git:
		repo, err = vcs.NewGitRepo(repoURL, repoPath)
	case vcs.Bzr:
		repo, err = vcs.NewBzrRepo(repoURL, repoPath)
	case vcs.Hg:
		repo, err = vcs.NewHgRepo(repoURL, repoPath)
	case vcs.Svn:
		repo, err = vcs.NewSvnRepo(repoURL, repoPath)
	}
	r.repo = repo
	return
}

// PathOptions returns a list of possible locations to find the repo
func (r *Repo) PathOptions() []string {
	pathOptions := []string{r.Path()}
	pathOptions = append(pathOptions, filepath.Join(r.Path(), "vendor", r.Name))
	parent := r.parent
	// Climb tree to find possible parent vendor dirs
	for parent != nil {
		pathOptions = append(pathOptions, filepath.Join(parent.Path(), "vendor", r.Name))
		parent = parent.parent
	}
	pathOptions = append(pathOptions, filepath.Join(gopath, "src", r.Name))
	return pathOptions
}

// RepoURL creates the repo url from the package import path
func (r *Repo) RepoURL() string {
	if r.URL != "" {
		return r.URL
	}
	// If it's already installed in vendor or gopath, grab the url from there
	repo := repoFromPath(r.PathOptions()...)
	if repo != nil {
		return repo.Remote()
	}
	// Fallback to resolving the path from the package import path
	// Add more cases as needed/requested
	parts := strings.Split(r.Name, "/")
	switch parts[0] {
	case "github.com":
		return fmt.Sprintf("git@github.com:%s.git", strings.Join(parts[1:3], "/"))
	case "golang.org":
		return fmt.Sprintf("git@github.com:golang/%s.git", parts[2])
	case "gopkg.in":
		nameParts := strings.Split(parts[2], ".")
		name := strings.Join(nameParts[:len(nameParts)-1], ".")
		r.Version = Version(nameParts[len(nameParts)-1])
		return fmt.Sprintf("git@github.com:%s/%s.git", parts[1], name)
	}
	return ""
}

// RepoType attempts to resolve the repository type of the package by it's name
func (r *Repo) RepoType() vcs.Type {
	// If it's already installed in vendor or gopath, grab the type from there
	repo := repoFromPath(r.PathOptions()...)
	if repo != nil {
		return repo.Vcs()
	}
	// Fallback to resolving the type from the package import path
	// Add more cases as needed/requested
	parts := strings.Split(r.Name, "/")
	switch parts[0] {
	case "github.com":
		return vcs.Git
	case "golang.org":
		return vcs.Git
	case "gopkg.in":
		return vcs.Git
	}
	return vcs.NoVCS
}

// MarshalYAML implements yaml.Marsheler to prevent duplicate storage of nested packages with vgo.yaml
func (r *Repo) MarshalYAML() (interface{}, error) {
	r.RLock()
	copy := *r
	r.RUnlock()
	if copy.Name == "." {
		copy.Name = ""
	}
	if copy.hasManifest && copy.parent != nil {
		copy.Dependencies = []*Repo{}
	}
	return copy, nil
}

// Print ...
func (r *Repo) Print(indent string, w io.Writer) {
	r.print(indent, 0, w)
}

func (r *Repo) print(indent string, depth int, w io.Writer) {
	w.Write([]byte(strings.Repeat(indent, depth) + r.Name + "\n"))
	for _, d := range r.Dependencies {
		d.print(indent, depth+1, w)
	}
}
<commit_msg>Fixes for repo installation path and loading of manifest<commit_after>package main

import (
	"fmt"
	"go/build"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"github.com/Masterminds/vcs"
	"gopkg.in/yaml.v2"
)

var repos = make(map[string]*Repo)

// Version compatibility string e.g. "~1.0.0" or "1.*"
type Version string

// NewRepo creates and initializes a Repo
func NewRepo(name string, parent *Repo) *Repo {
	if r, ok := repos[name]; ok {
		return r
	}
	r := &Repo{
		parent:       parent,
		Name:         name,
		manifestFile: "vgo.yaml",
	}
	repos[name] = r
	r.Install()
	return r
}

// Repo ...
type Repo struct {
	sync.RWMutex `yaml:"-"`

	meta         *build.Package `yaml:"-"`
	repo         vcs.Repo       `yaml:"-"`
	parent       *Repo          `yaml:"-"`
	hasManifest  bool           `yaml:"-"`
	manifestFile string         `yaml:"-"`
	installed    bool           `yaml:"-"`

	Name         string  `yaml:"name,omitempty"`
	Version      Version `yaml:"ver,omitempty"`
	Reference    string  `yaml:"ref,omitempty"`
	Dependencies []*Repo `yaml:"deps,omitempty"`
	URL          string  `yaml:"url,omitempty"`
	// UsedPkgs     Pkgs    `yaml:"-"`
}

// HasDep checks for the existence of a dependancy
func (r *Repo) HasDep(dep *Repo) bool {
	for _, d := range r.Dependencies {
		if d == dep {
			return true
		}
	}
	return false
}

// AddDep ...
func (r *Repo) AddDep(dep *Repo) {
	if r.HasDep(dep) {
		return
	}
	r.Dependencies = append(r.Dependencies, dep)
}

// Path calculates the install path for the repository
func (r *Repo) Path() string {
	if r.parent == nil {
		return cwd
	}
	return path.Join(cwd, "vendor", r.Name)
	// return path.Join(r.parent.Path(), "vendor", r.Name)
}

// FQN resolves the fully qualified package name. This is the equivalent to the name that go uses dependant on it's context.
func (r *Repo) FQN() string {
	if r.IsInGoPath() && !r.IsRoot() {
		return filepath.Join(r.Root().FQN(), "vendor", r.Name)
	}
	if r.Name == "" {
		return "."
	}
	return r.Name
}

// Root returns the topmost package (typically this is the application package)
func (r *Repo) Root() *Repo {
	if r.parent == nil {
		return r
	}
	return r.parent.Root()
}

// IsRoot returns whether the pkg is the root pkg
func (r *Repo) IsRoot() bool {
	return r.parent == nil
}

// IsInGoPath returns whether project and all vendored packages are contained in the $GOPATH
func (r *Repo) IsInGoPath() bool {
	if r.parent != nil {
		return r.parent.IsInGoPath()
	}
	return strings.HasPrefix(r.Path(), gosrcpath)
}

// LoadManifest ...
func (r *Repo) LoadManifest() error {
	r.hasManifest = false
	data, err := ioutil.ReadFile(filepath.Join(r.Path(), r.manifestFile))
	if err != nil {
		return err
	}
	r.Lock()
	err = yaml.Unmarshal(data, r)
	r.Unlock()
	if err != nil {
		return err
	}
	r.hasManifest = true
	r.updateDepsParents()
	r.updateMap()
	return nil
}

// updateDepsParents resolves the parent (caller) pkg for all dependencies recursively
func (r *Repo) updateDepsParents() {
	for _, d := range r.Dependencies {
		d.Lock()
		d.parent = r
		d.Unlock()
		d.updateDepsParents()
	}
}

// updateMap ...
func (r *Repo) updateMap() {
	if _, ok := repos[r.Name]; !ok {
		repos[r.Name] = r
	}
	for _, d := range r.Dependencies {
		d.updateMap()
	}
}

// Find looks for a package in it's dependencies or parents dependencies recursively
func (r *Repo) Find(name string) *Repo {
	for _, d := range r.Dependencies {
		if (*d).Name == name {
			return d
		}
	}
	if r.parent != nil {
		return (*r.parent).Find(name)
	}
	return nil
}

// SaveManifest ...
func (r *Repo) SaveManifest() error {
	data, err := yaml.Marshal(r)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(r.Path(), r.manifestFile), data, os.FileMode(0644))
	if err != nil {
		return err
	}
	return nil
}

// Install the package
func (r *Repo) Install() error {
	if r.parent == nil {
		// don't touch the current working directory
		return nil
	}
	repo, err := r.VCS()
	if repo == nil {
		return fmt.Errorf("Could not resolve repo for %s with error %s", r.Name, err)
	}
	r.installed = repo.CheckLocal()
	if !r.installed {
		Logf("Installing %s", r.Name)
		err = repo.Get()
		if err != nil {
			Logf("Failed to install %s with error %s, %s", r.Name, err.Error(), r.Path())
		}
	}
	r.Checkout(false)
	return err
}

// InstallDeps install package dependencies
func (r *Repo) InstallDeps() (err error) {
	wg := sync.WaitGroup{}
	for _, dep := range r.Dependencies {
		d := dep
		wg.Add(1)
		// go func() {
		err = d.Install()
		if err != nil {
			Logf("Package %s could not be installed with error", err.Error())
		}
		wg.Done()
		// }()
	}
	wg.Wait()
	return
}

// RelPath returns the path to package relative to the root package
func (r *Repo) RelPath() string {
	return strings.TrimPrefix(cwd, r.Path())
}

// Checkout switches the package version to the commit nearest maching the Compat string
func (r *Repo) Checkout(update bool) error {
	if r.parent == nil {
		// don't touch the current working directory
		return nil
	}
	repo, err := r.VCS()
	if err != nil {
		return err
	}
	if repo.IsDirty() {
		Logf("Skipping checkout for %s. Dependency is dirty.", r.Name)
	}
	r.Lock()
	defer r.Unlock()
	version := r.Version
	if r.Reference != "" {
		version = Version(r.Reference)
	}
	r.installed = repo.CheckLocal()
	if !r.installed {
		Logf("Dependency %s not installed", r.Name)
		return fmt.Errorf("Dependency %s not installed", r.Name)
	}
	v := string(version)
	if len(v) > 0 {
		if repo.IsReference(v) {
			err = repo.UpdateVersion(v)
			if err != nil {
				Logf("Checkout failed with error %s", err.Error())
				return err
			}
		} else {
			Logf("Reference %s not found for dependency %s", v, r.Name)
		}
	}
	if update {
		err = repo.Update()
		if err != nil {
			Logf("Update failed with error %s", err.Error())
			return err
		}
	}
	r.Reference, err = repo.Version()
	Logf("OK %s %s", r.Reference, r.Name)
	r.LoadManifest()
	r.InstallDeps()
	return err
}

// VCS resolves the vcs.Repo for the Repo
func (r *Repo) VCS() (repo vcs.Repo, err error) {
	r.Lock()
	defer r.Unlock()
	if r.repo != nil {
		repo = r.repo
		return
	}
	repoType := r.RepoType()
	repoURL := r.RepoURL()
	repoPath := r.Path()
	switch repoType {
	case vcs.Git:
		repo, err = vcs.NewGitRepo(repoURL, repoPath)
	case vcs.Bzr:
		repo, err = vcs.NewBzrRepo(repoURL, repoPath)
	case vcs.Hg:
		repo, err = vcs.NewHgRepo(repoURL, repoPath)
	case vcs.Svn:
		repo, err = vcs.NewSvnRepo(repoURL, repoPath)
	}
	r.repo = repo
	return
}

// PathOptions returns a list of possible locations to find the repo
func (r *Repo) PathOptions() []string {
	pathOptions := []string{r.Path()}
	pathOptions = append(pathOptions, filepath.Join(r.Path(), "vendor", r.Name))
	parent := r.parent
	// Climb tree to find possible parent vendor dirs
	for parent != nil {
		pathOptions = append(pathOptions, filepath.Join(parent.Path(), "vendor", r.Name))
		parent = parent.parent
	}
	pathOptions = append(pathOptions, filepath.Join(gopath, "src", r.Name))
	return pathOptions
}

// RepoURL creates the repo url from the package import path
func (r *Repo) RepoURL() string {
	if r.URL != "" {
		return r.URL
	}
	// If it's already installed in vendor or gopath, grab the url from there
	repo := repoFromPath(r.PathOptions()...)
	if repo != nil {
		return repo.Remote()
	}
	// Fallback to resolving the path from the package import path
	// Add more cases as needed/requested
	parts := strings.Split(r.Name, "/")
	switch parts[0] {
	case "github.com":
		return fmt.Sprintf("git@github.com:%s.git", strings.Join(parts[1:3], "/"))
	case "golang.org":
		return fmt.Sprintf("git@github.com:golang/%s.git", parts[2])
	case "gopkg.in":
		nameParts := strings.Split(parts[2], ".")
		name := strings.Join(nameParts[:len(nameParts)-1], ".")
		r.Version = Version(nameParts[len(nameParts)-1])
		return fmt.Sprintf("git@github.com:%s/%s.git", parts[1], name)
	}
	return ""
}

// RepoType attempts to resolve the repository type of the package by it's name
func (r *Repo) RepoType() vcs.Type {
	// If it's already installed in vendor or gopath, grab the type from there
	repo := repoFromPath(r.PathOptions()...)
	if repo != nil {
		return repo.Vcs()
	}
	// Fallback to resolving the type from the package import path
	// Add more cases as needed/requested
	parts := strings.Split(r.Name, "/")
	switch parts[0] {
	case "github.com":
		return vcs.Git
	case "golang.org":
		return vcs.Git
	case "gopkg.in":
		return vcs.Git
	}
	return vcs.NoVCS
}

// MarshalYAML implements yaml.Marsheler to prevent duplicate storage of nested packages with vgo.yaml
func (r *Repo) MarshalYAML() (interface{}, error) {
	r.RLock()
	copy := *r
	r.RUnlock()
	if copy.Name == "." {
		copy.Name = ""
	}
	if copy.hasManifest && copy.parent != nil {
		copy.Dependencies = []*Repo{}
	}
	return copy, nil
}

// Print ...
func (r *Repo) Print(indent string, w io.Writer) {
	r.print(indent, 0, w)
}

func (r *Repo) print(indent string, depth int, w io.Writer) {
	w.Write([]byte(strings.Repeat(indent, depth) + r.Name + "\n"))
	for _, d := range r.Dependencies {
		d.print(indent, depth+1, w)
	}
}
<|endoftext|>


<commit_before>package dnsr

var root = `
;       This file holds the information on root name servers needed to
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers).
;
;       This file is made available by InterNIC
;       under anonymous FTP as
;           file                /domain/named.cache
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
;
;       last update:     May 28, 2019
;       related version of root zone:     2019052802
;
; FORMERLY NS.INTERNIC.NET
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
;
; FORMERLY C.PSI.NET
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
;
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
;
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
; End of file`
<commit_msg>auto-update<commit_after>package dnsr

var root = `
;       This file holds the information on root name servers needed to
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers).
;
;       This file is made available by InterNIC
;       under anonymous FTP as
;           file                /domain/named.cache
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
;
;       last update:     June 17, 2019
;       related version of root zone:     2019061701
;
; FORMERLY NS.INTERNIC.NET
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
;
; FORMERLY C.PSI.NET
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
;
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
;
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
; End of file`
<|endoftext|>


<commit_before>// Copyright 2014 Sam Whited. All rights reserved.
// Use of this source code is governed by the MIT
// license which can be found in the LICENSE file.

// Package sass provides a cgo wrapper for libsass which can be used to compile
// SASS and SCSS to CSS.
package sass

// #cgo LDFLAGS: -lsass
/*
#include <sass_interface.h>
#include <stdlib.h>
void set_source(char* source_string, struct sass_context* ctx) {
	ctx->source_string = source_string;
}
void set_file_path(char* input_path, struct sass_file_context* ctx) {
	ctx->input_path = input_path;
}
void set_folder_paths(char* search_path, char* output_path, struct sass_folder_context* ctx) {
	ctx->search_path = search_path;
	ctx->output_path = output_path;
}
void set_options(struct sass_options options, struct sass_context* ctx) {
	ctx->options = options;
}
void set_file_options(struct sass_options options, struct sass_file_context* ctx) {
	ctx->options = options;
}
void set_folder_options(struct sass_options options, struct sass_folder_context* ctx) {
	ctx->options = options;
}
struct sass_options create_options(int output_style, int source_comments, char* image_path, char* include_paths) {
	struct sass_options options;
	options.output_style = output_style;
	options.source_comments = source_comments;
	options.image_path = image_path;
	options.include_paths = include_paths;

	return options;
}

char* get_output(struct sass_context* ctx) {
	return ctx->output_string;
}

int get_error_status(struct sass_context* ctx) {
	return ctx->error_status;
}

char* get_error_message(struct sass_context* ctx) {
	return ctx->error_message;
}

char* get_file_output(struct sass_file_context* ctx) {
	return ctx->output_string;
}
*/
import "C"
import "unsafe"
import "errors"

// Output CSS styles for compiling SASS.
const (
	STYLE_NESTED = iota
	STYLE_EXPANDED
	STYLE_COMPACT
	STYLE_COMPRESSED
)

// Output style for comments.
const (
	SOURCE_COMMENTS_NONE = iota
	SOURCE_COMMENTS_DEFAULT
	SOURCE_COMMENTS_MAP
)

// A set of options to pass to the SASS compiler.
type options struct {
	output_style    int
	source_comments int
	include_paths   string
	image_path      string
}

// Returns a new options struct with the defaults initialized.
func NewOptions() options {
	return options{
		output_style:    STYLE_NESTED,
		source_comments: SOURCE_COMMENTS_NONE,
		include_paths:   "",
		image_path:      "images",
	}
}

// Compile the given sass string.
func Compile(source string, opts options) (string, error) {
	var (
		ctx *C.struct_sass_context
		ret *C.char
	)

	ctx = C.sass_new_context()
	defer C.sass_free_context(ctx)
	defer C.free(unsafe.Pointer(ret))

	ctx.setOptions(opts)
	ctx.setSource(source)
	_, err := C.sass_compile(ctx)
	ret = C.get_output(ctx)
	errorstatus := C.get_error_status(ctx)
	errormessage := C.get_error_message(ctx)

	if errorstatus != 0 {
		err = errors.New(C.GoString(errormessage))
	}

	out := C.GoString(ret)

	return out, err
}

// Compile the given file.
func CompileFile(path string, opts options) (string, error) {
	var (
		ctx *C.struct_sass_file_context
		ret *C.char
	)

	ctx = C.sass_new_file_context()
	defer C.sass_free_file_context(ctx)
	defer C.free(unsafe.Pointer(ret))

	ctx.setOptions(opts)
	ctx.setPath(path)
	_, err := C.sass_compile_file(ctx)
	ret = C.get_file_output(ctx)
	out := C.GoString(ret)

	return out, err
}

// Compile all sass/scss files in the given directory.
func CompileDir(
	searchPath string,
	outPath string,
	opts options) error {

	ctx := C.sass_new_folder_context()
	defer C.sass_free_folder_context(ctx)

	ctx.setOptions(opts)
	ctx.setPaths(searchPath, outPath)
	_, err := C.sass_compile_folder(ctx)

	return err
}

// Sets the source for the given libsass context.
func (ctx *_Ctype_struct_sass_context) setSource(source string) error {
	csource := C.CString(source)
	_, err := C.set_source(csource, ctx)
	return err
}

// Sets the path for the given libsass file context.
func (ctx *_Ctype_struct_sass_file_context) setPath(path string) error {
	cpath := C.CString(path)
	_, err := C.set_file_path(cpath, ctx)
	return err
}

// Sets the search path and output path for the given libsass folder context.
func (ctx *_Ctype_struct_sass_folder_context) setPaths(
	searchPath string, outPath string) error {
	cspath := C.CString(searchPath)
	copath := C.CString(outPath)
	_, err := C.set_folder_paths(cspath, copath, ctx)
	return err
}

// Sets the libsass options for the given context.
func (ctx *_Ctype_struct_sass_context) setOptions(opts options) error {
	var (
		coptions C.struct_sass_options
		cim      = C.CString(opts.image_path)
		cin      = C.CString(opts.include_paths)
		cos      = C.int(opts.output_style)
		csc      = C.int(opts.source_comments)
	)

	coptions, err := C.create_options(cos, csc, cim, cin)
	if err != nil {
		return err
	}
	_, err = C.set_options(coptions, ctx)

	return err
}

// Sets the libsass options for the given file context.
func (ctx *_Ctype_struct_sass_file_context) setOptions(opts options) error {
	coptions, err := createCOptions(opts)
	if err != nil {
		return err
	}
	_, err = C.set_file_options(coptions, ctx)

	return err
}

// Sets the libsass options for the given folder context.
func (ctx *_Ctype_struct_sass_folder_context) setOptions(opts options) error {
	coptions, err := createCOptions(opts)
	if err != nil {
		return err
	}
	_, err = C.set_folder_options(coptions, ctx)

	return err
}

// Create a C options struct for libsass from some Go options.
func createCOptions(opts options) (C.struct_sass_options, error) {
	var (
		coptions C.struct_sass_options
		cim      = C.CString(opts.image_path)
		cin      = C.CString(opts.include_paths)
		cos      = C.int(opts.output_style)
		csc      = C.int(opts.source_comments)
	)

	coptions, err := C.create_options(cos, csc, cim, cin)
	return coptions, err
}
<commit_msg>Update sass.go<commit_after>// Copyright 2014 Sam Whited. All rights reserved.
// Use of this source code is governed by the MIT
// license which can be found in the LICENSE file.

// Package sass provides a cgo wrapper for libsass which can be used to compile
// SASS and SCSS to CSS.
package sass

// #cgo LDFLAGS: -lsass
/*
#include <sass_interface.h>
#include <stdlib.h>
void set_source(char* source_string, struct sass_context* ctx) {
	ctx->source_string = source_string;
}
void set_file_path(char* input_path, struct sass_file_context* ctx) {
	ctx->input_path = input_path;
}
void set_folder_paths(char* search_path, char* output_path, struct sass_folder_context* ctx) {
	ctx->search_path = search_path;
	ctx->output_path = output_path;
}
void set_options(struct sass_options options, struct sass_context* ctx) {
	ctx->options = options;
}
void set_file_options(struct sass_options options, struct sass_file_context* ctx) {
	ctx->options = options;
}
void set_folder_options(struct sass_options options, struct sass_folder_context* ctx) {
	ctx->options = options;
}
struct sass_options create_options(int output_style, int source_comments, char* image_path, char* include_paths) {
	struct sass_options options;
	options.output_style = output_style;
	options.source_comments = source_comments;
	//options.image_path = image_path;
	options.include_paths = include_paths;

	return options;
}

char* get_output(struct sass_context* ctx) {
	return ctx->output_string;
}

int get_error_status(struct sass_context* ctx) {
	return ctx->error_status;
}

char* get_error_message(struct sass_context* ctx) {
	return ctx->error_message;
}

char* get_file_output(struct sass_file_context* ctx) {
	return ctx->output_string;
}
*/
import "C"
import "unsafe"
import "errors"

// Output CSS styles for compiling SASS.
const (
	STYLE_NESTED = iota
	STYLE_EXPANDED
	STYLE_COMPACT
	STYLE_COMPRESSED
)

// Output style for comments.
const (
	SOURCE_COMMENTS_NONE = iota
	SOURCE_COMMENTS_DEFAULT
	SOURCE_COMMENTS_MAP
)

// A set of options to pass to the SASS compiler.
type options struct {
	output_style    int
	source_comments int
	include_paths   string
	image_path      string
}

// Returns a new options struct with the defaults initialized.
func NewOptions() options {
	return options{
		output_style:    STYLE_NESTED,
		source_comments: SOURCE_COMMENTS_NONE,
		include_paths:   "",
		image_path:      "images",
	}
}

// Compile the given sass string.
func Compile(source string, opts options) (string, error) {
	var (
		ctx *C.struct_sass_context
		ret *C.char
	)

	ctx = C.sass_new_context()
	defer C.sass_free_context(ctx)
	defer C.free(unsafe.Pointer(ret))

	ctx.setOptions(opts)
	ctx.setSource(source)
	_, err := C.sass_compile(ctx)
	ret = C.get_output(ctx)
	errorstatus := C.get_error_status(ctx)
	errormessage := C.get_error_message(ctx)

	if errorstatus != 0 {
		err = errors.New(C.GoString(errormessage))
	}

	out := C.GoString(ret)

	return out, err
}

// Compile the given file.
func CompileFile(path string, opts options) (string, error) {
	var (
		ctx *C.struct_sass_file_context
		ret *C.char
	)

	ctx = C.sass_new_file_context()
	defer C.sass_free_file_context(ctx)
	defer C.free(unsafe.Pointer(ret))

	ctx.setOptions(opts)
	ctx.setPath(path)
	_, err := C.sass_compile_file(ctx)
	ret = C.get_file_output(ctx)
	out := C.GoString(ret)

	return out, err
}

// Compile all sass/scss files in the given directory.
func CompileDir(
	searchPath string,
	outPath string,
	opts options) error {

	ctx := C.sass_new_folder_context()
	defer C.sass_free_folder_context(ctx)

	ctx.setOptions(opts)
	ctx.setPaths(searchPath, outPath)
	_, err := C.sass_compile_folder(ctx)

	return err
}

// Sets the source for the given libsass context.
func (ctx *_Ctype_struct_sass_context) setSource(source string) error {
	csource := C.CString(source)
	_, err := C.set_source(csource, ctx)
	return err
}

// Sets the path for the given libsass file context.
func (ctx *_Ctype_struct_sass_file_context) setPath(path string) error {
	cpath := C.CString(path)
	_, err := C.set_file_path(cpath, ctx)
	return err
}

// Sets the search path and output path for the given libsass folder context.
func (ctx *_Ctype_struct_sass_folder_context) setPaths(
	searchPath string, outPath string) error {
	cspath := C.CString(searchPath)
	copath := C.CString(outPath)
	_, err := C.set_folder_paths(cspath, copath, ctx)
	return err
}

// Sets the libsass options for the given context.
func (ctx *_Ctype_struct_sass_context) setOptions(opts options) error {
	var (
		coptions C.struct_sass_options
		cim      = C.CString(opts.image_path)
		cin      = C.CString(opts.include_paths)
		cos      = C.int(opts.output_style)
		csc      = C.int(opts.source_comments)
	)

	coptions, err := C.create_options(cos, csc, cim, cin)
	if err != nil {
		return err
	}
	_, err = C.set_options(coptions, ctx)

	return err
}

// Sets the libsass options for the given file context.
func (ctx *_Ctype_struct_sass_file_context) setOptions(opts options) error {
	coptions, err := createCOptions(opts)
	if err != nil {
		return err
	}
	_, err = C.set_file_options(coptions, ctx)

	return err
}

// Sets the libsass options for the given folder context.
func (ctx *_Ctype_struct_sass_folder_context) setOptions(opts options) error {
	coptions, err := createCOptions(opts)
	if err != nil {
		return err
	}
	_, err = C.set_folder_options(coptions, ctx)

	return err
}

// Create a C options struct for libsass from some Go options.
func createCOptions(opts options) (C.struct_sass_options, error) {
	var (
		coptions C.struct_sass_options
		cim      = C.CString(opts.image_path)
		cin      = C.CString(opts.include_paths)
		cos      = C.int(opts.output_style)
		csc      = C.int(opts.source_comments)
	)

	coptions, err := C.create_options(cos, csc, cim, cin)
	return coptions, err
}
<|endoftext|>


<commit_before>// send.go - mixnet client send
// Copyright (C) 2018  David Stainton.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package client

import (
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"github.com/katzenpost/client/utils"
	"io"
	"time"

	cConstants "github.com/katzenpost/client/constants"
	"github.com/katzenpost/core/constants"
	"github.com/katzenpost/core/crypto/rand"
	sConstants "github.com/katzenpost/core/sphinx/constants"
)

var ErrReplyTimeout = errors.New("failure waiting for reply, timeout reached")
var ErrMessageNotSent = errors.New("failure sending message")

func (s *Session) sendNext() {
	msg, err := s.egressQueue.Peek()
	if err != nil {
		s.fatalErrCh <- errors.New("impossible failure to Peek from queue")
		return
	}
	if msg == nil {
		s.fatalErrCh <- errors.New("impossible failure, got nil message from queue")
		return
	}
	m := msg.(*Message)
	s.doSend(m)
	_, err = s.egressQueue.Pop()
	if err != nil {
		s.fatalErrCh <- errors.New("impossible failure to Pop from queue")
	}
}

func NewRescheduler(s *Session) *rescheduler {
	r := &rescheduler{s: s}
	s.log.Debugf("Creating TimerQueue")
	r.timerQ = NewTimerQueue(r)
	return r
}

type rescheduler struct {
	s *Session
	timerQ *TimerQueue
}

func (r *rescheduler) Push(i Item) error {
	// rescheduler checks whether a message was ACK'd when the timerQ fires
	// and if it has not, reschedules the message for transmission again
	m := i.(*Message)
	if _, ok := r.s.surbIDMap.Load(*m.SURBID); ok {
		// still waiting for a SURB-ACK that hasn't arrived
		r.s.surbIDMap.Delete(*m.SURBID)
		r.s.opCh <- opRetransmit{msg :m}
	}
	return nil
}

func (s *Session) doRetransmit(msg *Message) {
	msg.Retransmissions++
	msgIdStr := fmt.Sprintf("[%v]", hex.EncodeToString(msg.ID[:]))
	s.log.Debugf("doRetransmit: %d for %s", msg.Retransmissions, msgIdStr)
	s.doSend(msg)
}

func (s *Session) doSend(msg *Message) {
	surbID := [sConstants.SURBIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, surbID[:])
	if err != nil {
		s.fatalErrCh <- fmt.Errorf("impossible failure, failed to generate SURB ID for message ID %x", *msg.ID)
		return
	}
	key := []byte{}
	var eta time.Duration
	msgIdStr := fmt.Sprintf("[%v]", hex.EncodeToString(msg.ID[:]))
	if msg.WithSURB {
		msg.SURBID = &surbID
		surbIdStr := fmt.Sprintf("[%v]", hex.EncodeToString(surbID[:]))
		s.log.Debugf("doSend %s with SURB ID %s", msgIdStr, surbIdStr)
		key, eta, err = s.minclient.SendCiphertext(msg.Recipient, msg.Provider, &surbID, msg.Payload)
	} else {
		s.log.Debugf("doSend %s without SURB", msgIdStr)
		err = s.minclient.SendUnreliableCiphertext(msg.Recipient, msg.Provider, msg.Payload)
	}

	// message was sent
	if err == nil {
		msg.SentAt = time.Now()
	}
	// expect a reply
	if msg.WithSURB {
		if err == nil {
			s.log.Debugf("doSend setting ReplyETA to %v", eta)
			// increase the timeout for each retransmission
			msg.ReplyETA = eta * (1 + time.Duration(msg.Retransmissions))
			msg.Key = key
			s.surbIDMap.Store(surbID, msg)
			if msg.Reliable {
				s.log.Debugf("Sending reliable message with retransmissions")
				timeSlop := eta // add a round-trip worth of delay before timing out
				msg.QueuePriority = uint64(msg.SentAt.Add(msg.ReplyETA).Add(timeSlop).UnixNano())
				s.rescheduler.timerQ.Push(msg)
			}
		}
		// write to waiting channel or close channel if message failed to send
		if msg.IsBlocking {
			sentWaitChanRaw, ok := s.sentWaitChanMap.Load(*msg.ID)
			if !ok {
				s.fatalErrCh <- fmt.Errorf("impossible failure, sentWaitChan not found for message ID %x", *msg.ID)
				return
			}
			sentWaitChan := sentWaitChanRaw.(chan *Message)
			if err == nil {
				sentWaitChan <- msg
			} else {
				close(sentWaitChan)
			}
			return
		}
	}
	s.eventCh.In() <- &MessageSentEvent{
		MessageID: msg.ID,
		Err:       err,
		SentAt:    msg.SentAt,
		ReplyETA:  msg.ReplyETA,
	}
}

func (s *Session) sendDropDecoy(loopSvc *utils.ServiceDescriptor) {
	payload := make([]byte, constants.UserForwardPayloadLength)
	id := [cConstants.MessageIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, id[:])
	if err != nil {
		s.fatalErrCh <- errors.New("failure to generate message ID for drop decoy")
		return
	}
	msg := &Message{
		ID:        &id,
		Recipient: loopSvc.Name,
		Provider:  loopSvc.Provider,
		Payload:   payload[:],
		WithSURB:  false,
		IsDecoy:   true,
	}
	s.doSend(msg)
}

func (s *Session) sendLoopDecoy(loopSvc *utils.ServiceDescriptor) {
	s.log.Info("sending loop decoy")
	payload := make([]byte, constants.UserForwardPayloadLength)
	id := [cConstants.MessageIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, id[:])
	if err != nil {
		s.fatalErrCh <- errors.New("failure to generate message ID for loop decoy")
		return
	}
	msg := &Message{
		ID:        &id,
		Recipient: loopSvc.Name,
		Provider:  loopSvc.Provider,
		Payload:   payload[:],
		WithSURB:  true,
		IsDecoy:   true,
	}
	defer s.incrementDecoyLoopTally()
	s.doSend(msg)
}

func (s *Session) composeMessage(recipient, provider string, message []byte, isBlocking bool) (*Message, error) {
	s.log.Debug("SendMessage")
	if len(message) > constants.UserForwardPayloadLength-4 {
		return nil, fmt.Errorf("invalid message size: %v", len(message))
	}
	payload := make([]byte, constants.UserForwardPayloadLength)
	binary.BigEndian.PutUint32(payload[:4], uint32(len(message)))
	copy(payload[4:], message)
	id := [cConstants.MessageIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, id[:])
	if err != nil {
		return nil, err
	}
	var msg = Message{
		ID:         &id,
		Recipient:  recipient,
		Provider:   provider,
		Payload:    payload[:],
		WithSURB:   true,
		IsBlocking: isBlocking,
	}
	return &msg, nil
}

// SendReliableMessage asynchronously sends messages with automatic retransmissiosn.
func (s *Session) SendReliableMessage(recipient, provider string, message []byte) (*[cConstants.MessageIDLength]byte, error) {
	msg, err := s.composeMessage(recipient, provider, message, false)
	if err != nil {
		return nil, err
	}
	msg.Reliable = true
	err = s.egressQueue.Push(msg)
	if err != nil {
		return nil, err
	}
	return msg.ID, nil
}

// SendUnreliableMessage asynchronously sends message without any automatic retransmissions.
func (s *Session) SendUnreliableMessage(recipient, provider string, message []byte) (*[cConstants.MessageIDLength]byte, error) {
	msg, err := s.composeMessage(recipient, provider, message, false)
	if err != nil {
		return nil, err
	}
	err = s.egressQueue.Push(msg)
	if err != nil {
		return nil, err
	}
	return msg.ID, nil
}

func (s *Session) BlockingSendUnreliableMessage(recipient, provider string, message []byte) ([]byte, error) {
	msg, err := s.composeMessage(recipient, provider, message, true)
	if err != nil {
		return nil, err
	}
	sentWaitChan := make(chan *Message)
	s.sentWaitChanMap.Store(*msg.ID, sentWaitChan)
	defer s.sentWaitChanMap.Delete(*msg.ID)

	replyWaitChan := make(chan []byte)
	s.replyWaitChanMap.Store(*msg.ID, replyWaitChan)
	defer s.replyWaitChanMap.Delete(*msg.ID)

	err = s.egressQueue.Push(msg)
	if err != nil {
		return nil, err
	}

	// wait until sent so that we know the ReplyETA for the waiting below
	sentMessage := <-sentWaitChan

	// if the message failed to send we will receive a nil message
	if sentMessage == nil {
		return nil, ErrMessageNotSent
	}

	// wait for reply or round trip timeout
	select {
	case reply := <-replyWaitChan:
		return reply, nil
	// these timeouts are often far too aggressive
	case <-time.After(sentMessage.ReplyETA + cConstants.RoundTripTimeSlop):
		return nil, ErrReplyTimeout
	}
	// unreachable
}

// BlockingSendReliableMessage sends a message with automatic message retransmission enabled
func (s *Session) BlockingSendReliableMessage(recipient, provider string, message []byte) ([]byte, error) {
	msg, err := s.composeMessage(recipient, provider, message, true)
	if err != nil {
		return nil, err
	}
	msg.Reliable = true
	sentWaitChan := make(chan *Message)
	s.sentWaitChanMap.Store(*msg.ID, sentWaitChan)
	defer s.sentWaitChanMap.Delete(*msg.ID)

	replyWaitChan := make(chan []byte)
	s.replyWaitChanMap.Store(*msg.ID, replyWaitChan)
	defer s.replyWaitChanMap.Delete(*msg.ID)

	err = s.egressQueue.Push(msg)
	if err != nil {
		return nil, err
	}

	// wait until sent so that we know the ReplyETA for the waiting below
	sentMessage := <-sentWaitChan

	// if the message failed to send we will receive a nil message
	if sentMessage == nil {
		return nil, ErrMessageNotSent
	}

	// TODO: it would be better to have the message automatically retransmitted a configurable number of times before emitting a failure to this channel
	// wait for reply or round trip timeout
	select {
	case reply := <-replyWaitChan:
		return reply, nil
	// these timeouts are often far too aggressive
	case <-time.After(sentMessage.ReplyETA + cConstants.RoundTripTimeSlop):
		return nil, ErrReplyTimeout
	}
	// unreachable
}
<commit_msg>Do not do anything with msg until it is ACKd<commit_after>// send.go - mixnet client send
// Copyright (C) 2018  David Stainton.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package client

import (
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"github.com/katzenpost/client/utils"
	"io"
	"time"

	cConstants "github.com/katzenpost/client/constants"
	"github.com/katzenpost/core/constants"
	"github.com/katzenpost/core/crypto/rand"
	sConstants "github.com/katzenpost/core/sphinx/constants"
)

var ErrReplyTimeout = errors.New("failure waiting for reply, timeout reached")
var ErrMessageNotSent = errors.New("failure sending message")

func (s *Session) sendNext() {
	msg, err := s.egressQueue.Peek()
	if err != nil {
		s.fatalErrCh <- errors.New("impossible failure to Peek from queue")
		return
	}
	if msg == nil {
		s.fatalErrCh <- errors.New("impossible failure, got nil message from queue")
		return
	}
	m := msg.(*Message)
	s.doSend(m)
	_, err = s.egressQueue.Pop()
	if err != nil {
		s.fatalErrCh <- errors.New("impossible failure to Pop from queue")
	}
}

func NewRescheduler(s *Session) *rescheduler {
	r := &rescheduler{s: s}
	s.log.Debugf("Creating TimerQueue")
	r.timerQ = NewTimerQueue(r)
	return r
}

type rescheduler struct {
	s *Session
	timerQ *TimerQueue
}

func (r *rescheduler) Push(i Item) error {
	// rescheduler checks whether a message was ACK'd when the timerQ fires
	// and if it has not, reschedules the message for transmission again
	m := i.(*Message)
	if _, ok := r.s.surbIDMap.Load(*m.SURBID); ok {
		// still waiting for a SURB-ACK that hasn't arrived
		r.s.surbIDMap.Delete(*m.SURBID)
		r.s.opCh <- opRetransmit{msg :m}
	}
	return nil
}

func (s *Session) doRetransmit(msg *Message) {
	msg.Retransmissions++
	msgIdStr := fmt.Sprintf("[%v]", hex.EncodeToString(msg.ID[:]))
	s.log.Debugf("doRetransmit: %d for %s", msg.Retransmissions, msgIdStr)
	s.doSend(msg)
}

func (s *Session) doSend(msg *Message) {
	surbID := [sConstants.SURBIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, surbID[:])
	if err != nil {
		s.fatalErrCh <- fmt.Errorf("impossible failure, failed to generate SURB ID for message ID %x", *msg.ID)
		return
	}
	key := []byte{}
	var eta time.Duration
	msgIdStr := fmt.Sprintf("[%v]", hex.EncodeToString(msg.ID[:]))
	if msg.WithSURB {
		msg.SURBID = &surbID
		surbIdStr := fmt.Sprintf("[%v]", hex.EncodeToString(surbID[:]))
		s.log.Debugf("doSend %s with SURB ID %s", msgIdStr, surbIdStr)
		key, eta, err = s.minclient.SendCiphertext(msg.Recipient, msg.Provider, &surbID, msg.Payload)
	} else {
		s.log.Debugf("doSend %s without SURB", msgIdStr)
		err = s.minclient.SendUnreliableCiphertext(msg.Recipient, msg.Provider, msg.Payload)
	}

	// message was sent
	if err == nil {
		msg.SentAt = time.Now()
	}
	// expect a reply
	if msg.WithSURB {
		if err == nil {
			s.log.Debugf("doSend setting ReplyETA to %v", eta)
			// increase the timeout for each retransmission
			msg.ReplyETA = eta * (1 + time.Duration(msg.Retransmissions))
			msg.Key = key
			s.surbIDMap.Store(surbID, msg)
			if msg.Reliable {
				s.log.Debugf("Sending reliable message with retransmissions")
				timeSlop := eta // add a round-trip worth of delay before timing out
				msg.QueuePriority = uint64(msg.SentAt.Add(msg.ReplyETA).Add(timeSlop).UnixNano())
				s.rescheduler.timerQ.Push(msg)
			}
		}
		// write to waiting channel or close channel if message failed to send
		if msg.IsBlocking {
			sentWaitChanRaw, ok := s.sentWaitChanMap.Load(*msg.ID)
			if !ok {
				s.fatalErrCh <- fmt.Errorf("impossible failure, sentWaitChan not found for message ID %x", *msg.ID)
				return
			}
			sentWaitChan := sentWaitChanRaw.(chan *Message)
			if err == nil {
				sentWaitChan <- msg
			} else {
				close(sentWaitChan)
			}
			return
		}
	}
	s.eventCh.In() <- &MessageSentEvent{
		MessageID: msg.ID,
		Err:       err,
		SentAt:    msg.SentAt,
		ReplyETA:  msg.ReplyETA,
	}
}

func (s *Session) sendDropDecoy(loopSvc *utils.ServiceDescriptor) {
	payload := make([]byte, constants.UserForwardPayloadLength)
	id := [cConstants.MessageIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, id[:])
	if err != nil {
		s.fatalErrCh <- errors.New("failure to generate message ID for drop decoy")
		return
	}
	msg := &Message{
		ID:        &id,
		Recipient: loopSvc.Name,
		Provider:  loopSvc.Provider,
		Payload:   payload[:],
		WithSURB:  false,
		IsDecoy:   true,
	}
	s.doSend(msg)
}

func (s *Session) sendLoopDecoy(loopSvc *utils.ServiceDescriptor) {
	s.log.Info("sending loop decoy")
	payload := make([]byte, constants.UserForwardPayloadLength)
	id := [cConstants.MessageIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, id[:])
	if err != nil {
		s.fatalErrCh <- errors.New("failure to generate message ID for loop decoy")
		return
	}
	msg := &Message{
		ID:        &id,
		Recipient: loopSvc.Name,
		Provider:  loopSvc.Provider,
		Payload:   payload[:],
		WithSURB:  true,
		IsDecoy:   true,
	}
	defer s.incrementDecoyLoopTally()
	s.doSend(msg)
}

func (s *Session) composeMessage(recipient, provider string, message []byte, isBlocking bool) (*Message, error) {
	s.log.Debug("SendMessage")
	if len(message) > constants.UserForwardPayloadLength-4 {
		return nil, fmt.Errorf("invalid message size: %v", len(message))
	}
	payload := make([]byte, constants.UserForwardPayloadLength)
	binary.BigEndian.PutUint32(payload[:4], uint32(len(message)))
	copy(payload[4:], message)
	id := [cConstants.MessageIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, id[:])
	if err != nil {
		return nil, err
	}
	var msg = Message{
		ID:         &id,
		Recipient:  recipient,
		Provider:   provider,
		Payload:    payload[:],
		WithSURB:   true,
		IsBlocking: isBlocking,
	}
	return &msg, nil
}

// SendReliableMessage asynchronously sends messages with automatic retransmissiosn.
func (s *Session) SendReliableMessage(recipient, provider string, message []byte) (*[cConstants.MessageIDLength]byte, error) {
	msg, err := s.composeMessage(recipient, provider, message, false)
	if err != nil {
		return nil, err
	}
	msg.Reliable = true
	err = s.egressQueue.Push(msg)
	if err != nil {
		return nil, err
	}
	return msg.ID, nil
}

// SendUnreliableMessage asynchronously sends message without any automatic retransmissions.
func (s *Session) SendUnreliableMessage(recipient, provider string, message []byte) (*[cConstants.MessageIDLength]byte, error) {
	msg, err := s.composeMessage(recipient, provider, message, false)
	if err != nil {
		return nil, err
	}
	err = s.egressQueue.Push(msg)
	if err != nil {
		return nil, err
	}
	return msg.ID, nil
}

func (s *Session) BlockingSendUnreliableMessage(recipient, provider string, message []byte) ([]byte, error) {
	msg, err := s.composeMessage(recipient, provider, message, true)
	if err != nil {
		return nil, err
	}
	sentWaitChan := make(chan *Message)
	s.sentWaitChanMap.Store(*msg.ID, sentWaitChan)
	defer s.sentWaitChanMap.Delete(*msg.ID)

	replyWaitChan := make(chan []byte)
	s.replyWaitChanMap.Store(*msg.ID, replyWaitChan)
	defer s.replyWaitChanMap.Delete(*msg.ID)

	err = s.egressQueue.Push(msg)
	if err != nil {
		return nil, err
	}

	// wait until sent so that we know the ReplyETA for the waiting below
	sentMessage := <-sentWaitChan

	// if the message failed to send we will receive a nil message
	if sentMessage == nil {
		return nil, ErrMessageNotSent
	}

	// wait for reply or round trip timeout
	select {
	case reply := <-replyWaitChan:
		return reply, nil
	// these timeouts are often far too aggressive
	case <-time.After(sentMessage.ReplyETA + cConstants.RoundTripTimeSlop):
		return nil, ErrReplyTimeout
	}
	// unreachable
}

// BlockingSendReliableMessage sends a message with automatic message retransmission enabled
func (s *Session) BlockingSendReliableMessage(recipient, provider string, message []byte) ([]byte, error) {
	msg, err := s.composeMessage(recipient, provider, message, true)
	if err != nil {
		return nil, err
	}
	msg.Reliable = true
	sentWaitChan := make(chan *Message)
	s.sentWaitChanMap.Store(*msg.ID, sentWaitChan)
	defer s.sentWaitChanMap.Delete(*msg.ID)

	replyWaitChan := make(chan []byte)
	s.replyWaitChanMap.Store(*msg.ID, replyWaitChan)
	defer s.replyWaitChanMap.Delete(*msg.ID)

	err = s.egressQueue.Push(msg)
	if err != nil {
		return nil, err
	}

	// wait until sent so that we know the ReplyETA for the waiting below
	sentMessage := <-sentWaitChan

	// if the message failed to send we will receive a nil message
	if sentMessage == nil {
		return nil, ErrMessageNotSent
	}

	// TODO: it would be better to have the message automatically retransmitted a configurable number of times before emitting a failure to this channel
	// wait for reply or round trip timeout
	select {
	case reply := <-replyWaitChan:
		return reply, nil
	case <-time.After(cConstants.RoundTripTimeSlop):
		return nil, ErrReplyTimeout
	}
	// unreachable
}
<|endoftext|>


<commit_before>//
// Copyright © 2011 Guy M. Allard
//
// 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 stomp

import (
	"os"
)

// Send
func (c *Connection) Send(h Headers, b string) (e os.Error) {
	c.log(SEND, "start")
	if !c.connected {
		return ECONBAD
	}
	if _, ok := h.Contains("destination"); !ok {
		return EREQDSTSND
	}
	e = nil
	ch := h.Clone()
	f := Frame{SEND, ch, []uint8(b)}
	r := make(chan os.Error)
	c.output <- wiredata{f, r}
	e = <-r
	c.log(SEND, "end")
	return e // nil or not
}
<commit_msg>Add more logging data in Send.<commit_after>//
// Copyright © 2011 Guy M. Allard
//
// 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 stomp

import (
	"os"
)

// Send
func (c *Connection) Send(h Headers, b string) (e os.Error) {
	c.log(SEND, "start", h)
	if !c.connected {
		return ECONBAD
	}
	if _, ok := h.Contains("destination"); !ok {
		return EREQDSTSND
	}
	e = nil
	ch := h.Clone()
	f := Frame{SEND, ch, []uint8(b)}
	r := make(chan os.Error)
	c.output <- wiredata{f, r}
	e = <-r
	c.log(SEND, "end", ch)
	return e // nil or not
}
<|endoftext|>


<commit_before>package main

import (
	_ "time"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"runtime"
	"errors"
	"html/template"
	"exec"
	"os"
	"bytes"
)

var templates = template.Must(template.ParseFiles("index.html"))

const MESSAGE_QUEUE_SIZE = 10

const STATUS_FAILURE = "{\"status\":\"failure\"}"

var authKey = []byte("somesecretauth")
var store sessions.Store

var pool *Pool
var clients map[int64]*Client

var db *sql.DB

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      []byte
	client1 *Client
	client2 *Client
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		for c1.id == c2.id {
			c2 = <- p.in
		}

		fmt.Println("match found for ", c1.id, " and ", c2.id)

		b := make([]byte, 32)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 32 {
			return
		}

		room := &Room{b, c1, c2}

		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	if userid == nil {
		return 0, errors.New("no cookie set")
	}
	return userid.(int64), nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, _ = sql.Open("mysql", "root:pass@/suitup")
	defer db.Close()

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/", mainHandle)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/question/new", newQuestion)
	http.HandleFunc("/question/submit", testCode)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)

	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("/home/suitup/hackmit/assets/"))))
	http.ListenAndServe(":8080", nil)
}

func mainHandle(w http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(w, "index.html", nil)
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("join ", uid)

	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}

	clients[uid] = client
	pool.in <- client

	chatroom := <- retChan

	fmt.Println("joinChatRoom-chatroom.id: ", chatroom.id)

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":\"", asciify(chatroom.id), "\"}")
}

func asciify(ba []byte) string {
	ret := make([]byte, len(ba))
	for i, b := range ba {
		ret[i] = (b % 26) + 97
	}
	return string(ret)
}

func testCode(w http.ResponseWriter, r *http.Request) {
	code := r.PostFormValue("submission")
	cvid := r.PostFormValue("cvid")
	qid := str(1)
	app := "./secure.sh"
	cmd, err := exec.Run(app, []string{app, qid, code}, nil, "", exec.DevNull, exec.Pipe, exec.Pipe)
	if (err != nil) {
       fmt.Fprintln(w, "{\"status\":\"failure\"}")
       return
    }
    var b bytes.Buffer
    io.Copy(&b, cmd.Stdout)
    fmt.Println(w, "{\"status\":\"success\", \"data\": ", b.String(), "}" )
    cmd.Close()
    return
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	client := clients[uid]

	if client != nil {
		delete(clients, uid)
	}

	fmt.Println("leave ", uid)

	fmt.Fprint(w, "{\"status\":\"success\"}")
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("send ", uid)

	message := r.FormValue("s")

	// message := r.PostFormValue("message")

	client := clients[uid]

	if client != nil && client.out != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}")
	} else {
		fmt.Fprint(w, STATUS_FAILURE)
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("check ", uid)

	client := clients[uid]

	if client != nil {
		select {
		case message, ok := <- client.in:
			if ok {
				fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"", message, "\"}")
			} else {
				delete(clients, uid)
				fmt.Fprint(w, "{\"status\":\"failure\"}")
			}
		default:
			fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"\"}")
		}

	} else {
		fmt.Fprint(w, "{\"status\":\"failure\",\"s\":\"\"}")
	}
}

type Question struct {
	Id 	int64			`json:"id"`
	Title string		`json:"title"`
	Body string			`json:"body"`
	Difficulty int 		`json:"diff"`
}

type User struct {
    Id            	int64      	`json:"id"`
    FacebookId  	string		`json:fbid"`
    Username 		string		`json:username"`
    Email 			string		`json:email"`
    Level 			int64		`json:lvl"`
    Score 			int64		`json:score"`
}

func newQuestion(w http.ResponseWriter, r *http.Request) {
	row := db.QueryRow("SELECT * FROM questions ORDER BY RAND()")
	q := new(Question)
	err := row.Scan(&q.Id, &q.Title, &q.Body, &q.Difficulty)

	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	b, err := json.Marshal(q)
	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	fmt.Fprint(w, string(b))
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		// row := db.QueryRow("SELECT id FROM users")
		row := db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
		user := new(User)
		err := row.Scan(&user.Id)

		if err != nil {
			_, err = db.Exec("INSERT INTO users (facebook_id, username, email, level, points) VALUES (?, ?, ?, 0, 0)", uid, "", "")
			if err != nil {
				fmt.Fprint(w, STATUS_FAILURE)
				return
			} else {
				row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
				err = row.Scan(&user.Id)
				if err != nil {
					fmt.Fprint(w, STATUS_FAILURE)
					return
				}
			}

		}

		session, _ := store.Get(r, "session")
		session.Values["userid"] = user.Id
		session.Save(r, w)

		fmt.Fprint(w, "{\"status\":\"success\"}")
	}
}

func readHttpBody(response *http.Response) string {
	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if (err != nil) {
	}
	return nil, err

}

func GetMe(token string) string {
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<commit_msg>adding packages<commit_after>package main

import (
	_ "time"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"runtime"
	"errors"
	"html/template"
	"os/exec"
	"bytes"
)

var templates = template.Must(template.ParseFiles("index.html"))

const MESSAGE_QUEUE_SIZE = 10

const STATUS_FAILURE = "{\"status\":\"failure\"}"

var authKey = []byte("somesecretauth")
var store sessions.Store

var pool *Pool
var clients map[int64]*Client

var db *sql.DB

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      []byte
	client1 *Client
	client2 *Client
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		for c1.id == c2.id {
			c2 = <- p.in
		}

		fmt.Println("match found for ", c1.id, " and ", c2.id)

		b := make([]byte, 32)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 32 {
			return
		}

		room := &Room{b, c1, c2}

		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	if userid == nil {
		return 0, errors.New("no cookie set")
	}
	return userid.(int64), nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, _ = sql.Open("mysql", "root:pass@/suitup")
	defer db.Close()

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/", mainHandle)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/question/new", newQuestion)
	http.HandleFunc("/question/submit", testCode)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)

	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("/home/suitup/hackmit/assets/"))))
	http.ListenAndServe(":8080", nil)
}

func mainHandle(w http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(w, "index.html", nil)
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("join ", uid)

	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}

	clients[uid] = client
	pool.in <- client

	chatroom := <- retChan

	fmt.Println("joinChatRoom-chatroom.id: ", chatroom.id)

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":\"", asciify(chatroom.id), "\"}")
}

func asciify(ba []byte) string {
	ret := make([]byte, len(ba))
	for i, b := range ba {
		ret[i] = (b % 26) + 97
	}
	return string(ret)
}

func testCode(w http.ResponseWriter, r *http.Request) {
	code := r.PostFormValue("submission")
	cvid := r.PostFormValue("cvid")
	qid := str(1)
	app := "./secure.sh"
	cmd, err := exec.Run(app, []string{app, qid, code}, nil, "", exec.DevNull, exec.Pipe, exec.Pipe)
	if (err != nil) {
       fmt.Fprintln(w, "{\"status\":\"failure\"}")
       return
    }
    var b bytes.Buffer
    io.Copy(&b, cmd.Stdout)
    fmt.Println(w, "{\"status\":\"success\", \"data\": ", b.String(), "}" )
    cmd.Close()
    return
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	client := clients[uid]

	if client != nil {
		delete(clients, uid)
	}

	fmt.Println("leave ", uid)

	fmt.Fprint(w, "{\"status\":\"success\"}")
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("send ", uid)

	message := r.FormValue("s")

	// message := r.PostFormValue("message")

	client := clients[uid]

	if client != nil && client.out != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}")
	} else {
		fmt.Fprint(w, STATUS_FAILURE)
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("check ", uid)

	client := clients[uid]

	if client != nil {
		select {
		case message, ok := <- client.in:
			if ok {
				fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"", message, "\"}")
			} else {
				delete(clients, uid)
				fmt.Fprint(w, "{\"status\":\"failure\"}")
			}
		default:
			fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"\"}")
		}

	} else {
		fmt.Fprint(w, "{\"status\":\"failure\",\"s\":\"\"}")
	}
}

type Question struct {
	Id 	int64			`json:"id"`
	Title string		`json:"title"`
	Body string			`json:"body"`
	Difficulty int 		`json:"diff"`
}

type User struct {
    Id            	int64      	`json:"id"`
    FacebookId  	string		`json:fbid"`
    Username 		string		`json:username"`
    Email 			string		`json:email"`
    Level 			int64		`json:lvl"`
    Score 			int64		`json:score"`
}

func newQuestion(w http.ResponseWriter, r *http.Request) {
	row := db.QueryRow("SELECT * FROM questions ORDER BY RAND()")
	q := new(Question)
	err := row.Scan(&q.Id, &q.Title, &q.Body, &q.Difficulty)

	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	b, err := json.Marshal(q)
	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	fmt.Fprint(w, string(b))
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		// row := db.QueryRow("SELECT id FROM users")
		row := db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
		user := new(User)
		err := row.Scan(&user.Id)

		if err != nil {
			_, err = db.Exec("INSERT INTO users (facebook_id, username, email, level, points) VALUES (?, ?, ?, 0, 0)", uid, "", "")
			if err != nil {
				fmt.Fprint(w, STATUS_FAILURE)
				return
			} else {
				row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
				err = row.Scan(&user.Id)
				if err != nil {
					fmt.Fprint(w, STATUS_FAILURE)
					return
				}
			}

		}

		session, _ := store.Get(r, "session")
		session.Values["userid"] = user.Id
		session.Save(r, w)

		fmt.Fprint(w, "{\"status\":\"success\"}")
	}
}

func readHttpBody(response *http.Response) string {
	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if (err != nil) {
	}
	return nil, err

}

func GetMe(token string) string {
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	_ "time"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"encoding/json"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"runtime"
	"errors"
)

const MESSAGE_QUEUE_SIZE = 10

var authKey = []byte("somesecretauth")
var store sessions.Store
var pool *Pool
var clients map[int64]*Client

var db *sql.DB
// var tv syscall.Timeval

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      int64
	client1 *Client
	client2 *Client
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		fmt.Println("match found for ", c1.id, " and ", c2.id)

		b := make([]byte, 8)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 8 {
			return
		}
		crId, _ := binary.Varint(b)

		room := &Room{crId, c1, c2}

		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	fmt.Println(session.Values)

	// b :=

	if userid == nil {
		return 0, errors.New("no cookie set")
	}
	// uid, _ := binary.Varint(b)
	return userid.(int64), nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, _ = sql.Open("mysql", "root:pass@/suitup")
	defer db.Close()

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/", mainHandle)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)
	http.ListenAndServe(":8080", nil)
}

type IdQuery struct {
    Id            int64      `json:"id"`

}

func mainHandle(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "hey")
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

  	fmt.Println("uid: ", uid)
	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}
	clients[uid] = client
	pool.in <- client

	fmt.Println("added ", uid, " to queue")
	chatroom := <- retChan

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":", chatroom.id, "}")
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	fmt.Fprint(w, uid)
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println(uid)

	message := r.FormValue("s")

	// message := r.PostFormValue("message")

	client := clients[uid]

	if client != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}-", message)
	} else {
		fmt.Fprint(w, "{\"status\":\"failure\"}-", message)
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	client := clients[uid]

	if client != nil {
		fmt.Println("client found")
		select {
		case message, ok := <- clients[uid].in:
			fmt.Println("message pulled from channel")
			if ok {
				fmt.Fprint(w, "{\"status\":\"success\"}-", message)
			} else {
				fmt.Fprint(w, "{\"status\":\"failure\"}")
			}
		default:
			fmt.Println("default")
			fmt.Fprint(w, "")
		}

	} else {
		fmt.Println("client not found")
		fmt.Fprint(w, "{\"status\":\"failure\"}")
	}
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		// row := db.QueryRow("SELECT id FROM users")
		row := db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
		iq := new(IdQuery)
		err := row.Scan(&iq.Id)

		if err != nil {
			_, err = db.Exec("insert into users (facebook_id, username, email, level, points) values (?, ?, ?, 0, 0)", uid, "", "")
			if err != nil {
				fmt.Fprint(w, "{\"status\":\"failure\"}")
				return
			} else {
				row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
				err = row.Scan(&iq.Id)
				if err != nil {
					fmt.Fprint(w, "{\"status\":\"failure\"}")
					return
				}
			}

		}

		fmt.Println("session-id: ", iq.Id)

		session, _ := store.Get(r, "session")
		session.Values["userid"] = iq.Id
		session.Save(r, w)

		fmt.Println(session.Values)

		fmt.Fprint(w, "{\"status\":\"success\"}")

	// 	if err == nil {
	// 		fmt.Fprint(w, "{\"status\":\"success\",\"uid\":", iq.Id, "}")
	// 	} else {
	// 		_, err = db.Exec("insert into users (facebook_id, username, email, level, points) values (?, ?, ?, 0, 0)", uid, "", "")
	// 		if err == nil {
	// 			row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
	// 			err = row.Scan(&iq.Id)
	// 			if err == nil {
	// 				fmt.Fprint(w, "{\"status\":\"success\"},\"uid\":", iq.Id, "}")
	// 			} else {
	// 				fmt.Fprint(w, "{\"status\":\"failure\"}")
	// 			}
	// 		} else {
	// 			fmt.Fprint(w, "{\"status\":\"failure\"}")
	// 		}
	// 	}
	// } else {
	// 	fmt.Fprint(w, "{\"status\":\"failure\"}")
	}
}

func readHttpBody(response *http.Response) string {
	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if (err != nil) {
	}
	return nil, err

}

func GetMe(token string) string {
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<commit_msg>debug<commit_after>package main

import (
	_ "time"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"encoding/json"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"runtime"
	"errors"
)

const MESSAGE_QUEUE_SIZE = 10

var authKey = []byte("somesecretauth")
var store sessions.Store
var pool *Pool
var clients map[int64]*Client

var db *sql.DB
// var tv syscall.Timeval

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      int64
	client1 *Client
	client2 *Client
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		fmt.Println("match found for ", c1.id, " and ", c2.id)

		b := make([]byte, 8)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 8 {
			return
		}
		crId, _ := binary.Varint(b)

		room := &Room{crId, c1, c2}

		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	fmt.Println(session.Values)
	if userid == nil {
		return 0, errors.New("no cookie set")
	}
	return userid.(int64), nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, _ = sql.Open("mysql", "root:pass@/suitup")
	defer db.Close()

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/", mainHandle)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)
	http.ListenAndServe(":8080", nil)
}

type IdQuery struct {
    Id            int64      `json:"id"`

}

func mainHandle(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "hey")
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

  	fmt.Println("uid: ", uid)
	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}
	clients[uid] = client
	pool.in <- client

	fmt.Println("added ", uid, " to queue")
	chatroom := <- retChan

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":", chatroom.id, "}")
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	fmt.Fprint(w, uid)
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println(uid)

	message := r.FormValue("s")

	// message := r.PostFormValue("message")

	client := clients[uid]

	if client != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}-", message)
	} else {
		fmt.Fprint(w, "{\"status\":\"failure\"}-", message)
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	client := clients[uid]

	if client != nil {
		fmt.Println("client found")
		select {
		case message, ok := <- clients[uid].in:
			fmt.Println("message pulled from channel")
			if ok {
				fmt.Fprint(w, message)
			} else {
				fmt.Fprint(w, "")
			}
		default:
			fmt.Println("")
			fmt.Fprint(w, "")
		}

	} else {
		fmt.Println("client not found")
		fmt.Fprint(w, "")
	}
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		// row := db.QueryRow("SELECT id FROM users")
		row := db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
		iq := new(IdQuery)
		err := row.Scan(&iq.Id)

		if err != nil {
			_, err = db.Exec("insert into users (facebook_id, username, email, level, points) values (?, ?, ?, 0, 0)", uid, "", "")
			if err != nil {
				fmt.Fprint(w, "{\"status\":\"failure\"}")
				return
			} else {
				row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
				err = row.Scan(&iq.Id)
				if err != nil {
					fmt.Fprint(w, "{\"status\":\"failure\"}")
					return
				}
			}

		}

		fmt.Println("session-id: ", iq.Id)

		session, _ := store.Get(r, "session")
		session.Values["userid"] = iq.Id
		session.Save(r, w)

		fmt.Println(session.Values)

		fmt.Fprint(w, "{\"status\":\"success\"}")

	// 	if err == nil {
	// 		fmt.Fprint(w, "{\"status\":\"success\",\"uid\":", iq.Id, "}")
	// 	} else {
	// 		_, err = db.Exec("insert into users (facebook_id, username, email, level, points) values (?, ?, ?, 0, 0)", uid, "", "")
	// 		if err == nil {
	// 			row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
	// 			err = row.Scan(&iq.Id)
	// 			if err == nil {
	// 				fmt.Fprint(w, "{\"status\":\"success\"},\"uid\":", iq.Id, "}")
	// 			} else {
	// 				fmt.Fprint(w, "{\"status\":\"failure\"}")
	// 			}
	// 		} else {
	// 			fmt.Fprint(w, "{\"status\":\"failure\"}")
	// 		}
	// 	}
	// } else {
	// 	fmt.Fprint(w, "{\"status\":\"failure\"}")
	}
}

func readHttpBody(response *http.Response) string {
	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if (err != nil) {
	}
	return nil, err

}

func GetMe(token string) string {
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>