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

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

// A vcsCmd describes how to use a version control system
// like Mercurial, Git, or Subversion.
type vcsCmd struct {
	name string
	cmd  string // name of binary to invoke command

	createCmd   string // command to download a fresh copy of a repository
	downloadCmd string // command to download updates into an existing repository

	tagCmd     []tagCmd // commands to list tags
	tagDefault string   // default tag to use
	tagSyncCmd string   // command to sync to specific tag
}

// A tagCmd describes a command to list available tags
// that can be passed to tagSyncCmd.
type tagCmd struct {
	cmd     string // command to list tags
	pattern string // regexp to extract tags from list
}

// vcsList lists the known version control systems
var vcsList = []*vcsCmd{
	vcsHg,
	vcsGit,
	vcsSvn,
	vcsBzr,
}

// vcsByCmd returns the version control system for the given
// command name (hg, git, svn, bzr).
func vcsByCmd(cmd string) *vcsCmd {
	for _, vcs := range vcsList {
		if vcs.cmd == cmd {
			return vcs
		}
	}
	return nil
}

// vcsHg describes how to use Mercurial.
var vcsHg = &vcsCmd{
	name: "Mercurial",
	cmd:  "hg",

	createCmd:   "clone -U {repo} {dir}",
	downloadCmd: "pull",

	// We allow both tag and branch names as 'tags'
	// for selecting a version.  This lets people have
	// a go.release.r60 branch and a go.1 branch
	// and make changes in both, without constantly
	// editing .hgtags.
	tagCmd: []tagCmd{
		{"tags", `^(\S+)`},
		{"branches", `^(\S+)`},
	},
	tagDefault: "default",
	tagSyncCmd: "update -r {tag}",
}

// vcsGit describes how to use Git.
var vcsGit = &vcsCmd{
	name: "Git",
	cmd:  "git",

	createCmd:   "clone {repo} {dir}",
	downloadCmd: "fetch",

	tagCmd:     []tagCmd{{"tag", `^(\S+)$`}},
	tagDefault: "master",
	tagSyncCmd: "checkout {tag}",
}

// vcsBzr describes how to use Bazaar.
var vcsBzr = &vcsCmd{
	name: "Bazaar",
	cmd:  "bzr",

	createCmd:   "branch {repo} {dir}",
	downloadCmd: "pull --overwrite", // TODO: REALLY?

	tagCmd:     []tagCmd{{"tags", `^(\S+)`}},
	tagDefault: "revno:-1",
	tagSyncCmd: "update -r {tag}",
}

// vcsSvn describes how to use Subversion.
var vcsSvn = &vcsCmd{
	name: "Subversion",
	cmd:  "svn",

	createCmd:   "checkout {repo} {dir}",
	downloadCmd: "update",

	// There is no tag command in subversion.
	// The branch information is all in the path names.
}

func (v *vcsCmd) String() string {
	return v.name
}

// run runs the command line cmd in the given directory.
// keyval is a list of key, value pairs.  run expands
// instances of {key} in cmd into value, but only after
// splitting cmd into individual arguments.
// If an error occurs, run prints the command line and the
// command's combined stdout+stderr to standard error.
// Otherwise run discards the command's output.
func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
	_, err := v.run1(dir, false, cmd, keyval)
	return err
}

// runOutput is like run but returns the output of the command.
func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
	return v.run1(dir, true, cmd, keyval)
}

// run1 is the generalized implementation of run and runOutput.
func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string) ([]byte, error) {
	m := make(map[string]string)
	for i := 0; i < len(keyval); i += 2 {
		m[keyval[i]] = keyval[i+1]
	}
	args := strings.Fields(cmdline)
	for i, arg := range args {
		args[i] = expand(m, arg)
	}

	cmd := exec.Command(v.cmd, args...)
	cmd.Dir = dir
	if buildX {
		fmt.Printf("cd %s\n", dir)
		fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " "))
	}
	var buf bytes.Buffer
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	out := buf.Bytes()
	err := cmd.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
		os.Stderr.Write(out)
		return nil, err
	}
	return out, nil
}

// create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
func (v *vcsCmd) create(dir, repo string) error {
	return v.run(".", v.createCmd, "dir", dir, "repo", repo)
}

// download downloads any new changes for the repo in dir.
func (v *vcsCmd) download(dir string) error {
	return v.run(dir, v.downloadCmd)
}

// tags returns the list of available tags for the repo in dir.
func (v *vcsCmd) tags(dir string) ([]string, error) {
	var tags []string
	for _, tc := range v.tagCmd {
		out, err := v.runOutput(dir, tc.cmd)
		if err != nil {
			return nil, err
		}
		re := regexp.MustCompile(`(?m-s)` + tc.pattern)
		tags = append(tags, re.FindAllString(string(out), -1)...)
	}
	return tags, nil
}

// tagSync syncs the repo in dir to the named tag,
// which either is a tag returned by tags or is v.tagDefault.
func (v *vcsCmd) tagSync(dir, tag string) error {
	if v.tagSyncCmd == "" {
		return nil
	}
	return v.run(dir, v.tagSyncCmd, "tag", tag)
}

// A vcsPath is describes how to convert an import path into a
// version control system and repository name.
type vcsPath struct {
	prefix string                              // prefix this description applies to
	re     string                              // pattern for import path
	repo   string                              // repository to use (expand with match of re)
	vcs    string                              // version control system to use (expand with match of re)
	check  func(match map[string]string) error // additional checks

	regexp *regexp.Regexp // cached compiled form of re
}

// vcsForImportPath analyzes importPath to determine the
// version control system, and code repository to use.
// On return, repo is the repository URL and root is the
// import path corresponding to the root of the repository
// (thus root is a prefix of importPath).
func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err error) {
	for _, srv := range vcsPaths {
		if !strings.HasPrefix(importPath, srv.prefix) {
			continue
		}
		m := srv.regexp.FindStringSubmatch(importPath)
		if m == nil {
			if srv.prefix != "" {
				return nil, "", "", fmt.Errorf("invalid %s import path %q", srv.prefix, importPath)
			}
			continue
		}

		// Build map of named subexpression matches for expand.
		match := map[string]string{
			"prefix": srv.prefix,
			"import": importPath,
		}
		for i, name := range srv.regexp.SubexpNames() {
			if name != "" && match[name] == "" {
				match[name] = m[i]
			}
		}
		if srv.vcs != "" {
			match["vcs"] = expand(match, srv.vcs)
		}
		if srv.repo != "" {
			match["repo"] = expand(match, srv.repo)
		}
		if srv.check != nil {
			if err := srv.check(match); err != nil {
				return nil, "", "", err
			}
		}
		vcs := vcsByCmd(match["vcs"])
		if vcs == nil {
			return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"])
		}
		return vcs, match["repo"], match["root"], nil
	}
	return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath)
}

// expand rewrites s to replace {k} with match[k] for each key k in match.
func expand(match map[string]string, s string) string {
	for k, v := range match {
		s = strings.Replace(s, "{"+k+"}", v, -1)
	}
	return s
}

// vcsPaths lists the known vcs paths.
var vcsPaths = []*vcsPath{
	// Google Code - new syntax
	{
		prefix: "code.google.com/",
		re:     `^(?P<root>code\.google\.com/p/(?P<project>[a-z0-9\-]+)(\.(?P<subrepo>[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  googleCodeVCS,
	},

	// Google Code - old syntax
	{
		re:    `^(?P<project>[a-z0-9_\-.]+)\.googlecode\.com/(git|hg|svn)(?P<path>/.*)?$`,
		check: oldGoogleCode,
	},

	// Github
	{
		prefix: "github.com/",
		re:     `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  noVCSSuffix,
	},

	// Bitbucket
	{
		prefix: "bitbucket.org/",
		re:     `^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  bitbucketVCS,
	},

	// Launchpad
	{
		prefix: "launchpad.net/",
		re:     `^(?P<root>launchpad\.net/([A-Za-z0-9_.\-]+(/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		vcs:    "bzr",
		repo:   "https://{root}",
	},

	// General syntax for any server.
	{
		re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
	},
}

func init() {
	// fill in cached regexps.
	// Doing this eagerly discovers invalid regexp syntax
	// without having to run a command that needs that regexp.
	for _, srv := range vcsPaths {
		srv.regexp = regexp.MustCompile(srv.re)
	}
}

// noVCSSuffix checks that the repository name does not
// end in .foo for any version control system foo.
// The usual culprit is ".git".
func noVCSSuffix(match map[string]string) error {
	repo := match["repo"]
	for _, vcs := range vcsList {
		if strings.HasSuffix(repo, "."+vcs.cmd) {
			return fmt.Errorf("invalid version control suffix in %s path", match["prefix"])
		}
	}
	return nil
}

var googleCheckout = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)

// googleCodeVCS determines the version control system for
// a code.google.com repository, by scraping the project's
// /source/checkout page.
func googleCodeVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}
	data, err := httpGET(expand(match, "https://code.google.com/p/{project}/source/checkout?repo={subrepo}"))
	if err != nil {
		return err
	}

	if m := googleCheckout.FindSubmatch(data); m != nil {
		if vcs := vcsByCmd(string(m[1])); vcs != nil {
			// Subversion requires the old URLs.
			// TODO: Test.
			if vcs == vcsSvn {
				if match["subrepo"] != "" {
					return fmt.Errorf("sub-repositories not supported in Google Code Subversion projects")
				}
				match["repo"] = expand(match, "https://{project}.googlecode.com/svn")
			}
			match["vcs"] = vcs.cmd
			return nil
		}
	}

	return fmt.Errorf("unable to detect version control system for code.google.com/ path")
}

// oldGoogleCode is invoked for old-style foo.googlecode.com paths.
// It prints an error giving the equivalent new path.
func oldGoogleCode(match map[string]string) error {
	return fmt.Errorf("invalid Google Code import path: use %s instead",
		expand(match, "code.google.com/p/{project}{path}"))
}

// bitbucketVCS determines the version control system for a
// BitBucket repository, by using the BitBucket API.
func bitbucketVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}

	var resp struct {
		SCM string `json:"scm"`
	}
	url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}")
	data, err := httpGET(url)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return fmt.Errorf("decoding %s: %v", url, err)
	}

	if vcsByCmd(resp.SCM) != nil {
		match["vcs"] = resp.SCM
		if resp.SCM == "git" {
			match["repo"] += ".git"
		}
		return nil
	}

	return fmt.Errorf("unable to detect version control system for bitbucket.org/ path")
}
<commit_msg>cmd/go: fix get github<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

// A vcsCmd describes how to use a version control system
// like Mercurial, Git, or Subversion.
type vcsCmd struct {
	name string
	cmd  string // name of binary to invoke command

	createCmd   string // command to download a fresh copy of a repository
	downloadCmd string // command to download updates into an existing repository

	tagCmd     []tagCmd // commands to list tags
	tagDefault string   // default tag to use
	tagSyncCmd string   // command to sync to specific tag
}

// A tagCmd describes a command to list available tags
// that can be passed to tagSyncCmd.
type tagCmd struct {
	cmd     string // command to list tags
	pattern string // regexp to extract tags from list
}

// vcsList lists the known version control systems
var vcsList = []*vcsCmd{
	vcsHg,
	vcsGit,
	vcsSvn,
	vcsBzr,
}

// vcsByCmd returns the version control system for the given
// command name (hg, git, svn, bzr).
func vcsByCmd(cmd string) *vcsCmd {
	for _, vcs := range vcsList {
		if vcs.cmd == cmd {
			return vcs
		}
	}
	return nil
}

// vcsHg describes how to use Mercurial.
var vcsHg = &vcsCmd{
	name: "Mercurial",
	cmd:  "hg",

	createCmd:   "clone -U {repo} {dir}",
	downloadCmd: "pull",

	// We allow both tag and branch names as 'tags'
	// for selecting a version.  This lets people have
	// a go.release.r60 branch and a go.1 branch
	// and make changes in both, without constantly
	// editing .hgtags.
	tagCmd: []tagCmd{
		{"tags", `^(\S+)`},
		{"branches", `^(\S+)`},
	},
	tagDefault: "default",
	tagSyncCmd: "update -r {tag}",
}

// vcsGit describes how to use Git.
var vcsGit = &vcsCmd{
	name: "Git",
	cmd:  "git",

	createCmd:   "clone {repo} {dir}",
	downloadCmd: "fetch",

	tagCmd:     []tagCmd{{"tag", `^(\S+)$`}},
	tagDefault: "master",
	tagSyncCmd: "checkout {tag}",
}

// vcsBzr describes how to use Bazaar.
var vcsBzr = &vcsCmd{
	name: "Bazaar",
	cmd:  "bzr",

	createCmd:   "branch {repo} {dir}",
	downloadCmd: "pull --overwrite", // TODO: REALLY?

	tagCmd:     []tagCmd{{"tags", `^(\S+)`}},
	tagDefault: "revno:-1",
	tagSyncCmd: "update -r {tag}",
}

// vcsSvn describes how to use Subversion.
var vcsSvn = &vcsCmd{
	name: "Subversion",
	cmd:  "svn",

	createCmd:   "checkout {repo} {dir}",
	downloadCmd: "update",

	// There is no tag command in subversion.
	// The branch information is all in the path names.
}

func (v *vcsCmd) String() string {
	return v.name
}

// run runs the command line cmd in the given directory.
// keyval is a list of key, value pairs.  run expands
// instances of {key} in cmd into value, but only after
// splitting cmd into individual arguments.
// If an error occurs, run prints the command line and the
// command's combined stdout+stderr to standard error.
// Otherwise run discards the command's output.
func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
	_, err := v.run1(dir, false, cmd, keyval)
	return err
}

// runOutput is like run but returns the output of the command.
func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
	return v.run1(dir, true, cmd, keyval)
}

// run1 is the generalized implementation of run and runOutput.
func (v *vcsCmd) run1(dir string, output bool, cmdline string, keyval []string) ([]byte, error) {
	m := make(map[string]string)
	for i := 0; i < len(keyval); i += 2 {
		m[keyval[i]] = keyval[i+1]
	}
	args := strings.Fields(cmdline)
	for i, arg := range args {
		args[i] = expand(m, arg)
	}

	cmd := exec.Command(v.cmd, args...)
	cmd.Dir = dir
	if buildX {
		fmt.Printf("cd %s\n", dir)
		fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " "))
	}
	var buf bytes.Buffer
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	out := buf.Bytes()
	err := cmd.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
		os.Stderr.Write(out)
		return nil, err
	}
	return out, nil
}

// create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
func (v *vcsCmd) create(dir, repo string) error {
	return v.run(".", v.createCmd, "dir", dir, "repo", repo)
}

// download downloads any new changes for the repo in dir.
func (v *vcsCmd) download(dir string) error {
	return v.run(dir, v.downloadCmd)
}

// tags returns the list of available tags for the repo in dir.
func (v *vcsCmd) tags(dir string) ([]string, error) {
	var tags []string
	for _, tc := range v.tagCmd {
		out, err := v.runOutput(dir, tc.cmd)
		if err != nil {
			return nil, err
		}
		re := regexp.MustCompile(`(?m-s)` + tc.pattern)
		tags = append(tags, re.FindAllString(string(out), -1)...)
	}
	return tags, nil
}

// tagSync syncs the repo in dir to the named tag,
// which either is a tag returned by tags or is v.tagDefault.
func (v *vcsCmd) tagSync(dir, tag string) error {
	if v.tagSyncCmd == "" {
		return nil
	}
	return v.run(dir, v.tagSyncCmd, "tag", tag)
}

// A vcsPath is describes how to convert an import path into a
// version control system and repository name.
type vcsPath struct {
	prefix string                              // prefix this description applies to
	re     string                              // pattern for import path
	repo   string                              // repository to use (expand with match of re)
	vcs    string                              // version control system to use (expand with match of re)
	check  func(match map[string]string) error // additional checks

	regexp *regexp.Regexp // cached compiled form of re
}

// vcsForImportPath analyzes importPath to determine the
// version control system, and code repository to use.
// On return, repo is the repository URL and root is the
// import path corresponding to the root of the repository
// (thus root is a prefix of importPath).
func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err error) {
	for _, srv := range vcsPaths {
		if !strings.HasPrefix(importPath, srv.prefix) {
			continue
		}
		m := srv.regexp.FindStringSubmatch(importPath)
		if m == nil {
			if srv.prefix != "" {
				return nil, "", "", fmt.Errorf("invalid %s import path %q", srv.prefix, importPath)
			}
			continue
		}

		// Build map of named subexpression matches for expand.
		match := map[string]string{
			"prefix": srv.prefix,
			"import": importPath,
		}
		for i, name := range srv.regexp.SubexpNames() {
			if name != "" && match[name] == "" {
				match[name] = m[i]
			}
		}
		if srv.vcs != "" {
			match["vcs"] = expand(match, srv.vcs)
		}
		if srv.repo != "" {
			match["repo"] = expand(match, srv.repo)
		}
		if srv.check != nil {
			if err := srv.check(match); err != nil {
				return nil, "", "", err
			}
		}
		vcs := vcsByCmd(match["vcs"])
		if vcs == nil {
			return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"])
		}
		return vcs, match["repo"], match["root"], nil
	}
	return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath)
}

// expand rewrites s to replace {k} with match[k] for each key k in match.
func expand(match map[string]string, s string) string {
	for k, v := range match {
		s = strings.Replace(s, "{"+k+"}", v, -1)
	}
	return s
}

// vcsPaths lists the known vcs paths.
var vcsPaths = []*vcsPath{
	// Google Code - new syntax
	{
		prefix: "code.google.com/",
		re:     `^(?P<root>code\.google\.com/p/(?P<project>[a-z0-9\-]+)(\.(?P<subrepo>[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  googleCodeVCS,
	},

	// Google Code - old syntax
	{
		re:    `^(?P<project>[a-z0-9_\-.]+)\.googlecode\.com/(git|hg|svn)(?P<path>/.*)?$`,
		check: oldGoogleCode,
	},

	// Github
	{
		prefix: "github.com/",
		re:     `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
		vcs:    "git",
		repo:   "https://{root}",
		check:  noVCSSuffix,
	},

	// Bitbucket
	{
		prefix: "bitbucket.org/",
		re:     `^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		repo:   "https://{root}",
		check:  bitbucketVCS,
	},

	// Launchpad
	{
		prefix: "launchpad.net/",
		re:     `^(?P<root>launchpad\.net/([A-Za-z0-9_.\-]+(/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
		vcs:    "bzr",
		repo:   "https://{root}",
	},

	// General syntax for any server.
	{
		re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
	},
}

func init() {
	// fill in cached regexps.
	// Doing this eagerly discovers invalid regexp syntax
	// without having to run a command that needs that regexp.
	for _, srv := range vcsPaths {
		srv.regexp = regexp.MustCompile(srv.re)
	}
}

// noVCSSuffix checks that the repository name does not
// end in .foo for any version control system foo.
// The usual culprit is ".git".
func noVCSSuffix(match map[string]string) error {
	repo := match["repo"]
	for _, vcs := range vcsList {
		if strings.HasSuffix(repo, "."+vcs.cmd) {
			return fmt.Errorf("invalid version control suffix in %s path", match["prefix"])
		}
	}
	return nil
}

var googleCheckout = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)

// googleCodeVCS determines the version control system for
// a code.google.com repository, by scraping the project's
// /source/checkout page.
func googleCodeVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}
	data, err := httpGET(expand(match, "https://code.google.com/p/{project}/source/checkout?repo={subrepo}"))
	if err != nil {
		return err
	}

	if m := googleCheckout.FindSubmatch(data); m != nil {
		if vcs := vcsByCmd(string(m[1])); vcs != nil {
			// Subversion requires the old URLs.
			// TODO: Test.
			if vcs == vcsSvn {
				if match["subrepo"] != "" {
					return fmt.Errorf("sub-repositories not supported in Google Code Subversion projects")
				}
				match["repo"] = expand(match, "https://{project}.googlecode.com/svn")
			}
			match["vcs"] = vcs.cmd
			return nil
		}
	}

	return fmt.Errorf("unable to detect version control system for code.google.com/ path")
}

// oldGoogleCode is invoked for old-style foo.googlecode.com paths.
// It prints an error giving the equivalent new path.
func oldGoogleCode(match map[string]string) error {
	return fmt.Errorf("invalid Google Code import path: use %s instead",
		expand(match, "code.google.com/p/{project}{path}"))
}

// bitbucketVCS determines the version control system for a
// BitBucket repository, by using the BitBucket API.
func bitbucketVCS(match map[string]string) error {
	if err := noVCSSuffix(match); err != nil {
		return err
	}

	var resp struct {
		SCM string `json:"scm"`
	}
	url := expand(match, "https://api.bitbucket.org/1.0/repositories/{bitname}")
	data, err := httpGET(url)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return fmt.Errorf("decoding %s: %v", url, err)
	}

	if vcsByCmd(resp.SCM) != nil {
		match["vcs"] = resp.SCM
		if resp.SCM == "git" {
			match["repo"] += ".git"
		}
		return nil
	}

	return fmt.Errorf("unable to detect version control system for bitbucket.org/ path")
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"os"
	"strconv"

	"github.com/codegangsta/cli"
	"github.com/concourse/atc"
	atcroutes "github.com/concourse/atc/web/routes"
	"github.com/concourse/fly/template"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/rata"
	"gopkg.in/yaml.v2"
)

func Configure(c *cli.Context) {
	target := returnTarget(c.GlobalString("target"))
	insecure := c.GlobalBool("insecure")
	configPath := c.String("config")
	paused := c.String("paused")
	asJSON := c.Bool("json")
	templateVariables := c.StringSlice("var")
	templateVariablesFile := c.StringSlice("vars-from")
	pipelineName := c.Args().First()

	if pipelineName == "" {
		pipelineName = atc.DefaultPipelineName
	}

	apiRequester := newAtcRequester(target, insecure)
	webRequestGenerator := rata.NewRequestGenerator(target, atcroutes.Routes)

	atcConfig := ATCConfig{
		pipelineName:        pipelineName,
		apiRequester:        apiRequester,
		webRequestGenerator: webRequestGenerator,
	}

	if configPath == "" {
		atcConfig.DumpConfig(asJSON)
	} else {
		atcConfig.SetConfig(paused, configPath, templateVariables, templateVariablesFile)
	}
}

type ATCConfig struct {
	pipelineName        string
	apiRequester        *atcRequester
	webRequestGenerator *rata.RequestGenerator
}

func (atcConfig ATCConfig) DumpConfig(asJSON bool) {
	config := getConfig(atcConfig.pipelineName, atcConfig.apiRequester)

	var payload []byte
	var err error
	if asJSON {
		payload, err = json.Marshal(config)
	} else {
		payload, err = yaml.Marshal(config)
	}

	if err != nil {
		log.Println("failed to marshal config to YAML:", err)
		os.Exit(1)
	}

	fmt.Printf("%s", payload)
}

type PipelineAction int

const (
	PausePipeline PipelineAction = iota
	UnpausePipeline
	DoNotChangePipeline
)

func (atcConfig ATCConfig) shouldPausePipeline(pausedFlag string) PipelineAction {
	if pausedFlag == "" {
		return DoNotChangePipeline
	}

	p, err := strconv.ParseBool(pausedFlag)
	if err != nil {
		failf("paused value '%s' is not a boolean\n", pausedFlag)
	}

	if p {
		return PausePipeline
	} else {
		return UnpausePipeline
	}
}

func failf(message string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, message+"\n", args...)
	os.Exit(1)
}

func failWithErrorf(message string, err error) {
	failf(message + ": " + err.Error())
}

func (atcConfig ATCConfig) SetConfig(pausedFlag string, configPath string, templateVariables []string, templateVariablesFile []string) {
	paused := atcConfig.shouldPausePipeline(pausedFlag)

	newConfig, newRawConfig := atcConfig.newConfig(configPath, templateVariablesFile, templateVariables)
	existingConfig, existingConfigVersion := atcConfig.existingConfig()

	diff(existingConfig, newConfig)

	resp := atcConfig.submitConfig(newRawConfig, paused, existingConfigVersion)
	atcConfig.showHelpfulMessage(resp, paused)
}

func (atcConfig ATCConfig) newConfig(configPath string, templateVariablesFiles []string, templateVariables []string) (atc.Config, []byte) {
	configFile, err := ioutil.ReadFile(configPath)
	if err != nil {
		failWithErrorf("could not read config file", err)
	}

	var resultVars template.Variables

	for _, path := range templateVariablesFiles {
		fileVars, err := template.LoadVariablesFromFile(path)
		if err != nil {
			log.Fatalln(err)
		}

		resultVars = resultVars.Merge(fileVars)
	}

	vars, err := template.LoadVariables(templateVariables)
	if err != nil {
		log.Fatalln(err)
	}

	resultVars = resultVars.Merge(vars)

	configFile, err = template.Evaluate(configFile, resultVars)
	if err != nil {
		log.Fatalln(err)
	}

	var newConfig atc.Config
	err = yaml.Unmarshal(configFile, &newConfig)
	if err != nil {
		log.Fatalln(err)
	}

	return newConfig, configFile
}

func (atcConfig ATCConfig) existingConfig() (atc.Config, string) {
	getConfig, err := atcConfig.apiRequester.CreateRequest(
		atc.GetConfig,
		rata.Params{"pipeline_name": atcConfig.pipelineName},
		nil,
	)
	if err != nil {
		log.Fatalln(err)
	}

	resp, err := atcConfig.apiRequester.httpClient.Do(getConfig)
	if err != nil {
		log.Println("failed to get config:", err, resp)
		os.Exit(1)
	}

	if resp.StatusCode != http.StatusOK {
		log.Println("bad response when getting config:", resp.Status)
		os.Exit(1)
	}

	version := resp.Header.Get(atc.ConfigVersionHeader)

	var existingConfig atc.Config
	err = json.NewDecoder(resp.Body).Decode(&existingConfig)
	if err != nil {
		log.Println("invalid config from server:", err)
		os.Exit(1)
	}

	return existingConfig, version
}

func (atcConfig ATCConfig) submitConfig(configFile []byte, paused PipelineAction, existingConfigVersion string) *http.Response {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	yamlWriter, err := writer.CreatePart(
		textproto.MIMEHeader{
			"Content-type": {"application/x-yaml"},
		},
	)

	if err != nil {
		log.Println("error building request:", err)
		os.Exit(1)
	}

	_, err = yamlWriter.Write(configFile)
	if err != nil {
		log.Println("error building request:", err)
		os.Exit(1)
	}

	switch paused {
	case PausePipeline:
		err = writer.WriteField("paused", "true")
	case UnpausePipeline:
		err = writer.WriteField("paused", "false")
	}

	if err != nil {
		log.Fatalln("failed to write field:", err)
	}

	writer.Close()

	setConfig, err := atcConfig.apiRequester.CreateRequest(
		atc.SaveConfig,
		rata.Params{"pipeline_name": atcConfig.pipelineName},
		body,
	)
	if err != nil {
		log.Fatalln("failed to set config:", err)
	}

	setConfig.Header.Set("Content-Type", writer.FormDataContentType())
	setConfig.Header.Set(atc.ConfigVersionHeader, existingConfigVersion)

	resp, err := atcConfig.apiRequester.httpClient.Do(setConfig)
	if err != nil {
		println("failed to update configuration: " + err.Error())
		os.Exit(1)
	}

	return resp
}

func (atcConfig ATCConfig) showHelpfulMessage(resp *http.Response, paused PipelineAction) {
	defer resp.Body.Close()

	switch resp.StatusCode {
	case http.StatusOK:
		fmt.Println("configuration updated")
	case http.StatusCreated:
		pipelineWebReq, _ := atcConfig.webRequestGenerator.CreateRequest(
			atcroutes.Pipeline,
			rata.Params{"pipeline_name": atcConfig.pipelineName},
			nil,
		)

		fmt.Println("pipeline created!")

		pipelineURL := pipelineWebReq.URL
		// don't show username and password
		pipelineURL.User = nil

		fmt.Printf("you can view your pipeline here: %s\n", pipelineURL.String())

		if paused == DoNotChangePipeline || paused == PausePipeline {
			fmt.Println("")
			fmt.Println("the pipeline is currently paused. to unpause, either:")
			fmt.Println("  - run again with --paused=false")
			fmt.Println("  - click play next to the pipeline in the web ui")
		}
	default:
		fmt.Fprintln(os.Stderr, "failed to update configuration.")

		indent := gexec.NewPrefixedWriter("  ", os.Stderr)
		fmt.Fprintf(indent, "response code: %s\n", resp.Status)
		fmt.Fprintf(indent, "response body:\n")

		indentAgain := gexec.NewPrefixedWriter("  ", indent)
		io.Copy(indentAgain, resp.Body)
		os.Exit(1)
	}
}

func diff(existingConfig atc.Config, newConfig atc.Config) {
	indent := gexec.NewPrefixedWriter("  ", os.Stdout)

	groupDiffs := diffIndices(GroupIndex(existingConfig.Groups), GroupIndex(newConfig.Groups))
	if len(groupDiffs) > 0 {
		fmt.Println("groups:")

		for _, diff := range groupDiffs {
			diff.WriteTo(indent, "group")
		}
	}

	resourceDiffs := diffIndices(ResourceIndex(existingConfig.Resources), ResourceIndex(newConfig.Resources))
	if len(resourceDiffs) > 0 {
		fmt.Println("resources:")

		for _, diff := range resourceDiffs {
			diff.WriteTo(indent, "resource")
		}
	}

	jobDiffs := diffIndices(JobIndex(existingConfig.Jobs), JobIndex(newConfig.Jobs))
	if len(jobDiffs) > 0 {
		fmt.Println("jobs:")

		for _, diff := range jobDiffs {
			diff.WriteTo(indent, "job")
		}
	}

	if !askToConfirm("apply configuration?") {
		println("bailing out")
		os.Exit(1)
	}
}
<commit_msg>unify error message style in config<commit_after>package commands

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"os"
	"strconv"

	"github.com/codegangsta/cli"
	"github.com/concourse/atc"
	atcroutes "github.com/concourse/atc/web/routes"
	"github.com/concourse/fly/template"
	"github.com/onsi/gomega/gexec"
	"github.com/tedsuo/rata"
	"gopkg.in/yaml.v2"
)

func Configure(c *cli.Context) {
	target := returnTarget(c.GlobalString("target"))
	insecure := c.GlobalBool("insecure")
	configPath := c.String("config")
	paused := c.String("paused")
	asJSON := c.Bool("json")
	templateVariables := c.StringSlice("var")
	templateVariablesFile := c.StringSlice("vars-from")
	pipelineName := c.Args().First()

	if pipelineName == "" {
		pipelineName = atc.DefaultPipelineName
	}

	apiRequester := newAtcRequester(target, insecure)
	webRequestGenerator := rata.NewRequestGenerator(target, atcroutes.Routes)

	atcConfig := ATCConfig{
		pipelineName:        pipelineName,
		apiRequester:        apiRequester,
		webRequestGenerator: webRequestGenerator,
	}

	if configPath == "" {
		atcConfig.DumpConfig(asJSON)
	} else {
		atcConfig.SetConfig(paused, configPath, templateVariables, templateVariablesFile)
	}
}

type ATCConfig struct {
	pipelineName        string
	apiRequester        *atcRequester
	webRequestGenerator *rata.RequestGenerator
}

func (atcConfig ATCConfig) DumpConfig(asJSON bool) {
	config := getConfig(atcConfig.pipelineName, atcConfig.apiRequester)

	var payload []byte
	var err error
	if asJSON {
		payload, err = json.Marshal(config)
	} else {
		payload, err = yaml.Marshal(config)
	}

	if err != nil {
		log.Println("failed to marshal config to YAML:", err)
		os.Exit(1)
	}

	fmt.Printf("%s", payload)
}

type PipelineAction int

const (
	PausePipeline PipelineAction = iota
	UnpausePipeline
	DoNotChangePipeline
)

func (atcConfig ATCConfig) shouldPausePipeline(pausedFlag string) PipelineAction {
	if pausedFlag == "" {
		return DoNotChangePipeline
	}

	p, err := strconv.ParseBool(pausedFlag)
	if err != nil {
		failf("paused value '%s' is not a boolean\n", pausedFlag)
	}

	if p {
		return PausePipeline
	} else {
		return UnpausePipeline
	}
}

func failf(message string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, message+"\n", args...)
	os.Exit(1)
}

func failWithErrorf(message string, err error, args ...interface{}) {
	templatedMessage := fmt.Sprintf(message, args...)
	failf(templatedMessage + ": " + err.Error())
}

func (atcConfig ATCConfig) SetConfig(pausedFlag string, configPath string, templateVariables []string, templateVariablesFile []string) {
	paused := atcConfig.shouldPausePipeline(pausedFlag)

	newConfig, newRawConfig := atcConfig.newConfig(configPath, templateVariablesFile, templateVariables)
	existingConfig, existingConfigVersion := atcConfig.existingConfig()

	diff(existingConfig, newConfig)

	resp := atcConfig.submitConfig(newRawConfig, paused, existingConfigVersion)
	atcConfig.showHelpfulMessage(resp, paused)
}

func (atcConfig ATCConfig) newConfig(configPath string, templateVariablesFiles []string, templateVariables []string) (atc.Config, []byte) {
	configFile, err := ioutil.ReadFile(configPath)
	if err != nil {
		failWithErrorf("could not read config file", err)
	}

	var resultVars template.Variables

	for _, path := range templateVariablesFiles {
		fileVars, err := template.LoadVariablesFromFile(path)
		if err != nil {
			failWithErrorf("failed to load variables from file (%s)", err, path)
		}

		resultVars = resultVars.Merge(fileVars)
	}

	vars, err := template.LoadVariables(templateVariables)
	if err != nil {
		failWithErrorf("could not load template variables", err)
	}

	resultVars = resultVars.Merge(vars)

	configFile, err = template.Evaluate(configFile, resultVars)
	if err != nil {
		failWithErrorf("failed to evaluate variables into template", err)
	}

	var newConfig atc.Config
	err = yaml.Unmarshal(configFile, &newConfig)
	if err != nil {
		failWithErrorf("failed to parse configuration file", err)
	}

	return newConfig, configFile
}

func (atcConfig ATCConfig) existingConfig() (atc.Config, string) {
	getConfig, err := atcConfig.apiRequester.CreateRequest(
		atc.GetConfig,
		rata.Params{"pipeline_name": atcConfig.pipelineName},
		nil,
	)
	if err != nil {
		failWithErrorf("failed to build request", err)
	}

	resp, err := atcConfig.apiRequester.httpClient.Do(getConfig)
	if err != nil {
		failWithErrorf("failed to retrieve current configuration", err)
	}

	if resp.StatusCode != http.StatusOK {
		failWithErrorf("bad response when getting config", errors.New(resp.Status))
	}

	version := resp.Header.Get(atc.ConfigVersionHeader)

	var existingConfig atc.Config
	err = json.NewDecoder(resp.Body).Decode(&existingConfig)
	if err != nil {
		failWithErrorf("invalid configuration from server", err)
	}

	return existingConfig, version
}

func (atcConfig ATCConfig) submitConfig(configFile []byte, paused PipelineAction, existingConfigVersion string) *http.Response {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	yamlWriter, err := writer.CreatePart(
		textproto.MIMEHeader{
			"Content-type": {"application/x-yaml"},
		},
	)
	if err != nil {
		failWithErrorf("error building request", err)
	}

	_, err = yamlWriter.Write(configFile)
	if err != nil {
		failWithErrorf("error building request", err)
	}

	switch paused {
	case PausePipeline:
		err = writer.WriteField("paused", "true")
	case UnpausePipeline:
		err = writer.WriteField("paused", "false")
	}
	if err != nil {
		failWithErrorf("error building request", err)
	}

	writer.Close()

	setConfig, err := atcConfig.apiRequester.CreateRequest(
		atc.SaveConfig,
		rata.Params{"pipeline_name": atcConfig.pipelineName},
		body,
	)
	if err != nil {
		failWithErrorf("failed to build set config request", err)
	}

	setConfig.Header.Set("Content-Type", writer.FormDataContentType())
	setConfig.Header.Set(atc.ConfigVersionHeader, existingConfigVersion)

	resp, err := atcConfig.apiRequester.httpClient.Do(setConfig)
	if err != nil {
		failWithErrorf("failed to update configuration", err)
	}

	return resp
}

func (atcConfig ATCConfig) showHelpfulMessage(resp *http.Response, paused PipelineAction) {
	defer resp.Body.Close()

	switch resp.StatusCode {
	case http.StatusOK:
		fmt.Println("configuration updated")
	case http.StatusCreated:
		pipelineWebReq, _ := atcConfig.webRequestGenerator.CreateRequest(
			atcroutes.Pipeline,
			rata.Params{"pipeline_name": atcConfig.pipelineName},
			nil,
		)

		fmt.Println("pipeline created!")

		pipelineURL := pipelineWebReq.URL
		// don't show username and password
		pipelineURL.User = nil

		fmt.Printf("you can view your pipeline here: %s\n", pipelineURL.String())

		if paused == DoNotChangePipeline || paused == PausePipeline {
			fmt.Println("")
			fmt.Println("the pipeline is currently paused. to unpause, either:")
			fmt.Println("  - run again with --paused=false")
			fmt.Println("  - click play next to the pipeline in the web ui")
		}
	default:
		fmt.Fprintln(os.Stderr, "failed to update configuration.")

		indent := gexec.NewPrefixedWriter("  ", os.Stderr)
		fmt.Fprintf(indent, "response code: %s\n", resp.Status)
		fmt.Fprintf(indent, "response body:\n")

		indentAgain := gexec.NewPrefixedWriter("  ", indent)
		io.Copy(indentAgain, resp.Body)
		os.Exit(1)
	}
}

func diff(existingConfig atc.Config, newConfig atc.Config) {
	indent := gexec.NewPrefixedWriter("  ", os.Stdout)

	groupDiffs := diffIndices(GroupIndex(existingConfig.Groups), GroupIndex(newConfig.Groups))
	if len(groupDiffs) > 0 {
		fmt.Println("groups:")

		for _, diff := range groupDiffs {
			diff.WriteTo(indent, "group")
		}
	}

	resourceDiffs := diffIndices(ResourceIndex(existingConfig.Resources), ResourceIndex(newConfig.Resources))
	if len(resourceDiffs) > 0 {
		fmt.Println("resources:")

		for _, diff := range resourceDiffs {
			diff.WriteTo(indent, "resource")
		}
	}

	jobDiffs := diffIndices(JobIndex(existingConfig.Jobs), JobIndex(newConfig.Jobs))
	if len(jobDiffs) > 0 {
		fmt.Println("jobs:")

		for _, diff := range jobDiffs {
			diff.WriteTo(indent, "job")
		}
	}

	if !askToConfirm("apply configuration?") {
		println("bailing out")
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package execCmd

import (
	"os/exec"
	"fmt"
	_ "log"
	"strings"
	"ircflu/commands"
	"ircflu/msgsystem"
)

type ExecCommand struct {
	messagesIn chan msgsystem.Message
	messagesOut chan msgsystem.Message
}

func (h *ExecCommand) Name() string {
	return "exec"
}

func (h *ExecCommand) MessageInChan() chan msgsystem.Message {
	return h.messagesIn
}

func (h *ExecCommand) SetMessageInChan(channel chan msgsystem.Message) {
	h.messagesIn = channel
}

func (h *ExecCommand) MessageOutChan() chan msgsystem.Message {
	return h.messagesOut
}

func (h *ExecCommand) SetMessageOutChan(channel chan msgsystem.Message) {
	h.messagesOut = channel
}

func (h *ExecCommand) Parse(msg msgsystem.Message) bool {
	channel := msg.To
	m := strings.Split(msg.Msg, " ")
	cmd := m[0]
	params := strings.TrimSpace(strings.Join(m[1:], " "))

	switch cmd {
		case "!exec":
			if strings.Index(params, "rm ") >= 0 || strings.Index(params, "mv ") >= 0 {
				r := msgsystem.Message{
					To: channel,
					Msg: "Security breach. Talk to ircflu admin!",
				}
				h.messagesOut <- r
				return true
			}

			if strings.HasPrefix(channel[0], "#muesli") {
				fmt.Println("Executing:", params)

				c := strings.Split(params, " ")
				e := exec.Command(c[0], c[1:]...)
				out, err := e.CombinedOutput()
				fmt.Println("Output:", string(out))
				fmt.Println("Error:", err)

				r := msgsystem.Message{
					To: channel,
				}
				if err != nil {
					r.Msg = "Command '" + params + "' failed: " + err.Error()
				} else {
					r.Msg = "Command '" + params + "' succeeded!"
				}

				h.messagesOut <- r
			} else {
				r := msgsystem.Message{
					To: channel,
					Msg: "Usage: !exec [command]",
				}
				h.messagesOut <- r
			}

			return true
	}

	return false
}

func init() {
	execCmd := ExecCommand{}
	commands.RegisterCommand(&execCmd)
}
<commit_msg>* ExecCommands requires auth now.<commit_after>package execCmd

import (
	"os/exec"
	"fmt"
	_ "log"
	"strings"
	"ircflu/auth"
	"ircflu/commands"
	"ircflu/msgsystem"
)

type ExecCommand struct {
	messagesIn chan msgsystem.Message
	messagesOut chan msgsystem.Message
}

func (h *ExecCommand) Name() string {
	return "exec"
}

func (h *ExecCommand) MessageInChan() chan msgsystem.Message {
	return h.messagesIn
}

func (h *ExecCommand) SetMessageInChan(channel chan msgsystem.Message) {
	h.messagesIn = channel
}

func (h *ExecCommand) MessageOutChan() chan msgsystem.Message {
	return h.messagesOut
}

func (h *ExecCommand) SetMessageOutChan(channel chan msgsystem.Message) {
	h.messagesOut = channel
}

func (h *ExecCommand) Parse(msg msgsystem.Message) bool {
	channel := msg.To
	m := strings.Split(msg.Msg, " ")
	cmd := m[0]
	params := strings.TrimSpace(strings.Join(m[1:], " "))

	switch cmd {
		case "!exec":
			if !auth.IsAuthed(msg.Source) || strings.Index(params, "rm ") >= 0 || strings.Index(params, "mv ") >= 0 {
				r := msgsystem.Message{
					To: channel,
					Msg: "Security breach. Talk to ircflu admin!",
				}
				h.messagesOut <- r
				return true
			}

			if len(params) > 0 {
				fmt.Println("Executing:", params)

				c := strings.Split(params, " ")
				e := exec.Command(c[0], c[1:]...)
				out, err := e.CombinedOutput()
				fmt.Println("Output:", string(out))
				fmt.Println("Error:", err)

				r := msgsystem.Message{
					To: []string{msg.Source},
				}
				if err != nil {
					r.Msg = "Command '" + params + "' failed: " + err.Error()
				} else {
					r.Msg = "Command '" + params + "' succeeded!"
				}

				h.messagesOut <- r
			} else {
				r := msgsystem.Message{
					To: channel,
					Msg: "Usage: !exec [command]",
				}
				h.messagesOut <- r
			}

			return true
	}

	return false
}

func init() {
	execCmd := ExecCommand{}
	commands.RegisterCommand(&execCmd)
}
<|endoftext|>


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

package clock

import (
	"time"

	"golang.org/x/net/context"
)

// Clock is an interface to system time.
//
// The standard clock is SystemClock, which falls through to the system time
// library. Another clock, FakeClock, is available to simulate time facilities
// for testing.
type Clock interface {
	// Returns the current time (see time.Now).
	Now() time.Time

	// Sleeps the current goroutine (see time.Sleep).
	//
	// Sleep will return a TimerResult containing the time when it was awakened
	// and detailing its execution. If the sleep terminated prematurely from
	// cancellation, the TimerResult's Incomplete() method will return true.
	Sleep(context.Context, time.Duration) TimerResult

	// Creates a new Timer instance, bound to this Clock.
	//
	// If the supplied Context is canceled, the timer will expire immediately.
	NewTimer(c context.Context) Timer

	// Waits a duration, then sends the current time over the returned channel.
	//
	// If the supplied Context is canceled, the timer will expire immediately.
	After(context.Context, time.Duration) <-chan TimerResult
}
<commit_msg>common/clock: add package doc.<commit_after>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Package clock is an interface to system time and timers which is easy to
// test.
package clock

import (
	"time"

	"golang.org/x/net/context"
)

// Clock is an interface to system time.
//
// The standard clock is SystemClock, which falls through to the system time
// library. Another clock, FakeClock, is available to simulate time facilities
// for testing.
type Clock interface {
	// Returns the current time (see time.Now).
	Now() time.Time

	// Sleeps the current goroutine (see time.Sleep).
	//
	// Sleep will return a TimerResult containing the time when it was awakened
	// and detailing its execution. If the sleep terminated prematurely from
	// cancellation, the TimerResult's Incomplete() method will return true.
	Sleep(context.Context, time.Duration) TimerResult

	// Creates a new Timer instance, bound to this Clock.
	//
	// If the supplied Context is canceled, the timer will expire immediately.
	NewTimer(c context.Context) Timer

	// Waits a duration, then sends the current time over the returned channel.
	//
	// If the supplied Context is canceled, the timer will expire immediately.
	After(context.Context, time.Duration) <-chan TimerResult
}
<|endoftext|>


<commit_before>package resource

import (
	"crypto/md5"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"

	"github.com/dnaeon/gru/utils"
)

// BaseFile type is the base type which is embedded by
// File, Directory and Link resources.
type BaseFile struct {
	Base

	// Path to the file. Defaults to the resource name.
	Path string `luar:"-"`

	// Permission bits to set on the file.
	// For regular files defaults to 0644.
	// For directories defaults to 0755.
	Mode os.FileMode `luar:"mode"`

	// Owner of the file. Defaults to the currently running user.
	Owner string `luar:"owner"`

	// Group of the file.
	// Defaults to the group of the currently running user.
	Group string `luar:"group"`
}

// isModeSynced returns a boolean indicating whether the
// permissions of the file managed by the resource are in sync.
func (bf *BaseFile) isModeSynced() (bool, error) {
	dst := utils.NewFileUtil(bf.Path)

	if !dst.Exists() {
		return false, ErrResourceAbsent
	}

	mode, err := dst.Mode()
	if err != nil {
		return false, err
	}

	return mode.Perm() == bf.Mode, nil
}

// setMode sets the permissions on the file managed by the resource.
func (bf *BaseFile) setMode() error {
	dst := utils.NewFileUtil(bf.Path)

	return dst.Chmod(bf.Mode)
}

// isOwnerSynced checks whether the file ownership is correct.
func (bf *BaseFile) isOwnerSynced() (bool, error) {
	dst := utils.NewFileUtil(bf.Path)

	if !dst.Exists() {
		return false, ErrResourceAbsent
	}

	owner, err := dst.Owner()
	if err != nil {
		return false, err
	}

	return owner.User.Username == bf.Owner && owner.Group.Name == bf.Group, nil
}

// setOwner sets the ownership of the file.
func (bf *BaseFile) setOwner() error {
	dst := utils.NewFileUtil(bf.Path)

	return dst.SetOwner(bf.Owner, bf.Group)
}

// File resource manages files.
//
// Example:
//   foo = resource.file.new("/tmp/foo")
//   foo.state = "present"
//   foo.mode = tonumber("0600", 8)
//   foo.owner = "root"
//   foo.group = "wheel"
//   foo.content = "content of file foo"
type File struct {
	BaseFile

	// Content of file to set.
	Content []byte `luar:"content"`

	// Source file to use for the file content.
	Source string `luar:"source"`
}

// isContentSynced checks if the file content is in sync with the
// given content.
func (f *File) isContentSynced() (bool, error) {
	// We don't have a content, assume content is correct
	if f.Content == nil {
		return true, nil
	}

	dst := utils.NewFileUtil(f.Path)
	if !dst.Exists() {
		return false, ErrResourceAbsent
	}

	dstMd5, err := dst.Md5()
	if err != nil {
		return false, err
	}

	srcMd5 := fmt.Sprintf("%x", md5.Sum(f.Content))

	return srcMd5 == dstMd5, nil
}

// setContent sets the content of the file.
func (f *File) setContent() error {
	return ioutil.WriteFile(f.Path, f.Content, f.Mode)
}

// NewFile creates a resource for managing regular files.
func NewFile(name string) (Resource, error) {
	// Defaults for owner and group
	currentUser, err := user.Current()
	if err != nil {
		return nil, err
	}

	currentGroup, err := user.LookupGroupId(currentUser.Gid)
	if err != nil {
		return nil, err
	}

	// Resource defaults
	f := &File{
		BaseFile: BaseFile{
			Base: Base{
				Name:          name,
				Type:          "file",
				State:         "present",
				Require:       make([]string, 0),
				PresentStates: []string{"present"},
				AbsentStates:  []string{"absent"},
				Concurrent:    true,
				Subscribe:     make(TriggerMap),
			},
			Path:  name,
			Mode:  0644,
			Owner: currentUser.Username,
			Group: currentGroup.Name,
		},
		Content: nil,
		Source:  "",
	}

	// Set resource properties
	f.Properties = []Property{
		Property{
			Name:     "mode",
			Set:      f.setMode,
			IsSynced: f.isModeSynced,
		},
		Property{
			Name:     "ownership",
			Set:      f.setOwner,
			IsSynced: f.isOwnerSynced,
		},
		Property{
			Name:     "content",
			Set:      f.setContent,
			IsSynced: f.isContentSynced,
		},
	}

	return f, nil
}

// Validate validates the file resource.
func (f *File) Validate() error {
	if err := f.Base.Validate(); err != nil {
		return err
	}

	if f.Source != "" && f.Content != nil {
		return errors.New("cannot use both 'source' and 'content'")
	}

	return nil
}

// Initialize initializes the file resource.
func (f *File) Initialize() error {
	// Set file content from the given source file if any.
	// TODO: Currenly this works only for files in the site repo.
	// TODO: Implement a generic file content fetcher.
	if f.Source != "" {
		src := filepath.Join(DefaultConfig.SiteRepo, f.Source)
		content, err := ioutil.ReadFile(src)
		if err != nil {
			return err
		}
		f.Content = content
	}

	return nil
}

// Evaluate evaluates the state of the file resource.
func (f *File) Evaluate() (State, error) {
	state := State{
		Current: "unknown",
		Want:    f.State,
	}

	fi, err := os.Stat(f.Path)
	if os.IsNotExist(err) {
		state.Current = "absent"
		return state, nil
	}

	state.Current = "present"

	if !fi.Mode().IsRegular() {
		return state, errors.New("path exists, but is not a regular file")
	}

	return state, nil
}

// Create creates the file managed by the resource.
func (f *File) Create() error {
	return ioutil.WriteFile(f.Path, f.Content, f.Mode)
}

// Delete deletes the file managed by the resource.
func (f *File) Delete() error {
	return os.Remove(f.Path)
}

// Directory resource manages directories.
//
// Example:
//   bar = resource.directory.new("/tmp/bar")
//   bar.state = "present"
//   bar.mode = tonumber("0700", 8)
//   bar.owner = "root"
//   bar.group = "wheel"
type Directory struct {
	BaseFile

	// Parents flag specifies whether or not to create/delete
	// parent directories. Defaults to false.
	Parents bool `luar:"parents"`
}

// NewDirectory creates a resource for managing directories.
func NewDirectory(name string) (Resource, error) {
	// Defaults for owner and group
	currentUser, err := user.Current()
	if err != nil {
		return nil, err
	}

	currentGroup, err := user.LookupGroupId(currentUser.Gid)
	if err != nil {
		return nil, err
	}

	// Resource defaults
	d := &Directory{
		BaseFile: BaseFile{
			Base: Base{
				Name:          name,
				Type:          "directory",
				State:         "present",
				Require:       make([]string, 0),
				PresentStates: []string{"present"},
				AbsentStates:  []string{"absent"},
				Concurrent:    true,
				Subscribe:     make(TriggerMap),
			},
			Path:  name,
			Mode:  0755,
			Owner: currentUser.Username,
			Group: currentGroup.Name,
		},
		Parents: false,
	}

	// Set resource properties
	d.Properties = []Property{
		Property{
			Name:     "mode",
			Set:      d.setMode,
			IsSynced: d.isModeSynced,
		},
		Property{
			Name:     "ownership",
			Set:      d.setOwner,
			IsSynced: d.isOwnerSynced,
		},
	}

	return d, nil
}

// Evaluate evaluates the state of the directory.
func (d *Directory) Evaluate() (State, error) {
	state := State{
		Current: "unknown",
		Want:    d.State,
	}

	fi, err := os.Stat(d.Path)
	if os.IsNotExist(err) {
		state.Current = "absent"
		return state, nil
	}

	state.Current = "present"

	if !fi.Mode().IsDir() {
		return state, errors.New("path exists, but is not a directory")
	}

	return state, nil
}

// Create creates the directory.
func (d *Directory) Create() error {
	if d.Parents {
		return os.MkdirAll(d.Path, d.Mode)
	}

	return os.Mkdir(d.Path, d.Mode)
}

// Delete removes the directory.
func (d *Directory) Delete() error {
	if d.Parents {
		return os.RemoveAll(d.Path)
	}

	return os.Remove(d.Path)
}

// Link resource manages links between files.
//
// Example:
//   baz = resource.link.new("/tmp/baz")
//   baz.state = "present"
//   baz.source = "/tmp/qux"
type Link struct {
	BaseFile

	// Source file points to the file the link will be set to.
	Source string `luar:"source"`

	// Hard flag specifies whether or not to create a hard link to the file.
	// Defaults to false.
	Hard bool `luar:"hard"`
}

// NewLink creates a new resource for managing links between files.
func NewLink(name string) (Resource, error) {
	l := &Link{
		BaseFile: BaseFile{
			Base: Base{
				Name:          name,
				Type:          "link",
				State:         "present",
				Require:       make([]string, 0),
				PresentStates: []string{"present"},
				AbsentStates:  []string{"absent"},
				Concurrent:    true,
				Subscribe:     make(TriggerMap),
			},
			Path: name,
		},
		Source: "",
		Hard:   false,
	}

	return l, nil
}

// Validate validates the link resource.
func (l *Link) Validate() error {
	if l.Source == "" {
		return errors.New("must provide source file")
	}

	src := utils.NewFileUtil(l.Source)
	if !src.Exists() {
		return fmt.Errorf("source file %s does not exist", l.Source)
	}

	return nil
}

// Evaluate evaluates the state of the link.
func (l *Link) Evaluate() (State, error) {
	state := State{
		Current: "unknown",
		Want:    l.State,
	}

	_, err := os.Stat(l.Path)
	if os.IsNotExist(err) {
		state.Current = "absent"
		return state, nil
	}

	state.Current = "present"

	_, err = os.Readlink(l.Path)
	if err != nil {
		return state, fmt.Errorf("path exists, but is not a link: %s\n", err)
	}

	return state, nil
}

// Create creates the link.
func (l *Link) Create() error {
	if l.Hard {
		return os.Link(l.Source, l.Path)
	}

	return os.Symlink(l.Source, l.Path)
}

// Delete removes the link.
func (l *Link) Delete() error {
	return os.Remove(l.Path)
}

func init() {
	file := ProviderItem{
		Type:      "file",
		Provider:  NewFile,
		Namespace: DefaultResourceNamespace,
	}

	dir := ProviderItem{
		Type:      "directory",
		Provider:  NewDirectory,
		Namespace: DefaultResourceNamespace,
	}

	link := ProviderItem{
		Type:      "link",
		Provider:  NewLink,
		Namespace: DefaultResourceNamespace,
	}

	RegisterProvider(file, dir, link)
}
<commit_msg>resource: update File resource properties<commit_after>package resource

import (
	"crypto/md5"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"

	"github.com/dnaeon/gru/utils"
)

// BaseFile type is the base type which is embedded by
// File, Directory and Link resources.
type BaseFile struct {
	Base

	// Path to the file. Defaults to the resource name.
	Path string `luar:"-"`

	// Permission bits to set on the file.
	// For regular files defaults to 0644.
	// For directories defaults to 0755.
	Mode os.FileMode `luar:"mode"`

	// Owner of the file. Defaults to the currently running user.
	Owner string `luar:"owner"`

	// Group of the file.
	// Defaults to the group of the currently running user.
	Group string `luar:"group"`
}

// isModeSynced returns a boolean indicating whether the
// permissions of the file managed by the resource are in sync.
func (bf *BaseFile) isModeSynced() (bool, error) {
	dst := utils.NewFileUtil(bf.Path)

	if !dst.Exists() {
		return false, ErrResourceAbsent
	}

	mode, err := dst.Mode()
	if err != nil {
		return false, err
	}

	return mode.Perm() == bf.Mode, nil
}

// setMode sets the permissions on the file managed by the resource.
func (bf *BaseFile) setMode() error {
	dst := utils.NewFileUtil(bf.Path)

	return dst.Chmod(bf.Mode)
}

// isOwnerSynced checks whether the file ownership is correct.
func (bf *BaseFile) isOwnerSynced() (bool, error) {
	dst := utils.NewFileUtil(bf.Path)

	if !dst.Exists() {
		return false, ErrResourceAbsent
	}

	owner, err := dst.Owner()
	if err != nil {
		return false, err
	}

	return owner.User.Username == bf.Owner && owner.Group.Name == bf.Group, nil
}

// setOwner sets the ownership of the file.
func (bf *BaseFile) setOwner() error {
	dst := utils.NewFileUtil(bf.Path)

	return dst.SetOwner(bf.Owner, bf.Group)
}

// File resource manages files.
//
// Example:
//   foo = resource.file.new("/tmp/foo")
//   foo.state = "present"
//   foo.mode = tonumber("0600", 8)
//   foo.owner = "root"
//   foo.group = "wheel"
//   foo.content = "content of file foo"
type File struct {
	BaseFile

	// Content of file to set.
	Content []byte `luar:"content"`

	// Source file to use for the file content.
	Source string `luar:"source"`
}

// isContentSynced checks if the file content is in sync with the
// given content.
func (f *File) isContentSynced() (bool, error) {
	// We don't have a content, assume content is correct
	if f.Content == nil {
		return true, nil
	}

	dst := utils.NewFileUtil(f.Path)
	if !dst.Exists() {
		return false, ErrResourceAbsent
	}

	dstMd5, err := dst.Md5()
	if err != nil {
		return false, err
	}

	srcMd5 := fmt.Sprintf("%x", md5.Sum(f.Content))

	return srcMd5 == dstMd5, nil
}

// setContent sets the content of the file.
func (f *File) setContent() error {
	return ioutil.WriteFile(f.Path, f.Content, f.Mode)
}

// NewFile creates a resource for managing regular files.
func NewFile(name string) (Resource, error) {
	// Defaults for owner and group
	currentUser, err := user.Current()
	if err != nil {
		return nil, err
	}

	currentGroup, err := user.LookupGroupId(currentUser.Gid)
	if err != nil {
		return nil, err
	}

	// Resource defaults
	f := &File{
		BaseFile: BaseFile{
			Base: Base{
				Name:          name,
				Type:          "file",
				State:         "present",
				Require:       make([]string, 0),
				PresentStates: []string{"present"},
				AbsentStates:  []string{"absent"},
				Concurrent:    true,
				Subscribe:     make(TriggerMap),
			},
			Path:  name,
			Mode:  0644,
			Owner: currentUser.Username,
			Group: currentGroup.Name,
		},
		Content: nil,
		Source:  "",
	}

	// Set resource properties
	f.PropertyList = []Property{
		&ResourceProperty{
			PropertyName:         "mode",
			PropertySetFunc:      f.setMode,
			PropertyIsSyncedFunc: f.isModeSynced,
		},
		&ResourceProperty{
			PropertyName:         "ownership",
			PropertySetFunc:      f.setOwner,
			PropertyIsSyncedFunc: f.isOwnerSynced,
		},
		&ResourceProperty{
			PropertyName:         "content",
			PropertySetFunc:      f.setContent,
			PropertyIsSyncedFunc: f.isContentSynced,
		},
	}

	return f, nil
}

// Validate validates the file resource.
func (f *File) Validate() error {
	if err := f.Base.Validate(); err != nil {
		return err
	}

	if f.Source != "" && f.Content != nil {
		return errors.New("cannot use both 'source' and 'content'")
	}

	return nil
}

// Initialize initializes the file resource.
func (f *File) Initialize() error {
	// Set file content from the given source file if any.
	// TODO: Currenly this works only for files in the site repo.
	// TODO: Implement a generic file content fetcher.
	if f.Source != "" {
		src := filepath.Join(DefaultConfig.SiteRepo, f.Source)
		content, err := ioutil.ReadFile(src)
		if err != nil {
			return err
		}
		f.Content = content
	}

	return nil
}

// Evaluate evaluates the state of the file resource.
func (f *File) Evaluate() (State, error) {
	state := State{
		Current: "unknown",
		Want:    f.State,
	}

	fi, err := os.Stat(f.Path)
	if os.IsNotExist(err) {
		state.Current = "absent"
		return state, nil
	}

	state.Current = "present"

	if !fi.Mode().IsRegular() {
		return state, errors.New("path exists, but is not a regular file")
	}

	return state, nil
}

// Create creates the file managed by the resource.
func (f *File) Create() error {
	return ioutil.WriteFile(f.Path, f.Content, f.Mode)
}

// Delete deletes the file managed by the resource.
func (f *File) Delete() error {
	return os.Remove(f.Path)
}

// Directory resource manages directories.
//
// Example:
//   bar = resource.directory.new("/tmp/bar")
//   bar.state = "present"
//   bar.mode = tonumber("0700", 8)
//   bar.owner = "root"
//   bar.group = "wheel"
type Directory struct {
	BaseFile

	// Parents flag specifies whether or not to create/delete
	// parent directories. Defaults to false.
	Parents bool `luar:"parents"`
}

// NewDirectory creates a resource for managing directories.
func NewDirectory(name string) (Resource, error) {
	// Defaults for owner and group
	currentUser, err := user.Current()
	if err != nil {
		return nil, err
	}

	currentGroup, err := user.LookupGroupId(currentUser.Gid)
	if err != nil {
		return nil, err
	}

	// Resource defaults
	d := &Directory{
		BaseFile: BaseFile{
			Base: Base{
				Name:          name,
				Type:          "directory",
				State:         "present",
				Require:       make([]string, 0),
				PresentStates: []string{"present"},
				AbsentStates:  []string{"absent"},
				Concurrent:    true,
				Subscribe:     make(TriggerMap),
			},
			Path:  name,
			Mode:  0755,
			Owner: currentUser.Username,
			Group: currentGroup.Name,
		},
		Parents: false,
	}

	// Set resource properties
	d.PropertyList = []Property{
		&ResourceProperty{
			PropertyName:         "mode",
			PropertySetFunc:      d.setMode,
			PropertyIsSyncedFunc: d.isModeSynced,
		},
		&ResourceProperty{
			PropertyName:         "ownership",
			PropertySetFunc:      d.setOwner,
			PropertyIsSyncedFunc: d.isOwnerSynced,
		},
	}

	return d, nil
}

// Evaluate evaluates the state of the directory.
func (d *Directory) Evaluate() (State, error) {
	state := State{
		Current: "unknown",
		Want:    d.State,
	}

	fi, err := os.Stat(d.Path)
	if os.IsNotExist(err) {
		state.Current = "absent"
		return state, nil
	}

	state.Current = "present"

	if !fi.Mode().IsDir() {
		return state, errors.New("path exists, but is not a directory")
	}

	return state, nil
}

// Create creates the directory.
func (d *Directory) Create() error {
	if d.Parents {
		return os.MkdirAll(d.Path, d.Mode)
	}

	return os.Mkdir(d.Path, d.Mode)
}

// Delete removes the directory.
func (d *Directory) Delete() error {
	if d.Parents {
		return os.RemoveAll(d.Path)
	}

	return os.Remove(d.Path)
}

// Link resource manages links between files.
//
// Example:
//   baz = resource.link.new("/tmp/baz")
//   baz.state = "present"
//   baz.source = "/tmp/qux"
type Link struct {
	BaseFile

	// Source file points to the file the link will be set to.
	Source string `luar:"source"`

	// Hard flag specifies whether or not to create a hard link to the file.
	// Defaults to false.
	Hard bool `luar:"hard"`
}

// NewLink creates a new resource for managing links between files.
func NewLink(name string) (Resource, error) {
	l := &Link{
		BaseFile: BaseFile{
			Base: Base{
				Name:          name,
				Type:          "link",
				State:         "present",
				Require:       make([]string, 0),
				PresentStates: []string{"present"},
				AbsentStates:  []string{"absent"},
				Concurrent:    true,
				Subscribe:     make(TriggerMap),
			},
			Path: name,
		},
		Source: "",
		Hard:   false,
	}

	return l, nil
}

// Validate validates the link resource.
func (l *Link) Validate() error {
	if l.Source == "" {
		return errors.New("must provide source file")
	}

	src := utils.NewFileUtil(l.Source)
	if !src.Exists() {
		return fmt.Errorf("source file %s does not exist", l.Source)
	}

	return nil
}

// Evaluate evaluates the state of the link.
func (l *Link) Evaluate() (State, error) {
	state := State{
		Current: "unknown",
		Want:    l.State,
	}

	_, err := os.Stat(l.Path)
	if os.IsNotExist(err) {
		state.Current = "absent"
		return state, nil
	}

	state.Current = "present"

	_, err = os.Readlink(l.Path)
	if err != nil {
		return state, fmt.Errorf("path exists, but is not a link: %s\n", err)
	}

	return state, nil
}

// Create creates the link.
func (l *Link) Create() error {
	if l.Hard {
		return os.Link(l.Source, l.Path)
	}

	return os.Symlink(l.Source, l.Path)
}

// Delete removes the link.
func (l *Link) Delete() error {
	return os.Remove(l.Path)
}

func init() {
	file := ProviderItem{
		Type:      "file",
		Provider:  NewFile,
		Namespace: DefaultResourceNamespace,
	}

	dir := ProviderItem{
		Type:      "directory",
		Provider:  NewDirectory,
		Namespace: DefaultResourceNamespace,
	}

	link := ProviderItem{
		Type:      "link",
		Provider:  NewLink,
		Namespace: DefaultResourceNamespace,
	}

	RegisterProvider(file, dir, link)
}
<|endoftext|>


<commit_before>//  Copyright (c) 2012 Couchbase, Inc.
//  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 rest

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/gorilla/mux"

	"github.com/couchbaselabs/sync_gateway/auth"
	"github.com/couchbaselabs/sync_gateway/base"
)

// Handles PUT or POST to /username
func putUser(r http.ResponseWriter, rq *http.Request, a *auth.Authenticator, username string) error {
	body, _ := ioutil.ReadAll(rq.Body)
	var user auth.User
	err := json.Unmarshal(body, &user)
	if err != nil {
		return err
	}
	if user.AdminChannels == nil {
		return &base.HTTPError{http.StatusBadRequest, "Missing admin_channels property"}
	}
	user.AllChannels = nil		// Force it to be recomputed

	if rq.Method == "POST" {
		username = user.Name
		if username == "" {
			return &base.HTTPError{http.StatusBadRequest, "Missing name property"}
		}
	} else if user.Name == "" {
		user.Name = username
	} else if user.Name != username {
		return &base.HTTPError{http.StatusBadRequest, "Name mismatch (can't change name)"}
	}
	return a.SaveUser(&user)
}

// Generates a login session for a user and returns the session ID and cookie name.
func createUserSession(r http.ResponseWriter, rq *http.Request, authenticator *auth.Authenticator) error {
	body, err := ioutil.ReadAll(rq.Body)
	if err != nil {
		return err
	}
	var params struct {
		Name string        `json:"name"`
		TTL  time.Duration `json:"ttl"`
	}
	err = json.Unmarshal(body, &params)
	if err != nil {
		return err
	}
	if params.Name == "" || params.TTL < 0 {
		return &base.HTTPError{http.StatusBadRequest, "Invalid name or ttl"}
	}
	session, err := authenticator.CreateSession(params.Name, params.TTL)
	if err != nil {
		return err
	}
	var response struct {
		SessionID  string    `json:"session_id"`
		Expires    time.Time `json:"expires"`
		CookieName string    `json:"cookie_name"`
	}
	response.SessionID = session.ID
	response.Expires = session.Expiration
	response.CookieName = auth.CookieName
	bytes, _ := json.Marshal(response)
	r.Header().Set("Content-Type", "application/json")
	r.Write(bytes)
	return nil
}

func postUser(r http.ResponseWriter, rq *http.Request, auth *auth.Authenticator) error {
	return putUser(r, rq, auth, "")
}

func putUserInfo(r http.ResponseWriter, rq *http.Request, auth *auth.Authenticator) error {
	muxed := mux.Vars(rq)
	username := muxed["name"]
	if username == "GUEST" {
		username = "" //todo handle this at model layer?
	}
	return putUser(r, rq, auth, username)
}

func deleteUser(r http.ResponseWriter, rq *http.Request, auth *auth.Authenticator) error {
	muxed := mux.Vars(rq)
	username := muxed["name"]
	user, _ := auth.GetUser(username)
	if user == nil || auth.DeleteUser(user) != nil {
		return kNotFoundError
	}
	return nil
}

func getUserInfo(r http.ResponseWriter, rq *http.Request, auth *auth.Authenticator) error {
	muxed := mux.Vars(rq)
	// dbname := muxed["db"]
	username := muxed["name"]
	if username == "GUEST" {
		username = "" //todo handle this at model layer?
	}
	user, _ := auth.GetUser(username)
	if user == nil {
		return kNotFoundError;
	}
	bytes, _ := json.Marshal(user)
	r.Write(bytes)
	return nil
}

func renderError(err error, r http.ResponseWriter) {
	status, message := base.ErrorAsHTTPStatus(err)
	r.Header().Set("Content-Type", "application/json")
	r.WriteHeader(status)
	jsonOut, _ := json.Marshal(map[string]interface{}{"error": status, "reason": message})
	r.Write(jsonOut)
}

type authHandler func(http.ResponseWriter, *http.Request, *auth.Authenticator) error

func handleAuthReq(auth *auth.Authenticator, fun authHandler) func(http.ResponseWriter, *http.Request) {
	return func(r http.ResponseWriter, rq *http.Request) {
		err := fun(r, rq, auth)
		if (err != nil) {
			renderError(err, r)
		}
	}
}

// Starts a simple REST listener that will get and set user credentials.
func StartAuthListener(addr string, auth *auth.Authenticator) {
	r := mux.NewRouter()
	// r.StrictSlash(true)

	r.HandleFunc("/{db}/_session",
		handleAuthReq(auth, createUserSession)).Methods("POST")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(auth, getUserInfo)).Methods("GET", "HEAD")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(auth, putUserInfo)).Methods("PUT")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(auth, deleteUser)).Methods("DELETE")
	r.HandleFunc("/{db}/user",
		handleAuthReq(auth, postUser)).Methods("POST")

	// http.Handle("/", r);
	go http.ListenAndServe(addr, r)
}
<commit_msg>Cleanup of authrest.go<commit_after>//  Copyright (c) 2012 Couchbase, Inc.
//  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 rest

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/gorilla/mux"

	"github.com/couchbaselabs/sync_gateway/auth"
	"github.com/couchbaselabs/sync_gateway/base"
)

// Handles PUT or POST to /username
func putUserCommon(r http.ResponseWriter, rq *http.Request, a *auth.Authenticator, username string) error {
	body, _ := ioutil.ReadAll(rq.Body)
	var user auth.User
	err := json.Unmarshal(body, &user)
	if err != nil {
		return err
	}
	if user.AdminChannels == nil {
		return &base.HTTPError{http.StatusBadRequest, "Missing admin_channels property"}
	}
	user.AllChannels = nil // Force it to be recomputed

	if rq.Method == "POST" {
		username = user.Name
		if username == "" {
			return &base.HTTPError{http.StatusBadRequest, "Missing name property"}
		}
	} else if user.Name == "" {
		user.Name = username
	} else if user.Name != username {
		return &base.HTTPError{http.StatusBadRequest, "Name mismatch (can't change name)"}
	}
	return a.SaveUser(&user)
}

func postUser(r http.ResponseWriter, rq *http.Request, auth *auth.Authenticator) error {
	return putUserCommon(r, rq, auth, "")
}

func putUser(r http.ResponseWriter, rq *http.Request, auth *auth.Authenticator) error {
	muxed := mux.Vars(rq)
	username := muxed["name"]
	if username == "GUEST" {
		username = "" //todo handle this at model layer?
	}
	return putUserCommon(r, rq, auth, username)
}

// Generates a login session for a user and returns the session ID and cookie name.
func createUserSession(r http.ResponseWriter, rq *http.Request, authenticator *auth.Authenticator) error {
	body, err := ioutil.ReadAll(rq.Body)
	if err != nil {
		return err
	}
	var params struct {
		Name string        `json:"name"`
		TTL  time.Duration `json:"ttl"`
	}
	err = json.Unmarshal(body, &params)
	if err != nil {
		return err
	}
	if params.Name == "" || params.TTL < 0 {
		return &base.HTTPError{http.StatusBadRequest, "Invalid name or ttl"}
	}
	session, err := authenticator.CreateSession(params.Name, params.TTL)
	if err != nil {
		return err
	}
	var response struct {
		SessionID  string    `json:"session_id"`
		Expires    time.Time `json:"expires"`
		CookieName string    `json:"cookie_name"`
	}
	response.SessionID = session.ID
	response.Expires = session.Expiration
	response.CookieName = auth.CookieName
	bytes, _ := json.Marshal(response)
	r.Header().Set("Content-Type", "application/json")
	r.Write(bytes)
	return nil
}

func deleteUser(r http.ResponseWriter, rq *http.Request, auth *auth.Authenticator) error {
	muxed := mux.Vars(rq)
	username := muxed["name"]
	user, err := auth.GetUser(username)
	if user == nil {
		if err == nil {
			err = kNotFoundError
		}
		return err
	}
	return auth.DeleteUser(user)
}

func getUserInfo(r http.ResponseWriter, rq *http.Request, auth *auth.Authenticator) error {
	muxed := mux.Vars(rq)
	username := muxed["name"]
	if username == "GUEST" {
		username = "" //todo handle this at model layer?
	}
	user, err := auth.GetUser(username)
	if user == nil {
		if err == nil {
			err = kNotFoundError
		}
		return err
	}
	bytes, _ := json.Marshal(user)
	r.Write(bytes)
	return nil
}

func renderError(err error, r http.ResponseWriter) {
	status, message := base.ErrorAsHTTPStatus(err)
	r.Header().Set("Content-Type", "application/json")
	r.WriteHeader(status)
	jsonOut, _ := json.Marshal(map[string]interface{}{"error": status, "reason": message})
	r.Write(jsonOut)
}

type authHandler func(http.ResponseWriter, *http.Request, *auth.Authenticator) error

func handleAuthReq(auth *auth.Authenticator, fun authHandler) func(http.ResponseWriter, *http.Request) {
	return func(r http.ResponseWriter, rq *http.Request) {
		err := fun(r, rq, auth)
		if err != nil {
			renderError(err, r)
		}
	}
}

// Starts a simple REST listener that will get and set user credentials.
func StartAuthListener(addr string, auth *auth.Authenticator) {
	r := mux.NewRouter()
	// r.StrictSlash(true)

	r.HandleFunc("/{db}/_session",
		handleAuthReq(auth, createUserSession)).Methods("POST")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(auth, getUserInfo)).Methods("GET", "HEAD")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(auth, putUser)).Methods("PUT")
	r.HandleFunc("/{db}/user/{name}",
		handleAuthReq(auth, deleteUser)).Methods("DELETE")
	r.HandleFunc("/{db}/user",
		handleAuthReq(auth, postUser)).Methods("POST")

	// http.Handle("/", r);
	go http.ListenAndServe(addr, r)
}
<|endoftext|>


<commit_before>package user

import (
	"fmt"
	"log"
	"os"
	"strings"
	"syscall"

	"github.com/codegangsta/cli"
	"github.com/garlsecurity/securepassctl/spctl/service"
	"golang.org/x/crypto/ssh/terminal"
)

func init() {
	Command.Subcommands = append(Command.Subcommands,
		cli.Command{
			Name:        "passwd",
			Usage:       "modify user's password",
			ArgsUsage:   "USERNAME",
			Description: "Change or disable user password for SecurePass.",
			Action:      ActionPasswd,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "disable, d",
					Usage: "Disable user's password.",
				},
			},
		})
}

// ActionPasswd provides the passwd subcommand
func ActionPasswd(c *cli.Context) {
	if len(c.Args()) != 1 {
		log.Fatal("error: must specify a username")
	}

	username := c.Args()[0]
	if c.Bool("disable") {
		_, err := service.Service.UserPasswordDisable(username)
		if err != nil {
			log.Fatalf("error: %v", err)
		}
		log.Println("Password removed")
		os.Exit(0)
	}
	fmt.Fprintf(os.Stderr, "Enter new password: ")
	newPassword, err := terminal.ReadPassword(syscall.Stdin)
	fmt.Fprintln(os.Stderr)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Fprintf(os.Stderr, "Confirm new password: ")
	newPasswordConfirm, err := terminal.ReadPassword(syscall.Stdin)
	fmt.Fprintln(os.Stderr)
	if err != nil {
		log.Fatalf("error: %v", err)
	}

	newPasswordString := strings.TrimSpace(string(newPassword))
	if newPasswordString != strings.TrimSpace(string(newPasswordConfirm)) {
		log.Fatalf("error: Password mismatch.")
	}
	_, err = service.Service.UserPasswordChange(username, newPasswordString)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Fprintln(os.Stderr, "Password updated.")
}
<commit_msg>Implement getpasswd() a in more portable fashion<commit_after>package user

import (
	"fmt"
	"log"
	"os"
	"strings"
	"syscall"

	"github.com/codegangsta/cli"
	"github.com/garlsecurity/securepassctl/spctl/service"
	"golang.org/x/crypto/ssh/terminal"
)

func init() {
	Command.Subcommands = append(Command.Subcommands,
		cli.Command{
			Name:        "passwd",
			Usage:       "modify user's password",
			ArgsUsage:   "USERNAME",
			Description: "Change or disable user password for SecurePass.",
			Action:      ActionPasswd,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "disable, d",
					Usage: "Disable user's password.",
				},
			},
		})
}

// ActionPasswd provides the passwd subcommand
func ActionPasswd(c *cli.Context) {
	if len(c.Args()) != 1 {
		log.Fatal("error: must specify a username")
	}

	username := c.Args()[0]
	if c.Bool("disable") {
		_, err := service.Service.UserPasswordDisable(username)
		if err != nil {
			log.Fatalf("error: %v", err)
		}
		log.Println("Password removed")
		os.Exit(0)
	}
	fmt.Fprintf(os.Stderr, "Enter new password: ")
	newPassword, err := terminal.ReadPassword(int(syscall.Stdin))
	fmt.Fprintln(os.Stderr)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Fprintf(os.Stderr, "Confirm new password: ")
	newPasswordConfirm, err := terminal.ReadPassword(int(syscall.Stdin))
	fmt.Fprintln(os.Stderr)
	if err != nil {
		log.Fatalf("error: %v", err)
	}

	newPasswordString := strings.TrimSpace(string(newPassword))
	if newPasswordString != strings.TrimSpace(string(newPasswordConfirm)) {
		log.Fatalf("error: Password mismatch.")
	}
	_, err = service.Service.UserPasswordChange(username, newPasswordString)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Fprintln(os.Stderr, "Password updated.")
}
<|endoftext|>


<commit_before>package gophercloud

import "errors"

var (
	// ErrServiceNotFound is returned when no service matches the EndpointOpts.
	ErrServiceNotFound = errors.New("No suitable service could be found in the service catalog.")

	// ErrEndpointNotFound is returned when no available endpoints match the provided EndpointOpts.
	ErrEndpointNotFound = errors.New("No suitable endpoint could be found in the service catalog.")
)

// Availability describes the accessibility of a specific service endpoint.
type Availability string

const (
	// AvailabilityAdmin makes an endpoint only available to administrators.
	AvailabilityAdmin Availability = "admin"

	// AvailabilityPublic makes an endpoint available to everyone.
	AvailabilityPublic Availability = "public"

	// AvailabilityInternal makes an endpoint only available within the cluster.
	AvailabilityInternal Availability = "internal"
)

// EndpointOpts contains options for finding an endpoint for an Openstack client.
type EndpointOpts struct {

	// Type is the service type for the client (e.g., "compute", "object-store").
	// Type is a required field.
	Type string

	// Name is the service name for the client (e.g., "nova").
	// Name is not a required field, but it is used if present.
	// Services can have the same Type but a different Name, which is one example of when both Type and Name are needed.
	Name string

	// Region is the region in which the service resides.
	// Region must be specified for services that span multiple regions.
	Region string

	// Availability is the visibility of the endpoint to be returned: AvailabilityPublic, AvailabilityInternal, or AvailabilityAdmin.
	// Availability is not required, and defaults to InterfacePublic.
	// Not all providers or services offer all Availability options.
	Availability Availability
}

// EndpointLocator is a function that describes how to locate a single endpoint from a service catalog for a specific ProviderClient.
// It should be set during ProviderClient authentication and used to discover related ServiceClients.
type EndpointLocator func(EndpointOpts) (string, error)
<commit_msg>Explain differences in terminology between v[23].<commit_after>package gophercloud

import "errors"

var (
	// ErrServiceNotFound is returned when no service matches the EndpointOpts.
	ErrServiceNotFound = errors.New("No suitable service could be found in the service catalog.")

	// ErrEndpointNotFound is returned when no available endpoints match the provided EndpointOpts.
	ErrEndpointNotFound = errors.New("No suitable endpoint could be found in the service catalog.")
)

// Availability describes the accessibility of a specific service endpoint.
// Identity v2 lists these as different kinds of URLs ("adminURL", "internalURL", and "publicURL"), while
// v3 lists them as "Interfaces".
type Availability string

const (
	// AvailabilityAdmin makes an endpoint only available to administrators.
	AvailabilityAdmin Availability = "admin"

	// AvailabilityPublic makes an endpoint available to everyone.
	AvailabilityPublic Availability = "public"

	// AvailabilityInternal makes an endpoint only available within the cluster.
	AvailabilityInternal Availability = "internal"
)

// EndpointOpts contains options for finding an endpoint for an Openstack client.
type EndpointOpts struct {

	// Type is the service type for the client (e.g., "compute", "object-store").
	// Type is a required field.
	Type string

	// Name is the service name for the client (e.g., "nova").
	// Name is not a required field, but it is used if present.
	// Services can have the same Type but a different Name, which is one example of when both Type and Name are needed.
	Name string

	// Region is the region in which the service resides.
	// Region must be specified for services that span multiple regions.
	Region string

	// Availability is the visibility of the endpoint to be returned: AvailabilityPublic, AvailabilityInternal, or AvailabilityAdmin.
	// Availability is not required, and defaults to InterfacePublic.
	// Not all providers or services offer all Availability options.
	Availability Availability
}

// EndpointLocator is a function that describes how to locate a single endpoint from a service catalog for a specific ProviderClient.
// It should be set during ProviderClient authentication and used to discover related ServiceClients.
type EndpointLocator func(EndpointOpts) (string, error)
<|endoftext|>


<commit_before>// go2hal v0.3.3
// Copyright (c) 2017 Patrick Moule
// License: MIT

package hal

import (
	"testing"
)

func TestNewCurieLink(t *testing.T) {
	wantedName := "link"
	wantedHref := "http://{rel}"

	wantedCurieLink, _ := NewCurieLink(wantedName, wantedHref)

	if wantedCurieLink.Name != wantedName {
		t.Errorf("Link name == %q, want %q", wantedCurieLink.Name, wantedName)
	}

	if wantedCurieLink.Href != wantedHref {
		t.Errorf("Link href == %q, want %q", wantedCurieLink.Href, wantedHref)
	}

	if !wantedCurieLink.Templated {
		t.Errorf("Link templated == %q, want %q", wantedCurieLink.Templated, true)
	}

	_, invalidNameError := NewCurieLink("", wantedHref)

	if invalidNameError == nil {
		t.Errorf("NewCurieLink should return an error due to an invalid name value.")
	}

	_, invalidHrefError := NewCurieLink(wantedName, "")

	if invalidHrefError == nil {
		t.Errorf("NewCurieLink should return an error due to an invalid href value.")
	}
}
<commit_msg>fix: wrong format string in test<commit_after>// go2hal v0.3.3
// Copyright (c) 2017 Patrick Moule
// License: MIT

package hal

import (
	"testing"
)

func TestNewCurieLink(t *testing.T) {
	wantedName := "link"
	wantedHref := "http://{rel}"

	wantedCurieLink, _ := NewCurieLink(wantedName, wantedHref)

	if wantedCurieLink.Name != wantedName {
		t.Errorf("Link name == %q, want %q", wantedCurieLink.Name, wantedName)
	}

	if wantedCurieLink.Href != wantedHref {
		t.Errorf("Link href == %q, want %q", wantedCurieLink.Href, wantedHref)
	}

	if !wantedCurieLink.Templated {
		t.Errorf("Link templated == %t, want %t", wantedCurieLink.Templated, true)
	}

	_, invalidNameError := NewCurieLink("", wantedHref)

	if invalidNameError == nil {
		t.Errorf("NewCurieLink should return an error due to an invalid name value.")
	}

	_, invalidHrefError := NewCurieLink(wantedName, "")

	if invalidHrefError == nil {
		t.Errorf("NewCurieLink should return an error due to an invalid href value.")
	}
}
<|endoftext|>


<commit_before>package handler

import (
	"fmt"
	"github.com/huangjunwen/JustSQL/context"
	"github.com/pingcap/tidb/ast"
)

func handleSelectStmt(ctx *context.Context, obj interface{}) (interface{}, error) {

	select_stmt, ok := obj.(*ast.SelectStmt)
	if !ok {
		return nil, fmt.Errorf("handleSelectStmt: expect *ast.SelectStmt but got %T", obj)
	}

	if _, err := ctx.DB.Compile(select_stmt); err != nil {
		return nil, err
	}

	fn, err := NewDMLFunc(ctx, select_stmt)
	if err != nil {
		return nil, err
	}

	return map[string]interface{}{
		"Stmt": select_stmt,
		"Func": fn,
	}, nil

}

func init() {
	RegistHandler((*ast.SelectStmt)(nil), handleSelectStmt)
}
<commit_msg>Finish select statement's handler<commit_after>package handler

import (
	"fmt"
	"github.com/huangjunwen/JustSQL/context"
	"github.com/pingcap/tidb/ast"
)

func handleSelectStmt(ctx *context.Context, obj interface{}) (interface{}, error) {

	select_stmt, ok := obj.(*ast.SelectStmt)
	if !ok {
		return nil, fmt.Errorf("handleSelectStmt: expect *ast.SelectStmt but got %T", obj)
	}

	if _, err := ctx.DB.Compile(select_stmt); err != nil {
		return nil, err
	}

	fn, err := NewDMLFunc(ctx, select_stmt)
	if err != nil {
		return nil, err
	}

	select_stmt_meta, err := context.NewSelectStmtMeta(ctx, select_stmt)
	if err != nil {
		return nil, err
	}

	return map[string]interface{}{
		"Stmt": select_stmt_meta,
		"Func": fn,
	}, nil

}

func init() {
	RegistHandler((*ast.SelectStmt)(nil), handleSelectStmt)
}
<|endoftext|>