<commit_before>package enry

import (
	"bufio"
	"bytes"
	"path/filepath"
	"regexp"
	"strings"

	"gopkg.in/src-d/enry.v1/data"
)

// OtherLanguage is used as a zero value when a function can not return a specific language.
const OtherLanguage = ""

// Strategy type fix the signature for the functions that can be used as a strategy.
type Strategy func(filename string, content []byte, candidates []string) (languages []string)

// DefaultStrategies is the strategies' sequence GetLanguage uses to detect languages.
var DefaultStrategies = []Strategy{
	GetLanguagesByModeline,
	GetLanguagesByFilename,
	GetLanguagesByShebang,
	GetLanguagesByExtension,
	GetLanguagesByContent,
	GetLanguagesByClassifier,
}

var DefaultClassifier Classifier = &classifier{
	languagesLogProbabilities: data.LanguagesLogProbabilities,
	tokensLogProbabilities:    data.TokensLogProbabilities,
	tokensTotal:               data.TokensTotal,
}

// GetLanguage applies a sequence of strategies based on the given filename and content
// to find out the most probably language to return.
func GetLanguage(filename string, content []byte) (language string) {
	languages := GetLanguages(filename, content)
	return firstLanguage(languages)
}

func firstLanguage(languages []string) string {
	if len(languages) == 0 {
		return OtherLanguage
	}

	return languages[0]
}

// GetLanguageByModeline returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByModeline(content []byte) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByModeline, "", content, nil)
}

// GetLanguageByEmacsModeline returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByEmacsModeline(content []byte) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByEmacsModeline, "", content, nil)
}

// GetLanguageByVimModeline returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByVimModeline(content []byte) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByVimModeline, "", content, nil)
}

// GetLanguageByFilename returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByFilename(filename string) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByFilename, filename, nil, nil)
}

// GetLanguageByShebang returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByShebang(content []byte) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByShebang, "", content, nil)
}

// GetLanguageByExtension returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByExtension(filename string) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByExtension, filename, nil, nil)
}

// GetLanguageByContent returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByContent(content []byte) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByContent, "", content, nil)
}

// GetLanguageByClassifier returns the most probably language detected for the given content. It uses
// DefaultClassifier, if no candidates are provided it returns OtherLanguage.
func GetLanguageByClassifier(content []byte, candidates []string) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByClassifier, "", content, candidates)
}

func getLanguageByStrategy(strategy Strategy, filename string, content []byte, candidates []string) (string, bool) {
	languages := strategy(filename, content, candidates)
	return getFirstLanguageAndSafe(languages)
}

func getFirstLanguageAndSafe(languages []string) (language string, safe bool) {
	language = firstLanguage(languages)
	safe = len(languages) == 1
	return
}

// GetLanguageBySpecificClassifier returns the most probably language for the given content using
// classifier to detect language.
func GetLanguageBySpecificClassifier(content []byte, candidates []string, classifier Classifier) (language string, safe bool) {
	languages := GetLanguagesBySpecificClassifier(content, candidates, classifier)
	return getFirstLanguageAndSafe(languages)
}

// GetLanguages applies a sequence of strategies based on the given filename and content
// to find out the most probably languages to return.
func GetLanguages(filename string, content []byte) []string {
	if IsBinary(content) {
		return nil
	}

	var languages []string
	candidates := []string{}
	for _, strategy := range DefaultStrategies {
		languages = strategy(filename, content, candidates)
		if len(languages) == 1 {
			return languages
		}

		if len(languages) > 0 {
			candidates = append(candidates, languages...)
		}
	}

	return languages
}

// GetLanguagesByModeline returns a slice of possible languages for the given content, filename will be ignored.
// It complies with the signature to be a Strategy type.
func GetLanguagesByModeline(filename string, content []byte, candidates []string) []string {
	headFoot := getHeaderAndFooter(content)
	var languages []string
	for _, getLang := range modelinesFunc {
		languages = getLang("", headFoot, candidates)
		if len(languages) > 0 {
			break
		}
	}

	return languages
}

var modelinesFunc = []Strategy{
	GetLanguagesByEmacsModeline,
	GetLanguagesByVimModeline,
}

func getHeaderAndFooter(content []byte) []byte {
	const searchScope = 5
	if bytes.Count(content, []byte("\n")) < 2*searchScope {
		return content
	}

	header := headScope(content, searchScope)
	footer := footScope(content, searchScope)
	headerAndFooter := make([]byte, 0, len(content[:header])+len(content[footer:]))
	headerAndFooter = append(headerAndFooter, content[:header]...)
	headerAndFooter = append(headerAndFooter, content[footer:]...)
	return headerAndFooter
}

func headScope(content []byte, scope int) (index int) {
	for i := 0; i < scope; i++ {
		eol := bytes.IndexAny(content, "\n")
		content = content[eol+1:]
		index += eol
	}

	return index + scope - 1
}

func footScope(content []byte, scope int) (index int) {
	for i := 0; i < scope; i++ {
		index = bytes.LastIndexAny(content, "\n")
		content = content[:index]
	}

	return index + 1
}

var (
	reEmacsModeline = regexp.MustCompile(`.*-\*-\s*(.+?)\s*-\*-.*(?m:$)`)
	reEmacsLang     = regexp.MustCompile(`.*(?i:mode)\s*:\s*([^\s;]+)\s*;*.*`)
	reVimModeline   = regexp.MustCompile(`(?:(?m:\s|^)vi(?:m[<=>]?\d+|m)?|[\t\x20]*ex)\s*[:]\s*(.*)(?m:$)`)
	reVimLang       = regexp.MustCompile(`(?i:filetype|ft|syntax)\s*=(\w+)(?:\s|:|$)`)
)

// GetLanguagesByEmacsModeline returns a slice of possible languages for the given content, filename and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByEmacsModeline(filename string, content []byte, candidates []string) []string {
	matched := reEmacsModeline.FindAllSubmatch(content, -1)
	if matched == nil {
		return nil
	}

	// only take the last matched line, discard previous lines
	lastLineMatched := matched[len(matched)-1][1]
	matchedAlias := reEmacsLang.FindSubmatch(lastLineMatched)
	var alias string
	if matchedAlias != nil {
		alias = string(matchedAlias[1])
	} else {
		alias = string(lastLineMatched)
	}

	language, ok := GetLanguageByAlias(alias)
	if !ok {
		return nil
	}

	return []string{language}
}

// GetLanguagesByVimModeline returns a slice of possible languages for the given content, filename and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByVimModeline(filename string, content []byte, candidates []string) []string {
	matched := reVimModeline.FindAllSubmatch(content, -1)
	if matched == nil {
		return nil
	}

	// only take the last matched line, discard previous lines
	lastLineMatched := matched[len(matched)-1][1]
	matchedAlias := reVimLang.FindAllSubmatch(lastLineMatched, -1)
	if matchedAlias == nil {
		return nil
	}

	alias := string(matchedAlias[0][1])
	if len(matchedAlias) > 1 {
		// cases:
		// matchedAlias = [["syntax=ruby " "ruby"] ["ft=python " "python"] ["filetype=perl " "perl"]] returns OtherLanguage;
		// matchedAlias = [["syntax=python " "python"] ["ft=python " "python"] ["filetype=python " "python"]] returns "Python";
		for _, match := range matchedAlias {
			otherAlias := string(match[1])
			if otherAlias != alias {
				return nil
			}
		}
	}

	language, ok := GetLanguageByAlias(alias)
	if !ok {
		return nil
	}

	return []string{language}
}

// GetLanguagesByFilename returns a slice of possible languages for the given filename, content and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByFilename(filename string, content []byte, candidates []string) []string {
	return data.LanguagesByFilename[filepath.Base(filename)]
}

// GetLanguagesByShebang returns a slice of possible languages for the given content, filename and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByShebang(filename string, content []byte, candidates []string) (languages []string) {
	interpreter := getInterpreter(content)
	return data.LanguagesByInterpreter[interpreter]
}

var (
	shebangExecHack = regexp.MustCompile(`exec (\w+).+\$0.+\$@`)
	pythonVersion   = regexp.MustCompile(`python\d\.\d+`)
)

func getInterpreter(data []byte) (interpreter string) {
	line := getFirstLine(data)
	if !hasShebang(line) {
		return ""
	}

	// skip shebang
	line = bytes.TrimSpace(line[2:])
	splitted := bytes.Fields(line)
	if len(splitted) == 0 {
		return ""
	}

	if bytes.Contains(splitted[0], []byte("env")) {
		if len(splitted) > 1 {
			interpreter = string(splitted[1])
		}
	} else {
		splittedPath := bytes.Split(splitted[0], []byte{'/'})
		interpreter = string(splittedPath[len(splittedPath)-1])
	}

	if interpreter == "sh" {
		interpreter = lookForMultilineExec(data)
	}

	if pythonVersion.MatchString(interpreter) {
		interpreter = interpreter[:strings.Index(interpreter, `.`)]
	}

	return
}

func getFirstLine(data []byte) []byte {
	buf := bufio.NewScanner(bytes.NewReader(data))
	buf.Scan()
	line := buf.Bytes()
	if err := buf.Err(); err != nil {
		return nil
	}

	return line
}

func hasShebang(line []byte) bool {
	const shebang = `#!`
	prefix := []byte(shebang)
	return bytes.HasPrefix(line, prefix)
}

func lookForMultilineExec(data []byte) string {
	const magicNumOfLines = 5
	interpreter := "sh"

	buf := bufio.NewScanner(bytes.NewReader(data))
	for i := 0; i < magicNumOfLines && buf.Scan(); i++ {
		line := buf.Bytes()
		if shebangExecHack.Match(line) {
			interpreter = shebangExecHack.FindStringSubmatch(string(line))[1]
			break
		}
	}

	if err := buf.Err(); err != nil {
		return interpreter
	}

	return interpreter
}

// GetLanguagesByExtension returns a slice of possible languages for the given filename, content and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByExtension(filename string, content []byte, candidates []string) []string {
	if !strings.Contains(filename, ".") {
		return nil
	}

	filename = strings.ToLower(filename)
	dots := getDotIndexes(filename)
	for _, dot := range dots {
		ext := filename[dot:]
		languages, ok := data.LanguagesByExtension[ext]
		if ok {
			return languages
		}
	}

	return nil
}

func getDotIndexes(filename string) []int {
	dots := make([]int, 0, 2)
	for i, letter := range filename {
		if letter == rune('.') {
			dots = append(dots, i)
		}
	}

	return dots
}

// GetLanguagesByContent returns a slice of possible languages for the given content, filename and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByContent(filename string, content []byte, candidates []string) []string {
	ext := strings.ToLower(filepath.Ext(filename))
	fnMatcher, ok := data.ContentMatchers[ext]
	if !ok {
		return nil
	}

	return fnMatcher(content)
}

// GetLanguagesByClassifier uses DefaultClassifier as a Classifier and returns a sorted slice of possible languages ordered by
// decreasing language's probability. If there are not candidates it returns nil. It complies with the signature to be a Strategy type.
func GetLanguagesByClassifier(filename string, content []byte, candidates []string) (languages []string) {
	if len(candidates) == 0 {
		return nil
	}

	return GetLanguagesBySpecificClassifier(content, candidates, DefaultClassifier)
}

// GetLanguagesBySpecificClassifier returns a slice of possible languages. It takes in a Classifier to be used.
func GetLanguagesBySpecificClassifier(content []byte, candidates []string, classifier Classifier) (languages []string) {
	mapCandidates := make(map[string]float64)
	for _, candidate := range candidates {
		mapCandidates[candidate]++
	}

	return classifier.Classify(content, mapCandidates)
}

// GetLanguageExtensions returns the different extensions being used by the language.
func GetLanguageExtensions(language string) []string {
	return data.ExtensionsByLanguage[language]
}

// Type represent language's type. Either data, programming, markup, prose, or unknown.
type Type int

// Type's values.
const (
	Unknown Type = iota
	Data
	Programming
	Markup
	Prose
)

// GetLanguageType returns the type of the given language.
func GetLanguageType(language string) (langType Type) {
	intType, ok := data.LanguagesType[language]
	langType = Type(intType)
	if !ok {
		langType = Unknown
	}
	return langType
}

// GetLanguageByAlias returns either the language related to the given alias and ok set to true
// or Otherlanguage and ok set to false if the alias is not recognized.
func GetLanguageByAlias(alias string) (lang string, ok bool) {
	a := strings.Split(alias, `,`)[0]
	a = strings.ToLower(a)
	lang, ok = data.LanguagesByAlias[a]
	if !ok {
		lang = OtherLanguage
	}

	return
}
<commit_msg>common: Add filename parameter to GetLanguageByContent function<commit_after>package enry

import (
	"bufio"
	"bytes"
	"path/filepath"
	"regexp"
	"strings"

	"gopkg.in/src-d/enry.v1/data"
)

// OtherLanguage is used as a zero value when a function can not return a specific language.
const OtherLanguage = ""

// Strategy type fix the signature for the functions that can be used as a strategy.
type Strategy func(filename string, content []byte, candidates []string) (languages []string)

// DefaultStrategies is the strategies' sequence GetLanguage uses to detect languages.
var DefaultStrategies = []Strategy{
	GetLanguagesByModeline,
	GetLanguagesByFilename,
	GetLanguagesByShebang,
	GetLanguagesByExtension,
	GetLanguagesByContent,
	GetLanguagesByClassifier,
}

var DefaultClassifier Classifier = &classifier{
	languagesLogProbabilities: data.LanguagesLogProbabilities,
	tokensLogProbabilities:    data.TokensLogProbabilities,
	tokensTotal:               data.TokensTotal,
}

// GetLanguage applies a sequence of strategies based on the given filename and content
// to find out the most probably language to return.
func GetLanguage(filename string, content []byte) (language string) {
	languages := GetLanguages(filename, content)
	return firstLanguage(languages)
}

func firstLanguage(languages []string) string {
	if len(languages) == 0 {
		return OtherLanguage
	}

	return languages[0]
}

// GetLanguageByModeline returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByModeline(content []byte) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByModeline, "", content, nil)
}

// GetLanguageByEmacsModeline returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByEmacsModeline(content []byte) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByEmacsModeline, "", content, nil)
}

// GetLanguageByVimModeline returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByVimModeline(content []byte) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByVimModeline, "", content, nil)
}

// GetLanguageByFilename returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByFilename(filename string) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByFilename, filename, nil, nil)
}

// GetLanguageByShebang returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByShebang(content []byte) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByShebang, "", content, nil)
}

// GetLanguageByExtension returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByExtension(filename string) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByExtension, filename, nil, nil)
}

// GetLanguageByContent returns detected language. If there are more than one possibles languages
// it returns the first language by alphabetically order and safe to false.
func GetLanguageByContent(filename string, content []byte) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByContent, filename, content, nil)
}

// GetLanguageByClassifier returns the most probably language detected for the given content. It uses
// DefaultClassifier, if no candidates are provided it returns OtherLanguage.
func GetLanguageByClassifier(content []byte, candidates []string) (language string, safe bool) {
	return getLanguageByStrategy(GetLanguagesByClassifier, "", content, candidates)
}

func getLanguageByStrategy(strategy Strategy, filename string, content []byte, candidates []string) (string, bool) {
	languages := strategy(filename, content, candidates)
	return getFirstLanguageAndSafe(languages)
}

func getFirstLanguageAndSafe(languages []string) (language string, safe bool) {
	language = firstLanguage(languages)
	safe = len(languages) == 1
	return
}

// GetLanguageBySpecificClassifier returns the most probably language for the given content using
// classifier to detect language.
func GetLanguageBySpecificClassifier(content []byte, candidates []string, classifier Classifier) (language string, safe bool) {
	languages := GetLanguagesBySpecificClassifier(content, candidates, classifier)
	return getFirstLanguageAndSafe(languages)
}

// GetLanguages applies a sequence of strategies based on the given filename and content
// to find out the most probably languages to return.
func GetLanguages(filename string, content []byte) []string {
	if IsBinary(content) {
		return nil
	}

	var languages []string
	candidates := []string{}
	for _, strategy := range DefaultStrategies {
		languages = strategy(filename, content, candidates)
		if len(languages) == 1 {
			return languages
		}

		if len(languages) > 0 {
			candidates = append(candidates, languages...)
		}
	}

	return languages
}

// GetLanguagesByModeline returns a slice of possible languages for the given content, filename will be ignored.
// It complies with the signature to be a Strategy type.
func GetLanguagesByModeline(filename string, content []byte, candidates []string) []string {
	headFoot := getHeaderAndFooter(content)
	var languages []string
	for _, getLang := range modelinesFunc {
		languages = getLang("", headFoot, candidates)
		if len(languages) > 0 {
			break
		}
	}

	return languages
}

var modelinesFunc = []Strategy{
	GetLanguagesByEmacsModeline,
	GetLanguagesByVimModeline,
}

func getHeaderAndFooter(content []byte) []byte {
	const searchScope = 5
	if bytes.Count(content, []byte("\n")) < 2*searchScope {
		return content
	}

	header := headScope(content, searchScope)
	footer := footScope(content, searchScope)
	headerAndFooter := make([]byte, 0, len(content[:header])+len(content[footer:]))
	headerAndFooter = append(headerAndFooter, content[:header]...)
	headerAndFooter = append(headerAndFooter, content[footer:]...)
	return headerAndFooter
}

func headScope(content []byte, scope int) (index int) {
	for i := 0; i < scope; i++ {
		eol := bytes.IndexAny(content, "\n")
		content = content[eol+1:]
		index += eol
	}

	return index + scope - 1
}

func footScope(content []byte, scope int) (index int) {
	for i := 0; i < scope; i++ {
		index = bytes.LastIndexAny(content, "\n")
		content = content[:index]
	}

	return index + 1
}

var (
	reEmacsModeline = regexp.MustCompile(`.*-\*-\s*(.+?)\s*-\*-.*(?m:$)`)
	reEmacsLang     = regexp.MustCompile(`.*(?i:mode)\s*:\s*([^\s;]+)\s*;*.*`)
	reVimModeline   = regexp.MustCompile(`(?:(?m:\s|^)vi(?:m[<=>]?\d+|m)?|[\t\x20]*ex)\s*[:]\s*(.*)(?m:$)`)
	reVimLang       = regexp.MustCompile(`(?i:filetype|ft|syntax)\s*=(\w+)(?:\s|:|$)`)
)

// GetLanguagesByEmacsModeline returns a slice of possible languages for the given content, filename and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByEmacsModeline(filename string, content []byte, candidates []string) []string {
	matched := reEmacsModeline.FindAllSubmatch(content, -1)
	if matched == nil {
		return nil
	}

	// only take the last matched line, discard previous lines
	lastLineMatched := matched[len(matched)-1][1]
	matchedAlias := reEmacsLang.FindSubmatch(lastLineMatched)
	var alias string
	if matchedAlias != nil {
		alias = string(matchedAlias[1])
	} else {
		alias = string(lastLineMatched)
	}

	language, ok := GetLanguageByAlias(alias)
	if !ok {
		return nil
	}

	return []string{language}
}

// GetLanguagesByVimModeline returns a slice of possible languages for the given content, filename and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByVimModeline(filename string, content []byte, candidates []string) []string {
	matched := reVimModeline.FindAllSubmatch(content, -1)
	if matched == nil {
		return nil
	}

	// only take the last matched line, discard previous lines
	lastLineMatched := matched[len(matched)-1][1]
	matchedAlias := reVimLang.FindAllSubmatch(lastLineMatched, -1)
	if matchedAlias == nil {
		return nil
	}

	alias := string(matchedAlias[0][1])
	if len(matchedAlias) > 1 {
		// cases:
		// matchedAlias = [["syntax=ruby " "ruby"] ["ft=python " "python"] ["filetype=perl " "perl"]] returns OtherLanguage;
		// matchedAlias = [["syntax=python " "python"] ["ft=python " "python"] ["filetype=python " "python"]] returns "Python";
		for _, match := range matchedAlias {
			otherAlias := string(match[1])
			if otherAlias != alias {
				return nil
			}
		}
	}

	language, ok := GetLanguageByAlias(alias)
	if !ok {
		return nil
	}

	return []string{language}
}

// GetLanguagesByFilename returns a slice of possible languages for the given filename, content and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByFilename(filename string, content []byte, candidates []string) []string {
	return data.LanguagesByFilename[filepath.Base(filename)]
}

// GetLanguagesByShebang returns a slice of possible languages for the given content, filename and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByShebang(filename string, content []byte, candidates []string) (languages []string) {
	interpreter := getInterpreter(content)
	return data.LanguagesByInterpreter[interpreter]
}

var (
	shebangExecHack = regexp.MustCompile(`exec (\w+).+\$0.+\$@`)
	pythonVersion   = regexp.MustCompile(`python\d\.\d+`)
)

func getInterpreter(data []byte) (interpreter string) {
	line := getFirstLine(data)
	if !hasShebang(line) {
		return ""
	}

	// skip shebang
	line = bytes.TrimSpace(line[2:])
	splitted := bytes.Fields(line)
	if len(splitted) == 0 {
		return ""
	}

	if bytes.Contains(splitted[0], []byte("env")) {
		if len(splitted) > 1 {
			interpreter = string(splitted[1])
		}
	} else {
		splittedPath := bytes.Split(splitted[0], []byte{'/'})
		interpreter = string(splittedPath[len(splittedPath)-1])
	}

	if interpreter == "sh" {
		interpreter = lookForMultilineExec(data)
	}

	if pythonVersion.MatchString(interpreter) {
		interpreter = interpreter[:strings.Index(interpreter, `.`)]
	}

	return
}

func getFirstLine(data []byte) []byte {
	buf := bufio.NewScanner(bytes.NewReader(data))
	buf.Scan()
	line := buf.Bytes()
	if err := buf.Err(); err != nil {
		return nil
	}

	return line
}

func hasShebang(line []byte) bool {
	const shebang = `#!`
	prefix := []byte(shebang)
	return bytes.HasPrefix(line, prefix)
}

func lookForMultilineExec(data []byte) string {
	const magicNumOfLines = 5
	interpreter := "sh"

	buf := bufio.NewScanner(bytes.NewReader(data))
	for i := 0; i < magicNumOfLines && buf.Scan(); i++ {
		line := buf.Bytes()
		if shebangExecHack.Match(line) {
			interpreter = shebangExecHack.FindStringSubmatch(string(line))[1]
			break
		}
	}

	if err := buf.Err(); err != nil {
		return interpreter
	}

	return interpreter
}

// GetLanguagesByExtension returns a slice of possible languages for the given filename, content and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByExtension(filename string, content []byte, candidates []string) []string {
	if !strings.Contains(filename, ".") {
		return nil
	}

	filename = strings.ToLower(filename)
	dots := getDotIndexes(filename)
	for _, dot := range dots {
		ext := filename[dot:]
		languages, ok := data.LanguagesByExtension[ext]
		if ok {
			return languages
		}
	}

	return nil
}

func getDotIndexes(filename string) []int {
	dots := make([]int, 0, 2)
	for i, letter := range filename {
		if letter == rune('.') {
			dots = append(dots, i)
		}
	}

	return dots
}

// GetLanguagesByContent returns a slice of possible languages for the given content, filename and candidates
// will be ignored. It complies with the signature to be a Strategy type.
func GetLanguagesByContent(filename string, content []byte, candidates []string) []string {
	ext := strings.ToLower(filepath.Ext(filename))
	fnMatcher, ok := data.ContentMatchers[ext]
	if !ok {
		return nil
	}

	return fnMatcher(content)
}

// GetLanguagesByClassifier uses DefaultClassifier as a Classifier and returns a sorted slice of possible languages ordered by
// decreasing language's probability. If there are not candidates it returns nil. It complies with the signature to be a Strategy type.
func GetLanguagesByClassifier(filename string, content []byte, candidates []string) (languages []string) {
	if len(candidates) == 0 {
		return nil
	}

	return GetLanguagesBySpecificClassifier(content, candidates, DefaultClassifier)
}

// GetLanguagesBySpecificClassifier returns a slice of possible languages. It takes in a Classifier to be used.
func GetLanguagesBySpecificClassifier(content []byte, candidates []string, classifier Classifier) (languages []string) {
	mapCandidates := make(map[string]float64)
	for _, candidate := range candidates {
		mapCandidates[candidate]++
	}

	return classifier.Classify(content, mapCandidates)
}

// GetLanguageExtensions returns the different extensions being used by the language.
func GetLanguageExtensions(language string) []string {
	return data.ExtensionsByLanguage[language]
}

// Type represent language's type. Either data, programming, markup, prose, or unknown.
type Type int

// Type's values.
const (
	Unknown Type = iota
	Data
	Programming
	Markup
	Prose
)

// GetLanguageType returns the type of the given language.
func GetLanguageType(language string) (langType Type) {
	intType, ok := data.LanguagesType[language]
	langType = Type(intType)
	if !ok {
		langType = Unknown
	}
	return langType
}

// GetLanguageByAlias returns either the language related to the given alias and ok set to true
// or Otherlanguage and ok set to false if the alias is not recognized.
func GetLanguageByAlias(alias string) (lang string, ok bool) {
	a := strings.Split(alias, `,`)[0]
	a = strings.ToLower(a)
	lang, ok = data.LanguagesByAlias[a]
	if !ok {
		lang = OtherLanguage
	}

	return
}
<|endoftext|>


<commit_before>package gosrc

import (
	"fmt"
	"path/filepath"
	"sync"
	"time"

	"github.com/mb0/lab/ws"
)

var FlagGo uint64 = 1 << 16

type PkgFlag uint64

const (
	Found PkgFlag = 1 << iota

	Scanned

	HasSource
	HasTest
	HasXTest

	Watching
	Recursing
)

type Pkg struct {
	sync.Mutex
	Id   ws.Id
	Path string
	Dir  string
	Name string
	Flag PkgFlag
	Pkgs []*Pkg
	Src  *Info
	Test *Info
}

type Src struct {
	sync.RWMutex
	srcids []ws.Id
	pkgs   map[ws.Id]*Pkg
	queue  *ws.Throttle
	rmchan chan *ws.Res
	w      *ws.Ws
}

func New(srcids []ws.Id) *Src {
	return &Src{
		srcids: srcids,
		pkgs:   make(map[ws.Id]*Pkg),
		queue: &ws.Throttle{
			Tickers: make(chan *time.Ticker, 1),
			Ticks:   time.Second / 2,
		},
		rmchan: make(chan *ws.Res),
	}
}

func (s *Src) Filter(r *ws.Res) bool {
	if r.Flag&ws.FlagDir == 0 {
		if filepath.Ext(r.Name) == ".go" {
			r.Flag |= FlagGo
		}
		return false
	}
	if r.Parent.Flag&FlagGo != 0 {
		if r.Name != "testdata" && r.Name[0] != '_' {
			r.Flag |= FlagGo
		}
		return false
	}
	if r.Name == "pkg" || r.Name == "src" {
		for _, id := range s.srcids {
			if r.Id == id {
				r.Flag |= FlagGo
				break
			}
		}
	}
	return false
}

func (s *Src) Handle(op ws.Op, r *ws.Res) {
	if r.Flag&FlagGo == 0 {
		return
	}
	if r.Flag&ws.FlagDir != 0 {
		switch op & ws.WsMask {
		case ws.Change:
			s.queue.Add(r)
		case ws.Remove:
			s.rmchan <- r
		}
		return
	}
	if op&ws.FsMask != 0 && r.Parent.Flag&FlagGo != 0 {
		s.queue.Add(r.Parent)
	}
	return
}
func (s *Src) Run(w *ws.Ws) {
	s.w = w
	var timeout <-chan time.Time
	for {
		select {
		case t := <-s.queue.Tickers:
			if timeout = nil; t != nil {
				timeout = t.C
			}
		case <-timeout:
			for _, r := range s.queue.Work() {
				s.change(r)
			}
		case r := <-s.rmchan:
			s.queue.Delete(r)
			s.remove(r)
		}
	}
}

func (s *Src) WorkOn(path string) error {
	flag := Watching
	if d, f := filepath.Split(path); f == "..." {
		path = d
		flag |= Recursing
	}
	p, err := filepath.Abs(path)
	if err != nil {
		return err
	}
	id := ws.NewId(p)
	s.Lock()
	defer s.Unlock()
	pkg := s.pkgs[id]
	if pkg == nil {
		pkg := &Pkg{Flag: flag}
		pkg.Id = id
		s.pkgs[id] = pkg
		fmt.Printf("watching %s\n", p)
		return nil
	}
	pkg.Flag |= flag
	if s.w != nil {
		if r := s.w.Res(pkg.Id); r != nil {
			s.work(pkg, r)
		}
	}
	return nil
}

func (s *Src) work(p *Pkg, r *ws.Res) error {
	Scan(p, r)
	if p.Flag&Recursing != 0 {
		for _, c := range p.Pkgs {
			if c.Flag&Watching != 0 {
				continue
			}
			c.Flag |= Watching | Recursing
			cr := getchild(r, c.Id)
			if cr != nil {
				s.work(c, cr)
			}
		}
	}
	return nil
}
func getchild(r *ws.Res, id ws.Id) *ws.Res {
	for _, c := range r.Children {
		if c.Id == id {
			return c
		}
	}
	return nil
}
func scanpath(r *ws.Res, buf []byte) []byte {
	if r.Parent.Parent.Flag&FlagGo != 0 {
		buf = scanpath(r.Parent, buf)
		buf = append(buf, '/')
	}
	return append(buf, []byte(r.Name)...)
}
func importPath(r *ws.Res) string {
	return string(scanpath(r, make([]byte, 0, 64)))
}
func (s *Src) change(r *ws.Res) {
	s.Lock()
	defer s.Unlock()
	p := s.getorcreate(r)
	if p.Flag&Watching != 0 {
		// enqueue build and test run
		s.work(p, r)
	}
}
func (s *Src) getorcreate(r *ws.Res) *Pkg {
	p, ok := s.pkgs[r.Id]
	if !ok {
		p = &Pkg{Id: r.Id}
		s.pkgs[r.Id] = p
	}
	if p.Flag&Found == 0 {
		p.Flag |= Found
		p.Path = importPath(r)
		if r.Parent.Parent.Flag&FlagGo != 0 {
			pa := s.getorcreate(r.Parent)
			pa.Pkgs = append(pa.Pkgs, p)
		}
	}
	return p
}
func (s *Src) remove(r *ws.Res) {
	s.Lock()
	defer s.Unlock()
	p, ok := s.pkgs[r.Id]
	if !ok {
		return
	}
	// clean up p
	delete(s.pkgs, p.Id)
}
<commit_msg>reorder declarations<commit_after>package gosrc

import (
	"fmt"
	"path/filepath"
	"sync"
	"time"

	"github.com/mb0/lab/ws"
)

var FlagGo uint64 = 1 << 16

type PkgFlag uint64

const (
	_ PkgFlag = 1 << iota

	Watching
	Recursing

	Found   // Path, Dir
	Scanned // Name, Src, Test

	HasSource
	HasTest
	HasXTest
)

type Pkg struct {
	sync.Mutex
	Id   ws.Id
	Flag PkgFlag
	Pkgs []*Pkg

	Path string
	Dir  string

	Name string
	Src  *Info
	Test *Info
}

type Src struct {
	sync.RWMutex
	srcids []ws.Id
	pkgs   map[ws.Id]*Pkg
	queue  *ws.Throttle
	rmchan chan *ws.Res
	w      *ws.Ws
}

func New(srcids []ws.Id) *Src {
	return &Src{
		srcids: srcids,
		pkgs:   make(map[ws.Id]*Pkg),
		queue: &ws.Throttle{
			Tickers: make(chan *time.Ticker, 1),
			Ticks:   time.Second / 2,
		},
		rmchan: make(chan *ws.Res),
	}
}

func (s *Src) Filter(r *ws.Res) bool {
	if r.Flag&ws.FlagDir == 0 {
		if filepath.Ext(r.Name) == ".go" {
			r.Flag |= FlagGo
		}
		return false
	}
	if r.Parent.Flag&FlagGo != 0 {
		if r.Name != "testdata" && r.Name[0] != '_' {
			r.Flag |= FlagGo
		}
		return false
	}
	if r.Name == "pkg" || r.Name == "src" {
		for _, id := range s.srcids {
			if r.Id == id {
				r.Flag |= FlagGo
				break
			}
		}
	}
	return false
}

func (s *Src) Handle(op ws.Op, r *ws.Res) {
	if r.Flag&FlagGo == 0 {
		return
	}
	if r.Flag&ws.FlagDir != 0 {
		switch op & ws.WsMask {
		case ws.Change:
			s.queue.Add(r)
		case ws.Remove:
			s.rmchan <- r
		}
		return
	}
	if op&ws.FsMask != 0 && r.Parent.Flag&FlagGo != 0 {
		s.queue.Add(r.Parent)
	}
	return
}

func (s *Src) Run(w *ws.Ws) {
	s.w = w
	var timeout <-chan time.Time
	for {
		select {
		case t := <-s.queue.Tickers:
			if timeout = nil; t != nil {
				timeout = t.C
			}
		case <-timeout:
			for _, r := range s.queue.Work() {
				s.change(r)
			}
		case r := <-s.rmchan:
			s.queue.Delete(r)
			s.remove(r)
		}
	}
}
func (s *Src) change(r *ws.Res) {
	s.Lock()
	defer s.Unlock()
	p := s.getorcreate(r)
	if p.Flag&Watching != 0 {
		// enqueue build and test run
		Scan(p, r)
	}
}
func (s *Src) remove(r *ws.Res) {
	s.Lock()
	defer s.Unlock()
	p, ok := s.pkgs[r.Id]
	if !ok {
		return
	}
	// clean up p
	delete(s.pkgs, p.Id)
}
func (s *Src) getorcreate(r *ws.Res) *Pkg {
	p, ok := s.pkgs[r.Id]
	if !ok {
		p = &Pkg{Id: r.Id, Dir: r.Path()}
		s.pkgs[r.Id] = p
	}
	if p.Flag&Found == 0 {
		p.Flag |= Found
		p.Path = importPath(r)
		if r.Parent.Parent.Flag&FlagGo != 0 {
			pa := s.getorcreate(r.Parent)
			if pa.Flag&Recursing != 0 {
				p.Flag |= Watching | Recursing
			}
			pa.Pkgs = append(pa.Pkgs, p)
		}
	}
	return p
}

func (s *Src) WorkOn(path string) error {
	flag := Watching
	if d, f := filepath.Split(path); f == "..." {
		path = d
		flag |= Recursing
	}
	p, err := filepath.Abs(path)
	if err != nil {
		return err
	}
	id := ws.NewId(p)
	s.Lock()
	defer s.Unlock()
	pkg := s.pkgs[id]
	if pkg == nil {
		pkg := &Pkg{Flag: flag}
		pkg.Id = id
		s.pkgs[id] = pkg
		fmt.Printf("watching %s\n", p)
		return nil
	}
	pkg.Flag |= flag
	if s.w != nil {
		if r := s.w.Res(pkg.Id); r != nil {
			scanAll(pkg, r)
		}
	}
	return nil
}
func scanAll(p *Pkg, r *ws.Res) error {
	Scan(p, r)
	if p.Flag&Recursing != 0 {
		for _, c := range p.Pkgs {
			if c.Flag&Watching != 0 {
				continue
			}
			c.Flag |= Watching | Recursing
			if cr := getchild(r, c.Id); cr != nil {
				scanAll(c, cr)
			}
		}
	}
	return nil
}
func getchild(r *ws.Res, id ws.Id) *ws.Res {
	for _, c := range r.Children {
		if c.Id == id {
			return c
		}
	}
	return nil
}

func importPath(r *ws.Res) string {
	return string(scanpath(r, make([]byte, 0, 64)))
}
func scanpath(r *ws.Res, buf []byte) []byte {
	if r.Parent.Parent.Flag&FlagGo != 0 {
		buf = scanpath(r.Parent, buf)
		buf = append(buf, '/')
	}
	return append(buf, []byte(r.Name)...)
}
<|endoftext|>


<commit_before>package gqt_test

import (
	"io"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

var _ = Describe("Security", func() {
	var (
		client *runner.RunningGarden
		args   []string
	)

	BeforeEach(func() {
		args = []string{}
	})

	JustBeforeEach(func() {
		client = startGarden(args...)
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	Describe("Apparmor", func() {
		Context("when the --apparmor flag is pointing to a loaded policy", func() {
			BeforeEach(func() {
				args = append(args, "--apparmor", "garden-default")
			})

			It("should enforce the policy when running processes in unprivileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("garden-default"))
			})

			It("should not enforce the policy when running processes in privileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{
					Privileged: true,
				})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("unconfined"))
			})
		})

		Context("when the --apparmor flag is pointing to a non-existing policy", func() {
			BeforeEach(func() {
				args = append(args, "--apparmor", "non-existing-policy")
			})

			It("should return an error when creating a container", func() {
				_, err := client.Create(garden.ContainerSpec{})
				Expect(err).To(HaveOccurred())
			})
		})
	})
})
<commit_msg>add integration tests for cases where apparmor is disabled<commit_after>package gqt_test

import (
	"io"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

var _ = Describe("Security", func() {
	var (
		client *runner.RunningGarden
		args   []string
	)

	BeforeEach(func() {
		args = []string{}
	})

	JustBeforeEach(func() {
		client = startGarden(args...)
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	Describe("Apparmor", func() {
		Context("when the --apparmor flag is pointing to a loaded policy", func() {
			BeforeEach(func() {
				args = append(args, "--apparmor", "garden-default")
			})

			It("should enforce the policy when running processes in unprivileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("garden-default"))
			})

			It("should not enforce the policy when running processes in privileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{
					Privileged: true,
				})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("unconfined"))
			})
		})

		Context("when the --apparmor flag is not set", func() {
			It("should not enforce the policy when running processes in unprivileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("unconfined"))
			})

			It("should not enforce the policy when running processes in privileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{
					Privileged: true,
				})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("unconfined"))
			})
		})

		Context("when the --apparmor flag is pointing to a non-existing policy", func() {
			BeforeEach(func() {
				args = append(args, "--apparmor", "non-existing-policy")
			})

			It("should return an error when creating a container", func() {
				_, err := client.Create(garden.ContainerSpec{})
				Expect(err).To(HaveOccurred())
			})
		})
	})
})
<|endoftext|>


<commit_before>package grayt

import (
	"image"
	"sync"
)

type accumulator struct {
	pixels []pixel
	wide   int
	high   int
}

func newAccumulator(wide, high int) *accumulator {
	return &accumulator{
		pixels: make([]pixel, wide*high),
		wide:   wide,
		high:   high,
	}
}

func (a *accumulator) add(x, y int, c Colour, index int) {
	i := y*a.wide + x
	a.pixels[i].mu.Lock()
	a.pixels[i].add(c, index)
	a.pixels[i].mu.Unlock()
}

func (a *accumulator) get(x, y int) Colour {
	i := y*a.wide + x
	return a.pixels[i].colourSum
}

func (a *accumulator) mean() float64 {
	var sum float64
	for _, c := range a.pixels {
		c := c.colourSum
		sum += c.R + c.G + c.B
	}
	return sum / float64(len(a.pixels)) / 3.0
}

// ToImage converts the accumulator into an image. Exposure controls how bright
// the arithmetic mean brightness in the image is. A value of 1.0 results in a
// mean brightness half way between black and white.
func (a *accumulator) toImage(exposure float64) image.Image {
	const gamma = 2.2
	mean := a.mean()
	img := image.NewNRGBA(image.Rect(0, 0, a.wide, a.high))
	for x := 0; x < a.wide; x++ {
		for y := 0; y < a.high; y++ {
			img.Set(x, y, a.get(x, y).
				scale(0.5*exposure/mean).
				pow(1.0/gamma).
				toNRGBA())
		}
	}
	return img
}

type pixel struct {
	mu        sync.Mutex
	colourSum Colour
	nextIndex int
	pending   []pendingColour
}

type pendingColour struct {
	colour Colour
	index  int
}

func (e *pixel) add(c Colour, index int) {

	if e.nextIndex != index {
		e.pending = append(e.pending, pendingColour{c, index})
		return
	}

	e.colourSum = e.colourSum.add(c)
	e.nextIndex++

	for true {
		found := false
		for i := range e.pending {
			if e.pending[i].index == e.nextIndex {
				found = true
				e.colourSum = e.colourSum.add(e.pending[i].colour)
				e.nextIndex++
				e.pending[i] = e.pending[len(e.pending)-1]
				e.pending = e.pending[:len(e.pending)-1]
				break
			}
		}
		if !found {
			break
		}
	}
}
<commit_msg>Simplify accumulator<commit_after>package grayt

import (
	"image"
	"sync"
)

type accumulator struct {
	pixels []pixel
	wide   int
	high   int
}

type pixel struct {
	sync.Mutex
	Colour
}

func newAccumulator(wide, high int) *accumulator {
	return &accumulator{
		pixels: make([]pixel, wide*high),
		wide:   wide,
		high:   high,
	}
}

func (a *accumulator) add(x, y int, c Colour, index int) {
	i := y*a.wide + x
	a.pixels[i].Lock()
	a.pixels[i].Colour = a.pixels[i].add(c)
	a.pixels[i].Unlock()
}

func (a *accumulator) get(x, y int) Colour {
	i := y*a.wide + x
	return a.pixels[i].Colour
}

func (a *accumulator) mean() float64 {
	var sum float64
	for _, c := range a.pixels {
		sum += c.R + c.G + c.B
	}
	return sum / float64(len(a.pixels)) / 3.0
}

// ToImage converts the accumulator into an image. Exposure controls how bright
// the arithmetic mean brightness in the image is. A value of 1.0 results in a
// mean brightness half way between black and white.
func (a *accumulator) toImage(exposure float64) image.Image {
	const gamma = 2.2
	mean := a.mean()
	img := image.NewNRGBA(image.Rect(0, 0, a.wide, a.high))
	for x := 0; x < a.wide; x++ {
		for y := 0; y < a.high; y++ {
			img.Set(x, y, a.get(x, y).
				scale(0.5*exposure/mean).
				pow(1.0/gamma).
				toNRGBA())
		}
	}
	return img
}
<|endoftext|>


<commit_before>// Copyright 2016 The Upspin 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 gcp implements the user service upspin.KeyServer
// that runs on the Google Cloud Platform (GCP).
package gcp

import (
	"encoding/json"
	"math/big"
	"net"
	"strings"
	"sync"

	"upspin.io/cloud/storage"
	"upspin.io/errors"
	"upspin.io/factotum"
	"upspin.io/log"
	"upspin.io/upspin"
	"upspin.io/user"
	"upspin.io/valid"

	// We use GCS as the backing for our data.
	_ "upspin.io/cloud/storage/gcs"
)

// New initializes an instance of the key service.
// Required configuration options are listed at the package comments.
func New(options ...string) (upspin.KeyServer, error) {
	const op = "key/gcp.New"

	// All options are for the Storage layer.
	var storageOpts []storage.DialOpts
	for _, o := range options {
		storageOpts = append(storageOpts, storage.WithOptions(o))
	}

	s, err := storage.Dial("GCS", storageOpts...)
	if err != nil {
		return nil, errors.E(op, err)
	}
	log.Debug.Printf("Configured GCP user: %v", options)
	return &server{
		storage:   s,
		refCount:  &refCount{count: 1},
		lookupTXT: net.LookupTXT,
	}, nil
}

// server is the implementation of the KeyServer Service on GCP.
type server struct {
	storage storage.Storage
	*refCount

	// The name of the user accessing this server, set by Dial.
	user upspin.UserName

	// lookupTXT is a DNS lookup function that returns all TXT fields for a
	// domain. It should alias net.LookupTXT except in tests.
	lookupTXT func(domain string) ([]string, error)
}

var _ upspin.KeyServer = (*server)(nil)

type refCount struct {
	sync.Mutex
	count int
}

// userEntry is the on-disk representation of upspin.User, further annotated with
// non-public information, such as whether the user is an admin.
type userEntry struct {
	User    upspin.User
	IsAdmin bool
}

// Lookup implements upspin.KeyServer.
func (s *server) Lookup(name upspin.UserName) (*upspin.User, error) {
	const op = "key/gcp.Lookup"
	if err := valid.UserName(name); err != nil {
		return nil, errors.E(op, name, err)
	}
	entry, err := s.fetchUserEntry(op, name)
	if err != nil {
		return nil, err
	}
	return &entry.User, nil
}

// Put implements upspin.KeyServer.
func (s *server) Put(u *upspin.User) error {
	const op = "key/gcp.Put"
	if s.user == "" {
		return errors.E(op, errors.Internal, errors.Str("not bound to user"))
	}
	if err := valid.User(u); err != nil {
		return errors.E(op, err)
	}

	// Retrieve info about the user we want to Put.
	isAdmin := false
	newUser := false
	entry, err := s.fetchUserEntry(op, u.Name)
	switch {
	case errors.Match(errors.E(errors.NotExist), err):
		// OK; adding new user.
		newUser = true
	case err != nil:
		return err
	default:
		// User exists.
		isAdmin = entry.IsAdmin
	}

	if err := s.canPut(op, u.Name, newUser); err != nil {
		return err
	}

	// Set IsAdmin to what it was before or false by default.
	return s.putUserEntry(op, &userEntry{User: *u, IsAdmin: isAdmin})
}

// canPut reports whether the current logged-in user can Put the (new or
// existing) target user.
func (s *server) canPut(op string, target upspin.UserName, isTargetNew bool) error {
	name, suffix, domain, err := user.Parse(target)
	if err != nil {
		return errors.E(op, err)
	}
	// Do not allow * wildcard in name.
	if name == "*" {
		return errors.E(op, errors.Invalid, target, errors.Str("user has wildcard '*' in name"))
	}
	// If the current user is the same as target, it can proceed.
	if s.user == target {
		return nil
	}
	// For suffixed users, if the current user is the canonical user for
	// target, let it proceed.
	if suffix != "" {
		uname := name[:len(name)-(len("+")+len(suffix))]
		if s.user == upspin.UserName(uname+"@"+domain) {
			// Current user is the owner of target.
			return nil
		}
	}
	// Check whether the current user is a global admin.
	entry, err := s.fetchUserEntry(op, s.user)
	if err != nil {
		return err
	}
	if entry.IsAdmin {
		return nil
	}
	// Finally, for a newly-created user, check whether the logged-in user
	// owns the domain (and is thus allowed to create new users).
	if !isTargetNew {
		// Even domain admins cannot update their users.
		return errors.E(op, errors.Permission, s.user)
	}
	err = s.verifyOwns(s.user, entry.User.PublicKey, domain)
	if err == nil {
		// User owns the domain for the target user.
		return nil
	}
	return errors.E(op, errors.Permission, s.user, err)
}

// fetchUserEntry reads the user entry for a given user from permanent storage on GCP.
func (s *server) fetchUserEntry(op string, name upspin.UserName) (*userEntry, error) {
	log.Debug.Printf("%s: %s", op, name)
	b, err := s.storage.Download(string(name))
	if err != nil {
		log.Error.Printf("%s: error fetching %q: %v", op, name, err)
		return nil, errors.E(op, name, err)
	}
	var entry userEntry
	if err := json.Unmarshal(b, &entry); err != nil {
		return nil, errors.E(op, errors.Invalid, name, err)
	}
	return &entry, nil
}

// putUserEntry writes the user entry for a user to permanent storage on GCP.
func (s *server) putUserEntry(op string, entry *userEntry) error {
	log.Debug.Printf("%s: %s", op, entry.User.Name)
	if entry == nil {
		return errors.E(op, errors.Invalid, errors.Str("nil userEntry"))
	}
	b, err := json.Marshal(entry)
	if err != nil {
		return errors.E(op, errors.Invalid, err)
	}
	if _, err := s.storage.Put(string(entry.User.Name), b); err != nil {
		return errors.E(op, errors.IO, err)
	}
	return nil
}

// verifyOwns verifies whether the named user owns the given domain name, as
// per the domain name's upspin TXT field signature.
func (s *server) verifyOwns(u upspin.UserName, pubKey upspin.PublicKey, domain string) error {
	txts, err := s.lookupTXT(domain)
	if err != nil {
		return errors.E(errors.IO, err)
	}
	lastErr := errors.Str("not an administrator")
	const prefix = "upspin:"
	for _, txt := range txts {
		if len(txt) < len(prefix)+20 {
			// not enough data.
			continue
		}
		if !strings.HasPrefix(txt, prefix) {
			continue
		}
		// Is there a signature with two segments after the prefix?
		sigFields := strings.Split(txt[len(prefix):], "-")
		if len(sigFields) != 2 {
			continue
		}
		// Parse signature.
		var sig upspin.Signature
		var rs, ss big.Int
		if _, ok := rs.SetString(sigFields[0], 16); !ok {
			lastErr = errors.E(errors.Invalid, errors.Str("invalid signature field0"))
			continue
		}
		if _, ok := ss.SetString(sigFields[1], 16); !ok {
			lastErr = errors.E(errors.Invalid, errors.Str("invalid signature field1"))
			continue
		}
		sig.R = &rs
		sig.S = &ss

		log.Debug.Printf("Verifying if %q owns %q with pubKey: %q. Got sig: %q", u, domain, pubKey, txt[len(prefix):])
		msg := "upspin-domain:" + domain + "-" + string(u)
		lastErr = factotum.Verify([]byte(msg), sig, pubKey)
		if lastErr == nil {
			// Success!
			return nil
		}
	}
	return lastErr
}

// Dial implements upspin.Service.
func (s *server) Dial(ctx upspin.Context, e upspin.Endpoint) (upspin.Service, error) {
	s.refCount.Lock()
	s.refCount.count++
	s.refCount.Unlock()

	svc := *s
	svc.user = ctx.UserName()
	return &svc, nil
}

// Ping implements upspin.Service.
func (s *server) Ping() bool {
	return true
}

// Close implements upspin.Service.
func (s *server) Close() {
	// This instance is no longer tied to a user.
	s.user = ""

	s.refCount.Lock()
	defer s.refCount.Unlock()
	s.refCount.count--

	if s.refCount.count == 0 {
		if s.storage != nil {
			s.storage.Close()
		}
		s.storage = nil
	}
}

// Endpoint implements upspin.Service.
func (s *server) Endpoint() upspin.Endpoint {
	return upspin.Endpoint{} // No endpoint.
}
<commit_msg>key/gcp: return 'exists' error when trying to overwrite a user<commit_after>// Copyright 2016 The Upspin 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 gcp implements the user service upspin.KeyServer
// that runs on the Google Cloud Platform (GCP).
package gcp

import (
	"encoding/json"
	"math/big"
	"net"
	"strings"
	"sync"

	"upspin.io/cloud/storage"
	"upspin.io/errors"
	"upspin.io/factotum"
	"upspin.io/log"
	"upspin.io/upspin"
	"upspin.io/user"
	"upspin.io/valid"

	// We use GCS as the backing for our data.
	_ "upspin.io/cloud/storage/gcs"
)

// New initializes an instance of the key service.
// Required configuration options are listed at the package comments.
func New(options ...string) (upspin.KeyServer, error) {
	const op = "key/gcp.New"

	// All options are for the Storage layer.
	var storageOpts []storage.DialOpts
	for _, o := range options {
		storageOpts = append(storageOpts, storage.WithOptions(o))
	}

	s, err := storage.Dial("GCS", storageOpts...)
	if err != nil {
		return nil, errors.E(op, err)
	}
	log.Debug.Printf("Configured GCP user: %v", options)
	return &server{
		storage:   s,
		refCount:  &refCount{count: 1},
		lookupTXT: net.LookupTXT,
	}, nil
}

// server is the implementation of the KeyServer Service on GCP.
type server struct {
	storage storage.Storage
	*refCount

	// The name of the user accessing this server, set by Dial.
	user upspin.UserName

	// lookupTXT is a DNS lookup function that returns all TXT fields for a
	// domain. It should alias net.LookupTXT except in tests.
	lookupTXT func(domain string) ([]string, error)
}

var _ upspin.KeyServer = (*server)(nil)

type refCount struct {
	sync.Mutex
	count int
}

// userEntry is the on-disk representation of upspin.User, further annotated with
// non-public information, such as whether the user is an admin.
type userEntry struct {
	User    upspin.User
	IsAdmin bool
}

// Lookup implements upspin.KeyServer.
func (s *server) Lookup(name upspin.UserName) (*upspin.User, error) {
	const op = "key/gcp.Lookup"
	if err := valid.UserName(name); err != nil {
		return nil, errors.E(op, name, err)
	}
	entry, err := s.fetchUserEntry(op, name)
	if err != nil {
		return nil, err
	}
	return &entry.User, nil
}

// Put implements upspin.KeyServer.
func (s *server) Put(u *upspin.User) error {
	const op = "key/gcp.Put"
	if s.user == "" {
		return errors.E(op, errors.Internal, errors.Str("not bound to user"))
	}
	if err := valid.User(u); err != nil {
		return errors.E(op, err)
	}

	// Retrieve info about the user we want to Put.
	isAdmin := false
	newUser := false
	entry, err := s.fetchUserEntry(op, u.Name)
	switch {
	case errors.Match(errors.E(errors.NotExist), err):
		// OK; adding new user.
		newUser = true
	case err != nil:
		return err
	default:
		// User exists.
		isAdmin = entry.IsAdmin
	}

	if err := s.canPut(op, u.Name, newUser); err != nil {
		return err
	}

	// Set IsAdmin to what it was before or false by default.
	return s.putUserEntry(op, &userEntry{User: *u, IsAdmin: isAdmin})
}

// canPut reports whether the current logged-in user can Put the (new or
// existing) target user.
func (s *server) canPut(op string, target upspin.UserName, isTargetNew bool) error {
	name, suffix, domain, err := user.Parse(target)
	if err != nil {
		return errors.E(op, err)
	}
	// Do not allow * wildcard in name.
	if name == "*" {
		return errors.E(op, errors.Invalid, target, errors.Str("user has wildcard '*' in name"))
	}
	// If the current user is the same as target, it can proceed.
	if s.user == target {
		return nil
	}
	// For suffixed users, if the current user is the canonical user for
	// target, let it proceed.
	if suffix != "" {
		uname := name[:len(name)-(len("+")+len(suffix))]
		if s.user == upspin.UserName(uname+"@"+domain) {
			// Current user is the owner of target.
			return nil
		}
	}
	// Check whether the current user is a global admin.
	entry, err := s.fetchUserEntry(op, s.user)
	if err != nil {
		return err
	}
	if entry.IsAdmin {
		return nil
	}
	// Finally, for a newly-created user, check whether the logged-in user
	// owns the domain (and is thus allowed to create new users).
	if !isTargetNew {
		// Even domain admins cannot update their users.
		return errors.E(op, errors.Exist, s.user)
	}
	err = s.verifyOwns(s.user, entry.User.PublicKey, domain)
	if err == nil {
		// User owns the domain for the target user.
		return nil
	}
	return errors.E(op, errors.Permission, s.user, err)
}

// fetchUserEntry reads the user entry for a given user from permanent storage on GCP.
func (s *server) fetchUserEntry(op string, name upspin.UserName) (*userEntry, error) {
	log.Debug.Printf("%s: %s", op, name)
	b, err := s.storage.Download(string(name))
	if err != nil {
		log.Error.Printf("%s: error fetching %q: %v", op, name, err)
		return nil, errors.E(op, name, err)
	}
	var entry userEntry
	if err := json.Unmarshal(b, &entry); err != nil {
		return nil, errors.E(op, errors.Invalid, name, err)
	}
	return &entry, nil
}

// putUserEntry writes the user entry for a user to permanent storage on GCP.
func (s *server) putUserEntry(op string, entry *userEntry) error {
	log.Debug.Printf("%s: %s", op, entry.User.Name)
	if entry == nil {
		return errors.E(op, errors.Invalid, errors.Str("nil userEntry"))
	}
	b, err := json.Marshal(entry)
	if err != nil {
		return errors.E(op, errors.Invalid, err)
	}
	if _, err := s.storage.Put(string(entry.User.Name), b); err != nil {
		return errors.E(op, errors.IO, err)
	}
	return nil
}

// verifyOwns verifies whether the named user owns the given domain name, as
// per the domain name's upspin TXT field signature.
func (s *server) verifyOwns(u upspin.UserName, pubKey upspin.PublicKey, domain string) error {
	txts, err := s.lookupTXT(domain)
	if err != nil {
		return errors.E(errors.IO, err)
	}
	lastErr := errors.Str("not an administrator")
	const prefix = "upspin:"
	for _, txt := range txts {
		if len(txt) < len(prefix)+20 {
			// not enough data.
			continue
		}
		if !strings.HasPrefix(txt, prefix) {
			continue
		}
		// Is there a signature with two segments after the prefix?
		sigFields := strings.Split(txt[len(prefix):], "-")
		if len(sigFields) != 2 {
			continue
		}
		// Parse signature.
		var sig upspin.Signature
		var rs, ss big.Int
		if _, ok := rs.SetString(sigFields[0], 16); !ok {
			lastErr = errors.E(errors.Invalid, errors.Str("invalid signature field0"))
			continue
		}
		if _, ok := ss.SetString(sigFields[1], 16); !ok {
			lastErr = errors.E(errors.Invalid, errors.Str("invalid signature field1"))
			continue
		}
		sig.R = &rs
		sig.S = &ss

		log.Debug.Printf("Verifying if %q owns %q with pubKey: %q. Got sig: %q", u, domain, pubKey, txt[len(prefix):])
		msg := "upspin-domain:" + domain + "-" + string(u)
		lastErr = factotum.Verify([]byte(msg), sig, pubKey)
		if lastErr == nil {
			// Success!
			return nil
		}
	}
	return lastErr
}

// Dial implements upspin.Service.
func (s *server) Dial(ctx upspin.Context, e upspin.Endpoint) (upspin.Service, error) {
	s.refCount.Lock()
	s.refCount.count++
	s.refCount.Unlock()

	svc := *s
	svc.user = ctx.UserName()
	return &svc, nil
}

// Ping implements upspin.Service.
func (s *server) Ping() bool {
	return true
}

// Close implements upspin.Service.
func (s *server) Close() {
	// This instance is no longer tied to a user.
	s.user = ""

	s.refCount.Lock()
	defer s.refCount.Unlock()
	s.refCount.count--

	if s.refCount.count == 0 {
		if s.storage != nil {
			s.storage.Close()
		}
		s.storage = nil
	}
}

// Endpoint implements upspin.Service.
func (s *server) Endpoint() upspin.Endpoint {
	return upspin.Endpoint{} // No endpoint.
}
<|endoftext|>


<commit_before>package eyego

import (
	"fmt"
	"encoding/json"
	"io/ioutil"
)
type EyegoConfig struct {
	TargetDir string `json:"target_dir"`
	GoogleAPIKey string `json:"google_api_key"`
	Cards []Card `json:"cards"`
}

type Card struct {
	UploadKey string `json:"upload_key"`
	MacAddress string `json:"mac_address"`
}

var config EyegoConfig

func ConfigFrom(path string) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(b, &config)
	if err != nil {
		fmt.Print(err)
	}
}

func Config() EyegoConfig {
	return config
}

func DumpConfig(config EyegoConfig) {
	data, _ := json.Marshal(config)
	fmt.Printf("%s", data)
}

func GetCard(mac_address string) Card {
	for i := range config.Cards {
		if config.Cards[i].MacAddress == mac_address {
			return config.Cards[i]
		}
	}

	panic("no such card")
}
<commit_msg>improved error message<commit_after>package eyego

import (
	"fmt"
	"encoding/json"
	"io/ioutil"
)
type EyegoConfig struct {
	TargetDir string `json:"target_dir"`
	GoogleAPIKey string `json:"google_api_key"`
	Cards []Card `json:"cards"`
}

type Card struct {
	UploadKey string `json:"upload_key"`
	MacAddress string `json:"mac_address"`
}

var config EyegoConfig

func ConfigFrom(path string) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(b, &config)
	if err != nil {
		fmt.Print(err)
	}
}

func Config() EyegoConfig {
	return config
}

func DumpConfig(config EyegoConfig) {
	data, _ := json.Marshal(config)
	fmt.Printf("%s", data)
}

func GetCard(mac_address string) Card {
	for i := range config.Cards {
		if config.Cards[i].MacAddress == mac_address {
			return config.Cards[i]
		}
	}

	panic(fmt.Sprintf("no such card: %s", mac_address))
}
<|endoftext|>


<commit_before>package lib

import (
    "fmt"
    "os"
    "strconv"
    "time"

    . "gopkg.in/check.v1"
)

func (s *OssutilCommandSuite) rawSetBucketACL(bucket, acl string, force bool) (bool, error) {
    args := []string{CloudURLToString(bucket, ""), acl}
    showElapse, err := s.rawSetACLWithArgs(args, false, true, force)
    return showElapse, err
}

func (s *OssutilCommandSuite) rawSetObjectACL(bucket, object, acl string, recursive, force bool) (bool, error) {
    args := []string{CloudURLToString(bucket, object), acl}
    showElapse, err := s.rawSetACLWithArgs(args, recursive, false, force)
    return showElapse, err
}

func (s *OssutilCommandSuite) rawSetACLWithArgs(args []string, recursive, bucket, force bool) (bool, error) {
    command := "set-acl"
    str := ""
    routines := strconv.Itoa(Routines)
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
        "routines": &routines,
        "recursive": &recursive,
        "bucket": &bucket,
        "force": &force,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    time.Sleep(sleepTime)
    return showElapse, err
}

func (s *OssutilCommandSuite) TestSetBucketACL(c *C) {
    bucket := bucketNameExist

    // set acl
    for _, acl := range []string{"private", "public-read", "public-read-write"} {
        s.setBucketACL(bucket, acl, c)
        bucketStat := s.getStat(bucket, "", c)
        c.Assert(bucketStat[StatACL], Equals, acl)
    }
}

func (s *OssutilCommandSuite) TestSetBucketErrorACL(c *C) {
    bucket := bucketNameDest

    for _, acl := range []string{"default", "def", "erracl", "私有"} {
        showElapse, err := s.rawSetBucketACL(bucket, acl, false)
        c.Assert(err, NotNil)
        c.Assert(showElapse, Equals, false)

        showElapse, err = s.rawSetBucketACL(bucket, acl, true)
        c.Assert(err, NotNil)
        c.Assert(showElapse, Equals, false)

        bucketStat := s.getStat(bucket, "", c)
        c.Assert(bucketStat[StatACL], Equals, "private")
    }
}

func (s *OssutilCommandSuite) TestSetNotExistBucketACL(c *C) {
    bucket := bucketNamePrefix + "noexistsetacl"

    showElapse, err := s.rawGetStat(bucket, "")
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // set acl and create bucket
    showElapse, err = s.rawSetBucketACL(bucket, "public-read", true)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    time.Sleep(sleepTime)
    bucketStat := s.getStat(bucket, "", c)
    c.Assert(bucketStat[StatACL], Equals, "public-read")

    s.removeBucket(bucket, true, c)
    time.Sleep(3*sleepTime)

    // invalid bucket name
    bucket = "a"
    showElapse, err = s.rawSetBucketACL(bucket, "public-read", true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    showElapse, err = s.rawGetStat(bucket, "")
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestSetBucketEmptyACL(c *C) {
    bucket := bucketNamePrefix + "acl3"
    s.putBucket(bucket, c)
    time.Sleep(sleepTime)

    object := "test"
    s.putObject(bucket, object, uploadFileName, c)

    showElapse, err := s.rawSetObjectACL(bucket, object, "", false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)
}

func (s *OssutilCommandSuite) TestSetObjectACL(c *C) {
    bucket := bucketNameSetACL

    object := "TestSetObjectACL"

    // set acl to not exist object
    showElapse, err := s.rawSetObjectACL(bucket, object, "default", false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    object = "setacl-oldobject"
    s.putObject(bucket, object, uploadFileName, c)

    //get object acl
    objectStat := s.getStat(bucket, object, c)
    c.Assert(objectStat[StatACL], Equals, "default")

    object = "setacl-newobject"
    s.putObject(bucket, object, uploadFileName, c)

    // set acl
    for _, acl := range []string{"default", "private", "public-read", "public-read-write"} {
        s.setObjectACL(bucket, object, acl, false, true, c)
        objectStat = s.getStat(bucket, object, c)
        c.Assert(objectStat[StatACL], Equals, acl)
    }

    s.setObjectACL(bucket, object, "private", false, true, c)

    // set error acl
    for _, acl := range []string{"public_read", "erracl", "私有"} {
        showElapse, err = s.rawSetObjectACL(bucket, object, acl, false, false)
        c.Assert(showElapse, Equals, false)
        c.Assert(err, NotNil)

        objectStat = s.getStat(bucket, object, c)
        c.Assert(objectStat[StatACL], Equals, "private")
    }
}

func (s *OssutilCommandSuite) TestBatchSetObjectACL(c *C) {
    bucket := bucketNameSetACL1

    // put objects
    num := 2
    objectNames := []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("TestBatchSetObjectACL_setacl%d", i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }
    time.Sleep(time.Second)

    for _, object := range objectNames {
        objectStat := s.getStat(bucket, object, c)
        c.Assert(objectStat[StatACL], Equals, "default")
    }

    os.Stdout = out
    os.Stderr = errout

    // without --force option
    s.setObjectACL(bucket, "", "public-read-write", true, false, c)

    for _, object := range objectNames {
        objectStat := s.getStat(bucket, object, c)
        c.Assert(objectStat[StatACL], Equals, "default")
    }

    for _, acl := range []string{"public-read", "private", "public-read-write", "default"} {
        s.setObjectACL(bucket, "TestBatchSetObjectACL_setacl", acl, true, true, c)
        time.Sleep(sleepTime)

        for _, object := range objectNames {
            objectStat := s.getStat(bucket, object, c)
            c.Assert(objectStat[StatACL], Equals, acl)
        }
    }

    os.Stdout = testLogFile
    os.Stderr = testLogFile

    showElapse, err := s.rawSetObjectACL(bucket, "TestBatchSetObjectACL_setacl", "erracl", true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestErrSetACL(c *C) {
    acl := "private"
    args := []string{"os://", acl}
    showElapse, err := s.rawSetACLWithArgs(args, false, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    args = []string{"oss://", acl}
    showElapse, err = s.rawSetACLWithArgs(args, false, false, true)

    // error set bucket acl
    bucket := bucketNamePrefix + "set-acl"
    object := "testobject"
    args = []string{CloudURLToString(bucket, object), acl}
    showElapse, err = s.rawSetACLWithArgs(args, false, true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    args = []string{CloudURLToString(bucket, ""), acl}
    showElapse, err = s.rawSetACLWithArgs(args, true, true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // miss acl
    args = []string{CloudURLToString(bucket, "")}
    showElapse, err = s.rawSetACLWithArgs(args, false, true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    args = []string{CloudURLToString(bucket, object)}
    showElapse, err = s.rawSetACLWithArgs(args, false, true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    args = []string{CloudURLToString(bucket, object)}
    showElapse, err = s.rawSetACLWithArgs(args, true, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // miss object
    args = []string{CloudURLToString(bucket, ""), acl}
    showElapse, err = s.rawSetACLWithArgs(args, false, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // bad prefix
    showElapse, err = s.rawSetObjectACL(bucket, "/object", acl, true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestErrBatchSetACL(c *C) {
    bucket := bucketNameExist

    // put objects
    num := 10
    objectNames := []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("TestErrBatchSetACL_setacl:%d", i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }

    command := "set-acl"
    str := ""
    str1 := "abc"
    args := []string{CloudURLToString(bucket, ""), "public-read-write"}
    routines := strconv.Itoa(Routines)
    ok := true
    options := OptionMapType{
        "endpoint": &str1,
        "accessKeyID": &str1,
        "accessKeySecret": &str1,
        "stsToken": &str,
        "routines": &routines,
        "recursive": &ok,
        "force": &ok,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    for _, object := range objectNames {
        objectStat := s.getStat(bucket, object, c)
        c.Assert(objectStat[StatACL], Equals, "default")
    }
}

func (s *OssutilCommandSuite) TestSetACLIDKey(c *C) {
    bucket := bucketNameExist

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s[Bucket-Cname]\n%s=%s", "abc", "def", "ghi", bucket, "abc", bucket, "abc")
    s.createFile(cfile, data, c)

    command := "set-acl"
    str := ""
    args := []string{CloudURLToString(bucket, ""), "public-read"}
    routines := strconv.Itoa(Routines)
    ok := true
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
        "routines": &routines,
        "bucket": &ok,
        "force": &ok,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)

    options = OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &str,
        "configFile": &cfile,
        "routines": &routines,
        "bucket": &ok,
        "force": &ok,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}
<commit_msg>modify case<commit_after>package lib

import (
    "fmt"
    "os"
    "strconv"
    "time"

    . "gopkg.in/check.v1"
)

func (s *OssutilCommandSuite) rawSetBucketACL(bucket, acl string, force bool) (bool, error) {
    args := []string{CloudURLToString(bucket, ""), acl}
    showElapse, err := s.rawSetACLWithArgs(args, false, true, force)
    return showElapse, err
}

func (s *OssutilCommandSuite) rawSetObjectACL(bucket, object, acl string, recursive, force bool) (bool, error) {
    args := []string{CloudURLToString(bucket, object), acl}
    showElapse, err := s.rawSetACLWithArgs(args, recursive, false, force)
    return showElapse, err
}

func (s *OssutilCommandSuite) rawSetACLWithArgs(args []string, recursive, bucket, force bool) (bool, error) {
    command := "set-acl"
    str := ""
    routines := strconv.Itoa(Routines)
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
        "routines": &routines,
        "recursive": &recursive,
        "bucket": &bucket,
        "force": &force,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    time.Sleep(sleepTime)
    return showElapse, err
}

func (s *OssutilCommandSuite) TestSetBucketACL(c *C) {
    bucket := bucketNameExist

    // set acl
    for _, acl := range []string{"private", "public-read", "public-read-write"} {
        s.setBucketACL(bucket, acl, c)
        bucketStat := s.getStat(bucket, "", c)
        c.Assert(bucketStat[StatACL], Equals, acl)
    }
}

func (s *OssutilCommandSuite) TestSetBucketErrorACL(c *C) {
    bucket := bucketNameDest

    for _, acl := range []string{"default", "def", "erracl", "私有"} {
        showElapse, err := s.rawSetBucketACL(bucket, acl, false)
        c.Assert(err, NotNil)
        c.Assert(showElapse, Equals, false)

        showElapse, err = s.rawSetBucketACL(bucket, acl, true)
        c.Assert(err, NotNil)
        c.Assert(showElapse, Equals, false)

        bucketStat := s.getStat(bucket, "", c)
        c.Assert(bucketStat[StatACL], Equals, "private")
    }
}

func (s *OssutilCommandSuite) TestSetNotExistBucketACL(c *C) {
    bucket := bucketNamePrefix + "noexistsetacl"

    showElapse, err := s.rawGetStat(bucket, "")
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // set acl and create bucket
    showElapse, err = s.rawSetBucketACL(bucket, "public-read", true)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    time.Sleep(sleepTime)
    bucketStat := s.getStat(bucket, "", c)
    c.Assert(bucketStat[StatACL], Equals, "public-read")

    s.removeBucket(bucket, true, c)
    time.Sleep(3*sleepTime)

    // invalid bucket name
    bucket = "a"
    showElapse, err = s.rawSetBucketACL(bucket, "public-read", true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    showElapse, err = s.rawGetStat(bucket, "")
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestSetBucketEmptyACL(c *C) {
    bucket := bucketNamePrefix + "acl3"
    s.putBucket(bucket, c)
    time.Sleep(sleepTime)

    object := "test"
    s.putObject(bucket, object, uploadFileName, c)

    showElapse, err := s.rawSetObjectACL(bucket, object, "", false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)
}

func (s *OssutilCommandSuite) TestSetObjectACL(c *C) {
    bucket := bucketNameSetACL

    object := "TestSetObjectACL"

    // set acl to not exist object
    showElapse, err := s.rawSetObjectACL(bucket, object, "default", false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    object = "setacl-oldobject"
    s.putObject(bucket, object, uploadFileName, c)

    //get object acl
    objectStat := s.getStat(bucket, object, c)
    c.Assert(objectStat[StatACL], Equals, "default")

    object = "setacl-newobject"
    s.putObject(bucket, object, uploadFileName, c)

    // set acl
    for _, acl := range []string{"default", "private", "public-read", "public-read-write"} {
        s.setObjectACL(bucket, object, acl, false, true, c)
        objectStat = s.getStat(bucket, object, c)
        c.Assert(objectStat[StatACL], Equals, acl)
    }

    s.setObjectACL(bucket, object, "private", false, true, c)

    // set error acl
    for _, acl := range []string{"public_read", "erracl", "私有"} {
        showElapse, err = s.rawSetObjectACL(bucket, object, acl, false, false)
        c.Assert(showElapse, Equals, false)
        c.Assert(err, NotNil)

        objectStat = s.getStat(bucket, object, c)
        c.Assert(objectStat[StatACL], Equals, "private")
    }
}

func (s *OssutilCommandSuite) TestBatchSetObjectACL(c *C) {
    bucket := bucketNameSetACL1

    // put objects
    num := 2
    objectNames := []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("TestBatchSetObjectACL_setacl%d", i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }
    time.Sleep(sleepTime)

    for _, object := range objectNames {
        objectStat := s.getStat(bucket, object, c)
        c.Assert(objectStat[StatACL], Equals, "default")
    }

    // without --force option
    s.setObjectACL(bucket, "", "public-read-write", true, false, c)

    for _, object := range objectNames {
        objectStat := s.getStat(bucket, object, c)
        c.Assert(objectStat[StatACL], Equals, "default")
    }

    for _, acl := range []string{"public-read", "private", "public-read-write", "default"} {
        s.setObjectACL(bucket, "TestBatchSetObjectACL_setacl", acl, true, true, c)
        time.Sleep(sleepTime)

        for _, object := range objectNames {
            objectStat := s.getStat(bucket, object, c)
            c.Assert(objectStat[StatACL], Equals, acl)
        }
    }

    showElapse, err := s.rawSetObjectACL(bucket, "TestBatchSetObjectACL_setacl", "erracl", true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestErrSetACL(c *C) {
    acl := "private"
    args := []string{"os://", acl}
    showElapse, err := s.rawSetACLWithArgs(args, false, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    args = []string{"oss://", acl}
    showElapse, err = s.rawSetACLWithArgs(args, false, false, true)

    // error set bucket acl
    bucket := bucketNamePrefix + "set-acl"
    object := "testobject"
    args = []string{CloudURLToString(bucket, object), acl}
    showElapse, err = s.rawSetACLWithArgs(args, false, true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    args = []string{CloudURLToString(bucket, ""), acl}
    showElapse, err = s.rawSetACLWithArgs(args, true, true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // miss acl
    args = []string{CloudURLToString(bucket, "")}
    showElapse, err = s.rawSetACLWithArgs(args, false, true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    args = []string{CloudURLToString(bucket, object)}
    showElapse, err = s.rawSetACLWithArgs(args, false, true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    args = []string{CloudURLToString(bucket, object)}
    showElapse, err = s.rawSetACLWithArgs(args, true, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // miss object
    args = []string{CloudURLToString(bucket, ""), acl}
    showElapse, err = s.rawSetACLWithArgs(args, false, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // bad prefix
    showElapse, err = s.rawSetObjectACL(bucket, "/object", acl, true, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestErrBatchSetACL(c *C) {
    bucket := bucketNameExist

    // put objects
    num := 10
    objectNames := []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("TestErrBatchSetACL_setacl:%d", i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }

    command := "set-acl"
    str := ""
    str1 := "abc"
    args := []string{CloudURLToString(bucket, ""), "public-read-write"}
    routines := strconv.Itoa(Routines)
    ok := true
    options := OptionMapType{
        "endpoint": &str1,
        "accessKeyID": &str1,
        "accessKeySecret": &str1,
        "stsToken": &str,
        "routines": &routines,
        "recursive": &ok,
        "force": &ok,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    for _, object := range objectNames {
        objectStat := s.getStat(bucket, object, c)
        c.Assert(objectStat[StatACL], Equals, "default")
    }
}

func (s *OssutilCommandSuite) TestSetACLIDKey(c *C) {
    bucket := bucketNameExist

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s[Bucket-Cname]\n%s=%s", "abc", "def", "ghi", bucket, "abc", bucket, "abc")
    s.createFile(cfile, data, c)

    command := "set-acl"
    str := ""
    args := []string{CloudURLToString(bucket, ""), "public-read"}
    routines := strconv.Itoa(Routines)
    ok := true
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
        "routines": &routines,
        "bucket": &ok,
        "force": &ok,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)

    options = OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &str,
        "configFile": &cfile,
        "routines": &routines,
        "bucket": &ok,
        "force": &ok,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}
<|endoftext|>