<commit_before>package main

import (
	"bufio"
	"bytes"
	"io/ioutil"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"

	"gopkg.in/yaml.v2"
)

// readConfigfile creates the ConfigSettings struct from the g10k config file
func readConfigfile(configFile string) ConfigSettings {
	data, err := ioutil.ReadFile(configFile)
	if err != nil {
		Fatalf("readConfigfile(): There was an error parsing the config file " + configFile + ": " + err.Error())
	}

	//fmt.Println("data:", string(data))
	data = bytes.Replace(data, []byte(":cachedir:"), []byte("cachedir:"), -1)
	//fmt.Println("data:", string(data))
	var config ConfigSettings
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		Fatalf("YAML unmarshal error: " + err.Error())
	}

	//fmt.Println("config:", config)
	//fmt.Println("config ----- forge:", config.Forge)
	//for k, v := range config.Sources {
	//	fmt.Print(k)
	//	fmt.Print(v.Remote)
	//}

	// check if cachedir exists
	config.CacheDir = checkDirAndCreate(config.CacheDir, "cachedir")
	config.ForgeCacheDir = checkDirAndCreate(config.CacheDir+"forge/", "cachedir/forge")
	config.ModulesCacheDir = checkDirAndCreate(config.CacheDir+"modules/", "cachedir/modules")
	config.EnvCacheDir = checkDirAndCreate(config.CacheDir+"environments/", "cachedir/environments")

	if len(config.Forge.Baseurl) == 0 {
		config.Forge.Baseurl = "https://forgeapi.puppetlabs.com"
	}

	//fmt.Println("Forge Baseurl: ", config.Forge.Baseurl)

	// set default timeout to 5 seconds if no timeout setting found
	if config.Timeout == 0 {
		config.Timeout = 5
	}

	return config
}

// preparePuppetfile remove whitespace and comment lines from the given Puppetfile and merges Puppetfile resources that are identified with having a , at the end
func preparePuppetfile(pf string) string {
	file, err := os.Open(pf)
	if err != nil {
		Fatalf("preparePuppetfile(): Error while opening Puppetfile " + pf + " Error: " + err.Error())
	}
	defer file.Close()

	reComment := regexp.MustCompile("^\\s*#")
	reEmpty := regexp.MustCompile("^$")

	pfString := ""
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		if !reComment.MatchString(line) && !reEmpty.MatchString(line) {
			if strings.Contains(line, "#") {
				Debugf("found inline comment in " + pf + "line: " + line)
				line = strings.Split(line, "#")[0]
			}
			if regexp.MustCompile(",\\s*$").MatchString(line) {
				pfString += line
				Debugf("adding line:" + line)
			} else {
				pfString += line + "\n"
				Debugf("adding line:" + line)
			}
		}
	}
	if err := scanner.Err(); err != nil {
		Fatalf("preparePuppetfile(): Error while scanning Puppetfile " + pf + " Error: " + err.Error())
	}

	return pfString
}

// readPuppetfile creates the ConfigSettings struct from the Puppetfile
func readPuppetfile(pf string, sshKey string, source string) Puppetfile {
	var puppetFile Puppetfile
	puppetFile.privateKey = sshKey
	puppetFile.source = source
	puppetFile.forgeModules = map[string]ForgeModule{}
	puppetFile.gitModules = map[string]GitModule{}
	Debugf("readPuppetfile(): Trying to parse: " + pf)

	n := preparePuppetfile(pf)

	reModuledir := regexp.MustCompile("^\\s*(?:moduledir)\\s+['\"]?([^'\"]+)['\"]?")
	reForgeCacheTtl := regexp.MustCompile("^\\s*(?:forge.cacheTtl)\\s+['\"]?([^'\"]+)['\"]?")
	reForgeBaseURL := regexp.MustCompile("^\\s*(?:forge.baseUrl)\\s+['\"]?([^'\"]+)['\"]?")
	reForgeModule := regexp.MustCompile("^\\s*(?:mod)\\s+['\"]?([^'\"]+/[^'\"]+)['\"](?:\\s*(,)\\s*['\"]?([^'\"]*))?")
	reGitModule := regexp.MustCompile("^\\s*(?:mod)\\s+['\"]?([^'\"/]+)['\"]\\s*,(.*)")
	reGitAttribute := regexp.MustCompile("\\s*:(git|commit|tag|branch|ref|link|ignore[-_]unreachable|fallback)\\s*=>\\s*['\"]?([^'\"]+)['\"]?")
	//moduleName := ""
	//nextLineAttr := false

	for _, line := range strings.Split(n, "\n") {
		//fmt.Println("found line ---> ", line)
		if strings.Count(line, ":git") > 1 || strings.Count(line, ":tag") > 1 || strings.Count(line, ":branch") > 1 || strings.Count(line, ":ref") > 1 || strings.Count(line, ":link") > 1 {
			Fatalf("Error: trailing comma found in " + pf + " somewhere here: " + line)
		}
		if m := reModuledir.FindStringSubmatch(line); len(m) > 1 {
			puppetFile.moduleDir = m[1]
		} else if m := reForgeBaseURL.FindStringSubmatch(line); len(m) > 1 {
			puppetFile.forgeBaseURL = m[1]
			//fmt.Println("found forge base URL parameter ---> ", m[1])
		} else if m := reForgeCacheTtl.FindStringSubmatch(line); len(m) > 1 {
			ttl, err := time.ParseDuration(m[1])
			if err != nil {
				Fatalf("Error: Can not convert value " + m[1] + " of parameter " + m[0] + " to a golang Duration. Valid time units are 300ms, 1.5h or 2h45m. In " + pf + " line: " + line)
			}
			puppetFile.forgeCacheTtl = ttl
		} else if m := reForgeModule.FindStringSubmatch(line); len(m) > 1 {
			//fmt.Println("found forge mod name ---> ", m[1])
			comp := strings.Split(m[1], "/")
			if len(comp) != 2 {
				Fatalf("Error: Forge module name is invalid + should be like puppetlabs/apt + but is:" + m[3] + "in" + pf + "line: " + line)
			}
			if _, ok := puppetFile.forgeModules[m[1]]; ok {
				Fatalf("Error: Duplicate forge module found in " + pf + " for module " + m[1] + " line: " + line)
			}
			if len(m[3]) > 1 {
				if m[3] == ":latest" {
					puppetFile.forgeModules[m[1]] = ForgeModule{version: "latest", name: comp[1], author: comp[0]}
				} else {
					puppetFile.forgeModules[m[1]] = ForgeModule{version: m[3], name: comp[1], author: comp[0]}
				}
				//fmt.Println("found m[1] ---> '", m[1], "'")
				//fmt.Println("found forge mod attribute ---> ", m[3])
			} else {
				//puppetFile.forgeModules[m[1]] = ForgeModule{}
				puppetFile.forgeModules[m[1]] = ForgeModule{version: "present", name: comp[1], author: comp[0]}
			}
		} else if m := reGitModule.FindStringSubmatch(line); len(m) > 1 {
			//fmt.Println("found git mod name ---> ", m[1])
			if strings.Contains(m[1], "-") {
				Warnf("Warning: Found invalid character '-' in Puppet module name " + m[1] + " in " + pf + " line: " + line)
			}
			if len(m[2]) > 1 {
				gitModuleAttributes := m[2]
				//fmt.Println("found git mod attribute ---> ", gitModuleAttributes)
				if strings.Count(gitModuleAttributes, ":git") < 1 {
					Fatalf("Error: Missing :git url in " + pf + " for module " + m[1] + " line: " + line)
				}
				if strings.Count(gitModuleAttributes, ",") > 3 {
					Fatalf("Error: Too many attributes in " + pf + " for module " + m[1] + " line: " + line)
				}
				if _, ok := puppetFile.gitModules[m[1]]; ok {
					Fatalf("Error: Duplicate module found in " + pf + " for module " + m[1] + " line: " + line)
				}
				puppetFile.gitModules[m[1]] = GitModule{}
				gm := GitModule{}
				gitModuleAttributesArray := strings.Split(gitModuleAttributes, ",")
				//fmt.Println("found git mod attribute array ---> ", gitModuleAttributesArray)
				//fmt.Println("len(gitModuleAttributesArray) --> ", len(gitModuleAttributesArray))
				for i := 0; i <= strings.Count(gitModuleAttributes, ","); i++ {
					//fmt.Println("i -->", i)
					if i >= len(gitModuleAttributesArray) {
						Fatalf("Error: Trailing comma or invalid setting for module found in " + pf + " for module " + m[1] + " line: " + line)
					}
					a := reGitAttribute.FindStringSubmatch(gitModuleAttributesArray[i])
					//fmt.Println("a -->", a)
					if len(a) == 0 {
						Fatalf("Error: Trailing comma or invalid setting for module found in " + pf + " for module " + m[1] + " line: " + line)
					}
					if a[1] == "git" {
						gm.git = a[2]
					} else if a[1] == "branch" {
						gm.branch = a[2]
					} else if a[1] == "tag" {
						gm.tag = a[2]
					} else if a[1] == "commit" {
						gm.commit = a[2]
					} else if a[1] == "ref" {
						gm.ref = a[2]
					} else if a[1] == "link" {
						link, err := strconv.ParseBool(a[2])
						if err != nil {
							Fatalf("Error: Can not convert value " + a[2] + " of parameter " + a[1] + " to boolean. In " + pf + " for module " + m[1] + " line: " + line)
						}
						gm.link = link
					} else if a[1] == "ignore-unreachable" || a[1] == "ignore_unreachable" {
						ignoreUnreachable, err := strconv.ParseBool(a[2])
						if err != nil {
							Fatalf("Error: Can not convert value " + a[2] + " of parameter " + a[1] + " to boolean. In " + pf + " for module " + m[1] + " line: " + line)
						}
						gm.ignoreUnreachable = ignoreUnreachable
					} else if a[1] == "fallback" {
						mapSize := strings.Count(a[2], "|") + 1
						gm.fallback = make([]string, mapSize)
						for i, fallbackBranch := range strings.Split(a[2], "|") {
							//fmt.Println("--------> ", i, strings.TrimSpace(fallbackBranch))
							gm.fallback[i] = strings.TrimSpace(fallbackBranch)
						}
					}

				}
				puppetFile.gitModules[m[1]] = gm
			}
		}

	}
	// check if we need to set defaults
	if len(puppetFile.moduleDir) == 0 {
		puppetFile.moduleDir = "modules"
	}
	//fmt.Println(puppetFile)
	return puppetFile
}
<commit_msg>add force_forge_versions support and fail for conflicting Git module attributes<commit_after>package main

import (
	"bufio"
	"bytes"
	"io/ioutil"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"

	"gopkg.in/yaml.v2"
)

// readConfigfile creates the ConfigSettings struct from the g10k config file
func readConfigfile(configFile string) ConfigSettings {
	data, err := ioutil.ReadFile(configFile)
	if err != nil {
		Fatalf("readConfigfile(): There was an error parsing the config file " + configFile + ": " + err.Error())
	}

	//fmt.Println("data:", string(data))
	data = bytes.Replace(data, []byte(":cachedir:"), []byte("cachedir:"), -1)
	//fmt.Println("data:", string(data))
	var config ConfigSettings
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		Fatalf("YAML unmarshal error: " + err.Error())
	}

	//fmt.Println("config:", config)
	//fmt.Println("config ----- forge:", config.Forge)
	//for k, v := range config.Sources {
	//	fmt.Print(k)
	//	fmt.Print(v.Remote)
	//}

	// check if cachedir exists
	config.CacheDir = checkDirAndCreate(config.CacheDir, "cachedir")
	config.ForgeCacheDir = checkDirAndCreate(config.CacheDir+"forge/", "cachedir/forge")
	config.ModulesCacheDir = checkDirAndCreate(config.CacheDir+"modules/", "cachedir/modules")
	config.EnvCacheDir = checkDirAndCreate(config.CacheDir+"environments/", "cachedir/environments")

	if len(config.Forge.Baseurl) == 0 {
		config.Forge.Baseurl = "https://forgeapi.puppetlabs.com"
	}

	//fmt.Println("Forge Baseurl: ", config.Forge.Baseurl)

	// set default timeout to 5 seconds if no timeout setting found
	if config.Timeout == 0 {
		config.Timeout = 5
	}

	return config
}

// preparePuppetfile remove whitespace and comment lines from the given Puppetfile and merges Puppetfile resources that are identified with having a , at the end
func preparePuppetfile(pf string) string {
	file, err := os.Open(pf)
	if err != nil {
		Fatalf("preparePuppetfile(): Error while opening Puppetfile " + pf + " Error: " + err.Error())
	}
	defer file.Close()

	reComment := regexp.MustCompile("^\\s*#")
	reEmpty := regexp.MustCompile("^$")

	pfString := ""
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if !reComment.MatchString(line) && !reEmpty.MatchString(line) {
			if strings.Contains(line, "#") {
				Debugf("found inline comment in " + pf + "line: " + line)
				line = strings.Split(line, "#")[0]
			}
			if regexp.MustCompile(",\\s*$").MatchString(line) {
				pfString += line
				Debugf("adding line:" + line)
			} else {
				pfString += line + "\n"
				Debugf("adding line:" + line)
			}
		}
	}
	if err := scanner.Err(); err != nil {
		Fatalf("preparePuppetfile(): Error while scanning Puppetfile " + pf + " Error: " + err.Error())
	}

	return pfString
}

// readPuppetfile creates the ConfigSettings struct from the Puppetfile
func readPuppetfile(pf string, sshKey string, source string, forceForgeVersions bool) Puppetfile {
	var puppetFile Puppetfile
	puppetFile.privateKey = sshKey
	puppetFile.source = source
	puppetFile.forgeModules = map[string]ForgeModule{}
	puppetFile.gitModules = map[string]GitModule{}
	Debugf("readPuppetfile(): Trying to parse: " + pf)

	n := preparePuppetfile(pf)

	reModuledir := regexp.MustCompile("^\\s*(?:moduledir)\\s+['\"]?([^'\"]+)['\"]?")
	reForgeCacheTtl := regexp.MustCompile("^\\s*(?:forge.cacheTtl)\\s+['\"]?([^'\"]+)['\"]?")
	reForgeBaseURL := regexp.MustCompile("^\\s*(?:forge.baseUrl)\\s+['\"]?([^'\"]+)['\"]?")
	reForgeModule := regexp.MustCompile("^\\s*(?:mod)\\s+['\"]?([^'\"]+/[^'\"]+)['\"](?:\\s*(,)\\s*['\"]?([^'\"]*))?")
	reGitModule := regexp.MustCompile("^\\s*(?:mod)\\s+['\"]?([^'\"/]+)['\"]\\s*,(.*)")
	reGitAttribute := regexp.MustCompile("\\s*:(git|commit|tag|branch|ref|link|ignore[-_]unreachable|fallback)\\s*=>\\s*['\"]?([^'\"]+)['\"]?")
	reUniqueGitAttribute := regexp.MustCompile("\\s*:(?:commit|tag|branch|ref|link)\\s*=>")
	//moduleName := ""
	//nextLineAttr := false

	for _, line := range strings.Split(n, "\n") {
		//fmt.Println("found line ---> ", line)
		if strings.Count(line, ":git") > 1 || strings.Count(line, ":tag") > 1 || strings.Count(line, ":branch") > 1 || strings.Count(line, ":ref") > 1 || strings.Count(line, ":link") > 1 {
			Fatalf("Error: trailing comma found in " + pf + " somewhere here: " + line)
		}
		if m := reModuledir.FindStringSubmatch(line); len(m) > 1 {
			puppetFile.moduleDir = m[1]
		} else if m := reForgeBaseURL.FindStringSubmatch(line); len(m) > 1 {
			puppetFile.forgeBaseURL = m[1]
			//fmt.Println("found forge base URL parameter ---> ", m[1])
		} else if m := reForgeCacheTtl.FindStringSubmatch(line); len(m) > 1 {
			ttl, err := time.ParseDuration(m[1])
			if err != nil {
				Fatalf("Error: Can not convert value " + m[1] + " of parameter " + m[0] + " to a golang Duration. Valid time units are 300ms, 1.5h or 2h45m. In " + pf + " line: " + line)
			}
			puppetFile.forgeCacheTtl = ttl
		} else if m := reForgeModule.FindStringSubmatch(line); len(m) > 1 {
			//fmt.Println("found forge mod name ---> ", m[1])
			comp := strings.Split(m[1], "/")
			if len(comp) != 2 {
				Fatalf("Error: Forge module name is invalid + should be like puppetlabs/apt + but is:" + m[3] + "in" + pf + "line: " + line)
			}
			if _, ok := puppetFile.forgeModules[m[1]]; ok {
				Fatalf("Error: Duplicate forge module found in " + pf + " for module " + m[1] + " line: " + line)
			}
			if len(m[3]) > 1 {
				if m[3] == ":latest" {
					if forceForgeVersions {
						Fatalf("Error: Found latest setting for forge module in " + pf + " for module " + m[1] + " line: " + line + " and force_forge_versions is set to true! Please specify a version (e.g. '2.3.0')")
					}
					puppetFile.forgeModules[m[1]] = ForgeModule{version: "latest", name: comp[1], author: comp[0]}
				} else {
					puppetFile.forgeModules[m[1]] = ForgeModule{version: m[3], name: comp[1], author: comp[0]}
				}
				//fmt.Println("found m[1] ---> '", m[1], "'")
				//fmt.Println("found forge mod attribute ---> ", m[3])
			} else {
				//puppetFile.forgeModules[m[1]] = ForgeModule{}
				if forceForgeVersions {
					Fatalf("Error: Found present setting for forge module in " + pf + " for module " + m[1] + " line: " + line + " and force_forge_versions is set to true! Please specify a version (e.g. '2.3.0')")
				}
				puppetFile.forgeModules[m[1]] = ForgeModule{version: "present", name: comp[1], author: comp[0]}
			}
		} else if m := reGitModule.FindStringSubmatch(line); len(m) > 1 {
			gitModuleName := m[1]
			//fmt.Println("found git mod name ---> ", gitModuleName)
			if strings.Contains(gitModuleName, "-") {
				Warnf("Warning: Found invalid character '-' in Puppet module name " + gitModuleName + " in " + pf + " line: " + line)
			}
			if len(m[2]) > 1 {
				gitModuleAttributes := m[2]
				//fmt.Println("found git mod attribute ---> ", gitModuleAttributes)
				if strings.Count(gitModuleAttributes, ":git") < 1 {
					Fatalf("Error: Missing :git url in " + pf + " for module " + gitModuleName + " line: " + line)
				}
				if strings.Count(gitModuleAttributes, ",") > 3 {
					Fatalf("Error: Too many attributes in " + pf + " for module " + gitModuleName + " line: " + line)
				}
				if _, ok := puppetFile.gitModules[gitModuleName]; ok {
					Fatalf("Error: Duplicate module found in " + pf + " for module " + gitModuleName + " line: " + line)
				}
				gas := reUniqueGitAttribute.FindAllStringSubmatch(gitModuleAttributes, -1)
				cga := ""
				if len(gas) > 1 {
					for _, ga := range gas {
						cga += strings.TrimSpace(strings.Replace(ga[0], "=>", "", -1)) + ", "
					}
					Fatalf("Error: Found conflicting git attributes " + cga + "in " + pf + " for module " + gitModuleName + " line: " + line)
				}
				puppetFile.gitModules[gitModuleName] = GitModule{}
				gm := GitModule{}
				gitModuleAttributesArray := strings.Split(gitModuleAttributes, ",")
				//fmt.Println("found git mod attribute array ---> ", gitModuleAttributesArray)
				//fmt.Println("len(gitModuleAttributesArray) --> ", len(gitModuleAttributesArray))
				for i := 0; i <= strings.Count(gitModuleAttributes, ","); i++ {
					//fmt.Println("i -->", i)
					if i >= len(gitModuleAttributesArray) {
						Fatalf("Error: Trailing comma or invalid setting for module found in " + pf + " for module " + gitModuleName + " line: " + line)
					}
					a := reGitAttribute.FindStringSubmatch(gitModuleAttributesArray[i])
					//fmt.Println("a -->", a)
					if len(a) == 0 {
						Fatalf("Error: Trailing comma or invalid setting for module found in " + pf + " for module " + gitModuleName + " line: " + line)
					}
					gitModuleAttribute := a[1]
					if gitModuleAttribute == "git" {
						gm.git = a[2]
					} else if gitModuleAttribute == "branch" {
						gm.branch = a[2]
					} else if gitModuleAttribute == "tag" {
						gm.tag = a[2]
					} else if gitModuleAttribute == "commit" {
						gm.commit = a[2]
					} else if gitModuleAttribute == "ref" {
						gm.ref = a[2]
					} else if gitModuleAttribute == "link" {
						link, err := strconv.ParseBool(a[2])
						if err != nil {
							Fatalf("Error: Can not convert value " + a[2] + " of parameter " + gitModuleAttribute + " to boolean. In " + pf + " for module " + gitModuleName + " line: " + line)
						}
						gm.link = link
					} else if gitModuleAttribute == "ignore-unreachable" || gitModuleAttribute == "ignore_unreachable" {
						ignoreUnreachable, err := strconv.ParseBool(a[2])
						if err != nil {
							Fatalf("Error: Can not convert value " + a[2] + " of parameter " + gitModuleAttribute + " to boolean. In " + pf + " for module " + gitModuleName + " line: " + line)
						}
						gm.ignoreUnreachable = ignoreUnreachable
					} else if gitModuleAttribute == "fallback" {
						mapSize := strings.Count(a[2], "|") + 1
						gm.fallback = make([]string, mapSize)
						for i, fallbackBranch := range strings.Split(a[2], "|") {
							//fmt.Println("--------> ", i, strings.TrimSpace(fallbackBranch))
							gm.fallback[i] = strings.TrimSpace(fallbackBranch)
						}
					}

				}
				puppetFile.gitModules[gitModuleName] = gm
			}
		}

	}
	// check if we need to set defaults
	if len(puppetFile.moduleDir) == 0 {
		puppetFile.moduleDir = "modules"
	}
	//fmt.Println(puppetFile)
	return puppetFile
}
<|endoftext|>


<commit_before>package api

import (
	"net/http"

	"github.com/labstack/echo"
)

type Prefix struct {
	Background string   `json:"background"`
	Color      string   `json:"color"`
	Words      []string `json:"words"`
	TimedEvent bool     `json:"timedEvent"`
	Default    bool     `json:"default"`
}

var DefaultPrefixes = []Prefix{
	Prefix{
		Background: "4C6C9B",
		Color:      "FFFFFF",
		Words:      []string{"HW", "Read", "Reading"},
		Default:    true,
	},
	Prefix{
		Background: "9ACD32",
		Color:      "FFFFFF",
		Words:      []string{"Project"},
		Default:    true,
	},
	Prefix{
		Background: "C3A528",
		Color:      "FFFFFF",
		Words:      []string{"Report", "Essay", "Paper", "Write"},
		Default:    true,
	},
	Prefix{
		Background: "FFA500",
		Color:      "FFFFFF",
		Words:      []string{"Quiz", "PopQuiz"},
		Default:    true,
	},
	Prefix{
		Background: "DC143C",
		Color:      "FFFFFF",
		Words:      []string{"Test", "Final", "Exam", "Midterm"},
		Default:    true,
	},
	Prefix{
		Background: "2AC0F1",
		Color:      "FFFFFF",
		Words:      []string{"ICA"},
		Default:    true,
	},
	Prefix{
		Background: "2AF15E",
		Color:      "FFFFFF",
		Words:      []string{"Lab", "Study", "Memorize"},
		TimedEvent: true,
		Default:    true,
	},
	Prefix{
		Background: "003DAD",
		Color:      "FFFFFF",
		Words:      []string{"DocID"},
		Default:    true,
	},
	Prefix{
		Background: "000000",
		Color:      "00FF00",
		Words:      []string{"Trojun", "Hex"},
		Default:    true,
	},
	Prefix{
		Background: "5000BC",
		Color:      "FFFFFF",
		Words:      []string{"OptionalHW", "Challenge"},
		Default:    true,
	},
	Prefix{
		Background: "000099",
		Color:      "FFFFFF",
		Words:      []string{"Presentation", "Prez"},
		Default:    true,
	},
	Prefix{
		Background: "123456",
		Color:      "FFFFFF",
		Words:      []string{"BuildSession", "Build"},
		TimedEvent: true,
		Default:    true,
	},
	Prefix{
		Background: "5A1B87",
		Color:      "FFFFFF",
		Words:      []string{"Meeting", "Meet"},
		TimedEvent: true,
		Default:    true,
	},
}

type PrefixesResponse struct {
	Status             string   `json:"status"`
	Prefixes           []Prefix `json:"prefixes"`
	FallbackBackground string   `json:"fallbackBackground"`
	FallbackColor      string   `json:"fallbackColor"`
}

func InitPrefixesAPI(e *echo.Echo) {
	e.GET("/prefixes/getList", func(c echo.Context) error {
		if GetSessionUserID(&c) == -1 {
			return c.JSON(http.StatusUnauthorized, ErrorResponse{"error", "logged_out"})
		}

		return c.JSON(http.StatusOK, PrefixesResponse{"ok", DefaultPrefixes, "FFD3BD", "000000"})
	})
}
<commit_msg>add "begin" tag<commit_after>package api

import (
	"net/http"

	"github.com/labstack/echo"
)

type Prefix struct {
	Background string   `json:"background"`
	Color      string   `json:"color"`
	Words      []string `json:"words"`
	TimedEvent bool     `json:"timedEvent"`
	Default    bool     `json:"default"`
}

var DefaultPrefixes = []Prefix{
	Prefix{
		Background: "4C6C9B",
		Color:      "FFFFFF",
		Words:      []string{"HW", "Read", "Reading"},
		Default:    true,
	},
	Prefix{
		Background: "9ACD32",
		Color:      "FFFFFF",
		Words:      []string{"Project"},
		Default:    true,
	},
	Prefix{
		Background: "C3A528",
		Color:      "FFFFFF",
		Words:      []string{"Report", "Essay", "Paper", "Write"},
		Default:    true,
	},
	Prefix{
		Background: "FFA500",
		Color:      "FFFFFF",
		Words:      []string{"Quiz", "PopQuiz"},
		Default:    true,
	},
	Prefix{
		Background: "DC143C",
		Color:      "FFFFFF",
		Words:      []string{"Test", "Final", "Exam", "Midterm"},
		Default:    true,
	},
	Prefix{
		Background: "2AC0F1",
		Color:      "FFFFFF",
		Words:      []string{"ICA"},
		Default:    true,
	},
	Prefix{
		Background: "2AF15E",
		Color:      "FFFFFF",
		Words:      []string{"Lab", "Study", "Memorize"},
		TimedEvent: true,
		Default:    true,
	},
	Prefix{
		Background: "003DAD",
		Color:      "FFFFFF",
		Words:      []string{"DocID"},
		Default:    true,
	},
	Prefix{
		Background: "000000",
		Color:      "00FF00",
		Words:      []string{"Trojun", "Hex"},
		Default:    true,
	},
	Prefix{
		Background: "5000BC",
		Color:      "FFFFFF",
		Words:      []string{"OptionalHW", "Challenge"},
		Default:    true,
	},
	Prefix{
		Background: "000099",
		Color:      "FFFFFF",
		Words:      []string{"Presentation", "Prez"},
		Default:    true,
	},
	Prefix{
		Background: "123456",
		Color:      "FFFFFF",
		Words:      []string{"BuildSession", "Build"},
		TimedEvent: true,
		Default:    true,
	},
	Prefix{
		Background: "5A1B87",
		Color:      "FFFFFF",
		Words:      []string{"Meeting", "Meet"},
		TimedEvent: true,
		Default:    true,
	},
	Prefix{
		Background:	"01b501".
		Color:		"FFFFFF",
		Words:		[]string["Begin", "Start"],
		TimedEvent: true,
		Defualt:	true,
	}
}

type PrefixesResponse struct {
	Status             string   `json:"status"`
	Prefixes           []Prefix `json:"prefixes"`
	FallbackBackground string   `json:"fallbackBackground"`
	FallbackColor      string   `json:"fallbackColor"`
}

func InitPrefixesAPI(e *echo.Echo) {
	e.GET("/prefixes/getList", func(c echo.Context) error {
		if GetSessionUserID(&c) == -1 {
			return c.JSON(http.StatusUnauthorized, ErrorResponse{"error", "logged_out"})
		}

		return c.JSON(http.StatusOK, PrefixesResponse{"ok", DefaultPrefixes, "FFD3BD", "000000"})
	})
}
<|endoftext|>


<commit_before>package response

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"os"
	"path/filepath"

	"github.com/volatile/core"
	"github.com/volatile/core/httputil"
)

const templatesDir = "templates"

var (
	// ErrNoTemplatesDir is used when a template feature is used without having the templates directory.
	ErrNoTemplatesDir = fmt.Errorf("response: templates can't be used without a %q directory", templatesDir)

	templates *template.Template
)

func init() {
	if _, err := os.Stat(templatesDir); err != nil {
		return
	}

	templates = template.New(templatesDir)

	// Built-in templates funcs
	templates.Funcs(template.FuncMap{
		"html":  templatesFuncHTML,
		"nl2br": templatesFuncNL2BR,
	})

	core.BeforeRun(func() {
		if err := filepath.Walk(templatesDir, templatesWalk); err != nil {
			panic("response: " + err.Error())
		}
	})
}

// walk is the path/filepath.WalkFunc used to walk templatesDir in order to initialize templates.
// It will try to parse all files it encounters and recurse into subdirectories.
func templatesWalk(path string, f os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if f.IsDir() {
		return nil
	}

	_, err = templates.ParseFiles(path)
	return err
}

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of which the second has type error.
// In that case, if the second (error) argument evaluates to non-nil during execution, execution terminates and Execute returns that error.
// FuncMap has the same base type as FuncMap in "text/template", copied here so clients need not import "text/template".
type FuncMap map[string]interface{}

// TemplatesFuncs adds a function that will be available to all templates.
func TemplatesFuncs(funcMap FuncMap) {
	if templates == nil {
		panic(ErrNoTemplatesDir)
	}
	templates.Funcs(template.FuncMap(funcMap))
}

// Status responds with the status code.
func Status(c *core.Context, code int) {
	http.Error(c.ResponseWriter, http.StatusText(code), code)
}

// String responds with the string s.
func String(c *core.Context, s string) {
	StringStatus(c, http.StatusOK, s)
}

// StringStatus responds with the status code and the string s.
func StringStatus(c *core.Context, code int, s string) {
	httputil.SetDetectedContentType(c.ResponseWriter, []byte(s))
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write([]byte(s))
}

// Bytes responds with the slice of bytes b.
func Bytes(c *core.Context, b []byte) {
	BytesStatus(c, http.StatusOK, b)
}

// BytesStatus responds with the status code and the slice of bytes b.
func BytesStatus(c *core.Context, code int, b []byte) {
	httputil.SetDetectedContentType(c.ResponseWriter, b)
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// JSON responds with the JSON marshalled v.
func JSON(c *core.Context, v interface{}) {
	JSONStatus(c, http.StatusOK, v)
}

// JSONStatus responds with the status code and the JSON marshalled v.
func JSONStatus(c *core.Context, code int, v interface{}) {
	b, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}

	c.ResponseWriter.Header().Set("Content-Type", "application/json")
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// Template responds with the template associated to name.
func Template(c *core.Context, name string, data map[string]interface{}) {
	TemplateStatus(c, http.StatusOK, name, data)
}

// TemplateStatus responds with the status code and the template associated to name.
func TemplateStatus(c *core.Context, code int, name string, data map[string]interface{}) {
	c.ResponseWriter.Header().Set("Content-Type", "text/html; charsets=utf-8")
	c.ResponseWriter.WriteHeader(code)
	if err := ExecuteTemplate(c.ResponseWriter, c, name, data); err != nil {
		panic(err)
	}
}

// ExecuteTemplate works like the standard html/template.Template.ExecuteTemplate function but ensures that the context is part of the data.
func ExecuteTemplate(wr io.Writer, c *core.Context, name string, data map[string]interface{}) error {
	if templates == nil {
		return ErrNoTemplatesDir
	}

	if data == nil {
		data = make(map[string]interface{})
	}
	data["c"] = c

	return templates.ExecuteTemplate(wr, name, data)
}
<commit_msg>Fix template charset<commit_after>package response

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"os"
	"path/filepath"

	"github.com/volatile/core"
	"github.com/volatile/core/httputil"
)

const templatesDir = "templates"

var (
	// ErrNoTemplatesDir is used when a template feature is used without having the templates directory.
	ErrNoTemplatesDir = fmt.Errorf("response: templates can't be used without a %q directory", templatesDir)

	templates *template.Template
)

func init() {
	if _, err := os.Stat(templatesDir); err != nil {
		return
	}

	templates = template.New(templatesDir)

	// Built-in templates funcs
	templates.Funcs(template.FuncMap{
		"html":  templatesFuncHTML,
		"nl2br": templatesFuncNL2BR,
	})

	core.BeforeRun(func() {
		if err := filepath.Walk(templatesDir, templatesWalk); err != nil {
			panic("response: " + err.Error())
		}
	})
}

// walk is the path/filepath.WalkFunc used to walk templatesDir in order to initialize templates.
// It will try to parse all files it encounters and recurse into subdirectories.
func templatesWalk(path string, f os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if f.IsDir() {
		return nil
	}

	_, err = templates.ParseFiles(path)
	return err
}

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of which the second has type error.
// In that case, if the second (error) argument evaluates to non-nil during execution, execution terminates and Execute returns that error.
// FuncMap has the same base type as FuncMap in "text/template", copied here so clients need not import "text/template".
type FuncMap map[string]interface{}

// TemplatesFuncs adds a function that will be available to all templates.
func TemplatesFuncs(funcMap FuncMap) {
	if templates == nil {
		panic(ErrNoTemplatesDir)
	}
	templates.Funcs(template.FuncMap(funcMap))
}

// Status responds with the status code.
func Status(c *core.Context, code int) {
	http.Error(c.ResponseWriter, http.StatusText(code), code)
}

// String responds with the string s.
func String(c *core.Context, s string) {
	StringStatus(c, http.StatusOK, s)
}

// StringStatus responds with the status code and the string s.
func StringStatus(c *core.Context, code int, s string) {
	httputil.SetDetectedContentType(c.ResponseWriter, []byte(s))
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write([]byte(s))
}

// Bytes responds with the slice of bytes b.
func Bytes(c *core.Context, b []byte) {
	BytesStatus(c, http.StatusOK, b)
}

// BytesStatus responds with the status code and the slice of bytes b.
func BytesStatus(c *core.Context, code int, b []byte) {
	httputil.SetDetectedContentType(c.ResponseWriter, b)
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// JSON responds with the JSON marshalled v.
func JSON(c *core.Context, v interface{}) {
	JSONStatus(c, http.StatusOK, v)
}

// JSONStatus responds with the status code and the JSON marshalled v.
func JSONStatus(c *core.Context, code int, v interface{}) {
	b, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}

	c.ResponseWriter.Header().Set("Content-Type", "application/json")
	c.ResponseWriter.WriteHeader(code)
	c.ResponseWriter.Write(b)
}

// Template responds with the template associated to name.
func Template(c *core.Context, name string, data map[string]interface{}) {
	TemplateStatus(c, http.StatusOK, name, data)
}

// TemplateStatus responds with the status code and the template associated to name.
func TemplateStatus(c *core.Context, code int, name string, data map[string]interface{}) {
	c.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
	c.ResponseWriter.WriteHeader(code)
	if err := ExecuteTemplate(c.ResponseWriter, c, name, data); err != nil {
		panic(err)
	}
}

// ExecuteTemplate works like the standard html/template.Template.ExecuteTemplate function but ensures that the context is part of the data.
func ExecuteTemplate(wr io.Writer, c *core.Context, name string, data map[string]interface{}) error {
	if templates == nil {
		return ErrNoTemplatesDir
	}

	if data == nil {
		data = make(map[string]interface{})
	}
	data["c"] = c

	return templates.ExecuteTemplate(wr, name, data)
}
<|endoftext|>


<commit_before>//  Copyright (c) 2014 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 scorer

import (
	"fmt"
	"math"
	"reflect"

	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/search"
	"github.com/blevesearch/bleve/size"
)

var reflectStaticSizeTermQueryScorer int

func init() {
	var tqs TermQueryScorer
	reflectStaticSizeTermQueryScorer = int(reflect.TypeOf(tqs).Size())
}

type TermQueryScorer struct {
	queryTerm              string
	queryField             string
	queryBoost             float64
	docTerm                uint64
	docTotal               uint64
	idf                    float64
	options                search.SearcherOptions
	idfExplanation         *search.Explanation
	queryNorm              float64
	queryWeight            float64
	queryWeightExplanation *search.Explanation
}

func (s *TermQueryScorer) Size() int {
	sizeInBytes := reflectStaticSizeTermQueryScorer + size.SizeOfPtr +
		len(s.queryTerm) + len(s.queryField)

	if s.idfExplanation != nil {
		sizeInBytes += s.idfExplanation.Size()
	}

	if s.queryWeightExplanation != nil {
		sizeInBytes += s.queryWeightExplanation.Size()
	}

	return sizeInBytes
}

func NewTermQueryScorer(queryTerm []byte, queryField string, queryBoost float64, docTotal, docTerm uint64, options search.SearcherOptions) *TermQueryScorer {
	rv := TermQueryScorer{
		queryTerm:   string(queryTerm),
		queryField:  queryField,
		queryBoost:  queryBoost,
		docTerm:     docTerm,
		docTotal:    docTotal,
		idf:         1.0 + math.Log(float64(docTotal)/float64(docTerm+1.0)),
		options:     options,
		queryWeight: 1.0,
	}

	if options.Explain {
		rv.idfExplanation = &search.Explanation{
			Value:   rv.idf,
			Message: fmt.Sprintf("idf(docFreq=%d, maxDocs=%d)", docTerm, docTotal),
		}
	}

	return &rv
}

func (s *TermQueryScorer) Weight() float64 {
	sum := s.queryBoost * s.idf
	return sum * sum
}

func (s *TermQueryScorer) SetQueryNorm(qnorm float64) {
	s.queryNorm = qnorm

	// update the query weight
	s.queryWeight = s.queryBoost * s.idf * s.queryNorm

	if s.options.Explain {
		childrenExplanations := make([]*search.Explanation, 3)
		childrenExplanations[0] = &search.Explanation{
			Value:   s.queryBoost,
			Message: "boost",
		}
		childrenExplanations[1] = s.idfExplanation
		childrenExplanations[2] = &search.Explanation{
			Value:   s.queryNorm,
			Message: "queryNorm",
		}
		s.queryWeightExplanation = &search.Explanation{
			Value:    s.queryWeight,
			Message:  fmt.Sprintf("queryWeight(%s:%s^%f), product of:", s.queryField, s.queryTerm, s.queryBoost),
			Children: childrenExplanations,
		}
	}
}

func (s *TermQueryScorer) Score(ctx *search.SearchContext, termMatch *index.TermFieldDoc) *search.DocumentMatch {
	var scoreExplanation *search.Explanation

	// need to compute score
	var tf float64
	if termMatch.Freq < MaxSqrtCache {
		tf = SqrtCache[int(termMatch.Freq)]
	} else {
		tf = math.Sqrt(float64(termMatch.Freq))
	}
	score := tf * termMatch.Norm * s.idf

	if s.options.Explain {
		childrenExplanations := make([]*search.Explanation, 3)
		childrenExplanations[0] = &search.Explanation{
			Value:   tf,
			Message: fmt.Sprintf("tf(termFreq(%s:%s)=%d", s.queryField, s.queryTerm, termMatch.Freq),
		}
		childrenExplanations[1] = &search.Explanation{
			Value:   termMatch.Norm,
			Message: fmt.Sprintf("fieldNorm(field=%s, doc=%s)", s.queryField, termMatch.ID),
		}
		childrenExplanations[2] = s.idfExplanation
		scoreExplanation = &search.Explanation{
			Value:    score,
			Message:  fmt.Sprintf("fieldWeight(%s:%s in %s), product of:", s.queryField, s.queryTerm, termMatch.ID),
			Children: childrenExplanations,
		}
	}

	// if the query weight isn't 1, multiply
	if s.queryWeight != 1.0 {
		score = score * s.queryWeight
		if s.options.Explain {
			childExplanations := make([]*search.Explanation, 2)
			childExplanations[0] = s.queryWeightExplanation
			childExplanations[1] = scoreExplanation
			scoreExplanation = &search.Explanation{
				Value:    score,
				Message:  fmt.Sprintf("weight(%s:%s^%f in %s), product of:", s.queryField, s.queryTerm, s.queryBoost, termMatch.ID),
				Children: childExplanations,
			}
		}
	}

	rv := ctx.DocumentMatchPool.Get()
	rv.IndexInternalID = append(rv.IndexInternalID, termMatch.ID...)
	if s.options.Score != "none" {
		rv.Score = score
	}
	if s.options.Explain {
		rv.Expl = scoreExplanation
	}

	if len(termMatch.Vectors) > 0 {
		if cap(rv.FieldTermLocations) < len(termMatch.Vectors) {
			rv.FieldTermLocations = make([]search.FieldTermLocation, 0, len(termMatch.Vectors))
		}

		for _, v := range termMatch.Vectors {
			var ap search.ArrayPositions
			if len(v.ArrayPositions) > 0 {
				n := len(rv.FieldTermLocations)
				if n < cap(rv.FieldTermLocations) { // reuse ap slice if available
					ap = rv.FieldTermLocations[:n+1][n].Location.ArrayPositions[:0]
				}
				ap = append(ap, v.ArrayPositions...)
			}
			rv.FieldTermLocations =
				append(rv.FieldTermLocations, search.FieldTermLocation{
					Field: v.Field,
					Term:  s.queryTerm,
					Location: search.Location{
						Pos:            v.Pos,
						Start:          v.Start,
						End:            v.End,
						ArrayPositions: ap,
					},
				})
		}
	}

	return rv
}
<commit_msg>New flag within TermQueryScorer on whether to add score<commit_after>//  Copyright (c) 2014 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 scorer

import (
	"fmt"
	"math"
	"reflect"

	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/search"
	"github.com/blevesearch/bleve/size"
)

var reflectStaticSizeTermQueryScorer int

func init() {
	var tqs TermQueryScorer
	reflectStaticSizeTermQueryScorer = int(reflect.TypeOf(tqs).Size())
}

type TermQueryScorer struct {
	queryTerm              string
	queryField             string
	queryBoost             float64
	docTerm                uint64
	docTotal               uint64
	idf                    float64
	options                search.SearcherOptions
	idfExplanation         *search.Explanation
	includeScore           bool
	queryNorm              float64
	queryWeight            float64
	queryWeightExplanation *search.Explanation
}

func (s *TermQueryScorer) Size() int {
	sizeInBytes := reflectStaticSizeTermQueryScorer + size.SizeOfPtr +
		len(s.queryTerm) + len(s.queryField)

	if s.idfExplanation != nil {
		sizeInBytes += s.idfExplanation.Size()
	}

	if s.queryWeightExplanation != nil {
		sizeInBytes += s.queryWeightExplanation.Size()
	}

	return sizeInBytes
}

func NewTermQueryScorer(queryTerm []byte, queryField string, queryBoost float64, docTotal, docTerm uint64, options search.SearcherOptions) *TermQueryScorer {
	rv := TermQueryScorer{
		queryTerm:    string(queryTerm),
		queryField:   queryField,
		queryBoost:   queryBoost,
		docTerm:      docTerm,
		docTotal:     docTotal,
		idf:          1.0 + math.Log(float64(docTotal)/float64(docTerm+1.0)),
		options:      options,
		queryWeight:  1.0,
		includeScore: options.Score != "none",
	}

	if options.Explain {
		rv.idfExplanation = &search.Explanation{
			Value:   rv.idf,
			Message: fmt.Sprintf("idf(docFreq=%d, maxDocs=%d)", docTerm, docTotal),
		}
	}

	return &rv
}

func (s *TermQueryScorer) Weight() float64 {
	sum := s.queryBoost * s.idf
	return sum * sum
}

func (s *TermQueryScorer) SetQueryNorm(qnorm float64) {
	s.queryNorm = qnorm

	// update the query weight
	s.queryWeight = s.queryBoost * s.idf * s.queryNorm

	if s.options.Explain {
		childrenExplanations := make([]*search.Explanation, 3)
		childrenExplanations[0] = &search.Explanation{
			Value:   s.queryBoost,
			Message: "boost",
		}
		childrenExplanations[1] = s.idfExplanation
		childrenExplanations[2] = &search.Explanation{
			Value:   s.queryNorm,
			Message: "queryNorm",
		}
		s.queryWeightExplanation = &search.Explanation{
			Value:    s.queryWeight,
			Message:  fmt.Sprintf("queryWeight(%s:%s^%f), product of:", s.queryField, s.queryTerm, s.queryBoost),
			Children: childrenExplanations,
		}
	}
}

func (s *TermQueryScorer) Score(ctx *search.SearchContext, termMatch *index.TermFieldDoc) *search.DocumentMatch {
	var scoreExplanation *search.Explanation

	// need to compute score
	var tf float64
	if termMatch.Freq < MaxSqrtCache {
		tf = SqrtCache[int(termMatch.Freq)]
	} else {
		tf = math.Sqrt(float64(termMatch.Freq))
	}
	score := tf * termMatch.Norm * s.idf

	if s.options.Explain {
		childrenExplanations := make([]*search.Explanation, 3)
		childrenExplanations[0] = &search.Explanation{
			Value:   tf,
			Message: fmt.Sprintf("tf(termFreq(%s:%s)=%d", s.queryField, s.queryTerm, termMatch.Freq),
		}
		childrenExplanations[1] = &search.Explanation{
			Value:   termMatch.Norm,
			Message: fmt.Sprintf("fieldNorm(field=%s, doc=%s)", s.queryField, termMatch.ID),
		}
		childrenExplanations[2] = s.idfExplanation
		scoreExplanation = &search.Explanation{
			Value:    score,
			Message:  fmt.Sprintf("fieldWeight(%s:%s in %s), product of:", s.queryField, s.queryTerm, termMatch.ID),
			Children: childrenExplanations,
		}
	}

	// if the query weight isn't 1, multiply
	if s.queryWeight != 1.0 {
		score = score * s.queryWeight
		if s.options.Explain {
			childExplanations := make([]*search.Explanation, 2)
			childExplanations[0] = s.queryWeightExplanation
			childExplanations[1] = scoreExplanation
			scoreExplanation = &search.Explanation{
				Value:    score,
				Message:  fmt.Sprintf("weight(%s:%s^%f in %s), product of:", s.queryField, s.queryTerm, s.queryBoost, termMatch.ID),
				Children: childExplanations,
			}
		}
	}

	rv := ctx.DocumentMatchPool.Get()
	rv.IndexInternalID = append(rv.IndexInternalID, termMatch.ID...)
	if s.includeScore {
		rv.Score = score
	}
	if s.options.Explain {
		rv.Expl = scoreExplanation
	}

	if len(termMatch.Vectors) > 0 {
		if cap(rv.FieldTermLocations) < len(termMatch.Vectors) {
			rv.FieldTermLocations = make([]search.FieldTermLocation, 0, len(termMatch.Vectors))
		}

		for _, v := range termMatch.Vectors {
			var ap search.ArrayPositions
			if len(v.ArrayPositions) > 0 {
				n := len(rv.FieldTermLocations)
				if n < cap(rv.FieldTermLocations) { // reuse ap slice if available
					ap = rv.FieldTermLocations[:n+1][n].Location.ArrayPositions[:0]
				}
				ap = append(ap, v.ArrayPositions...)
			}
			rv.FieldTermLocations =
				append(rv.FieldTermLocations, search.FieldTermLocation{
					Field: v.Field,
					Term:  s.queryTerm,
					Location: search.Location{
						Pos:            v.Pos,
						Start:          v.Start,
						End:            v.End,
						ArrayPositions: ap,
					},
				})
		}
	}

	return rv
}
<|endoftext|>


<commit_before>// Copyright 2012 The Gorilla 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 securecookie

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/gob"
	"encoding/base64"
	"errors"
	"fmt"
	"hash"
	"strconv"
	"time"
)

// Codec defines an interface to encode and decode cookie values.
type Codec interface {
	Encode(name string, value interface{}) (string, error)
	Decode(name, value string) (interface{}, error)
}

// New returns a new SecureCookie.
//
// hashKey is required, used to authenticate values using HMAC. Create it using
// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes.
//
// blockKey is optional, used to encrypt values. Create it using
// GenerateRandomKey(). The key length must correspond to the block size
// of the encryption algorithm. For AES, used by default, valid lengths are
// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
func New(hashKey, blockKey []byte) *SecureCookie {
	s := &SecureCookie{
		hashKey:   hashKey,
		blockKey:  blockKey,
		hashFunc:  sha256.New,
		maxAge:    86400 * 30,
		maxLength: 4096,
	}
	if hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
	}
	if blockKey != nil {
		s.BlockFunc(aes.NewCipher)
	}
	return s
}

// SecureCookie encodes and decodes authenticated and optionally encrypted
// cookie values.
type SecureCookie struct {
	hashKey   []byte
	hashFunc  func() hash.Hash
	blockKey  []byte
	block     cipher.Block
	maxLength int
	maxAge    int64
	minAge    int64
	err       error
	// For testing purposes, the function that returns the current timestamp.
	// If not set, it will use time.Now().UTC().Unix().
	timeFunc func() int64
}

// MaxLength restricts the maximum length, in bytes, for the cookie value.
//
// Default is 4096, which is the maximum value accepted by Internet Explorer.
func (s *SecureCookie) MaxLength(value int) *SecureCookie {
	s.maxLength = value
	return s
}

// MaxAge restricts the maximum age, in seconds, for the cookie value.
//
// Default is 86400 * 30. Set it to 0 for no restriction.
func (s *SecureCookie) MaxAge(value int) *SecureCookie {
	s.maxAge = int64(value)
	return s
}

// MinAge restricts the minimum age, in seconds, for the cookie value.
//
// Default is 0 (no restriction).
func (s *SecureCookie) MinAge(value int) *SecureCookie {
	s.minAge = int64(value)
	return s
}

// HashFunc sets the hash algorithm used to create HMAC.
//
// Default is crypto/sha256.New.
func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie {
	s.hashFunc = f
	return s
}

// BlockFunc sets the encryption algorithm used to create cipher.Block.
//
// Default is crypto/aes.New.
func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie {
	if s.blockKey == nil {
		s.err = errors.New("securecookie: block key is not set")
	} else if block, err := f(s.blockKey); err == nil {
		s.block = block
	} else {
		s.err = err
	}
	return s
}

// Encode encodes a cookie value.
//
// It decodes, verifies a message authentication code, optionally decrypts and
// finally deserializes the value.
//
// The name argument is the cookie name. It is stored with the encoded value.
// The value argument is the map to be encoded.
func (s *SecureCookie) Encode(name string, value interface{}) (string, error) {
	if s.err != nil {
		return "", s.err
	}
	if s.hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
		return "", s.err
	}
	var err error
	var b []byte
	// 1. Serialize.
	if b, err = serialize(value); err != nil {
		return "", err
	}
	// 2. Encrypt and encode to base64 (optional).
	if s.block != nil {
		if b, err = encrypt(s.block, b); err != nil {
			return "", err
		}
		b = encode(b)
	}
	// 3. Create MAC for "date|name|value" and append the result.
	buf := bytes.NewBufferString(fmt.Sprintf("%d|%s|", s.timestamp(), name))
	buf.Write(b)
	mac := createMac(hmac.New(s.hashFunc, s.hashKey), buf.Bytes())
	buf.WriteString("|")
	buf.Write(mac)
	// 4. Encode to base64.
	b = encode(buf.Bytes())
	// 5. Check length.
	if s.maxLength != 0 && len(b) > s.maxLength {
		return "", errors.New("securecookie: the value is too long")
	}
	// Done.
	return string(b), nil
}

// Decode decodes a cookie value.
//
// It decodes, verifies a message authentication code, optionally decrypts and
// finally deserializes the value.
//
// The name argument is the cookie name. It must be the same name used when
// it was stored. The value argument is the encoded cookie value. The dst
// argument is where the cookie will be decoded. It must be a pointer.
func (s *SecureCookie) Decode(name, value string, dst interface{}) error {
	if s.err != nil {
		return s.err
	}
	if s.hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
		return s.err
	}
	// 1. Check length.
	if s.maxLength != 0 && len(value) > s.maxLength {
		return errors.New("securecookie: the value is too long")
	}
	// 2. Decode from base64.
	b, err := decode([]byte(value))
	if err != nil {
		return err
	}
	// 3. Value is "date|name|value|mac". Split.
	parts := bytes.SplitN(b, []byte("|"), 4)
	if len(parts) != 4 {
		return errors.New("securecookie: invalid value")
	}
	// 4. Verify name against parts[1] and date ranges against parts[0].
	if name != string(parts[1]) {
		return errors.New("securecookie: invalid name")
	}
	var t1 int64
	if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
		return errors.New("securecookie: invalid timestamp")
	}
	t2 := s.timestamp()
	if s.minAge != 0 && t1 > t2-s.minAge {
		return errors.New("securecookie: timestamp is too new")
	}
	if s.maxAge != 0 && t1 < t2-s.maxAge {
		return errors.New("securecookie: expired timestamp")
	}
	// 5. Verify MAC: "date|name|parts[2]" against parts[3].
	h := hmac.New(s.hashFunc, s.hashKey)
	if err = verifyMac(h, b[:len(b)-len(parts[3])-1], parts[3]); err != nil {
		return err
	}
	// 6. Decode from base64 and decrypt parts[2] (optional).
	if s.block != nil {
		if b, err = decode(parts[2]); err != nil {
			return err
		}
		if b, err = decrypt(s.block, b); err != nil {
			return err
		}
	}
	// 7. Deserialize.
	if err = deserialize(b, dst); err != nil {
		return err
	}
	// Done.
	return nil
}

// timestamp returns the current timestamp, in seconds.
//
// For testing purposes, the function that generates the timestamp can be
// overridden. If not set, it will return time.Now().UTC().Unix().
func (s *SecureCookie) timestamp() int64 {
	if s.timeFunc == nil {
		return time.Now().UTC().Unix()
	}
	return s.timeFunc()
}

// Authentication -------------------------------------------------------------

// createMac creates a message authentication code (MAC).
func createMac(h hash.Hash, value []byte) []byte {
	h.Write(value)
	return h.Sum(nil)
}

// verifyMac verifies that a message authentication code (MAC) is valid.
func verifyMac(h hash.Hash, value []byte, mac []byte) error {
	mac2 := createMac(h, value)
	if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 {
		return nil
	}
	return errors.New("securecookie: the value is not valid")
}

// Encryption -----------------------------------------------------------------

// encrypt encrypts a value using the given block in counter mode.
//
// A random initialization vector with the length of the block size is
// prepended to the resulting ciphertext.
func encrypt(block cipher.Block, value []byte) ([]byte, error) {
	// Initialization vector on wikipedia: http://goo.gl/zF67k
	iv := GenerateRandomKey(block.BlockSize())
	if iv == nil {
		return nil, errors.New("securecookie: failed to generate random iv")
	}
	// Encrypt it.
	stream := cipher.NewCTR(block, iv)
	stream.XORKeyStream(value, value)
	// Return iv + ciphertext.
	return append(iv, value...), nil
}

// decrypt decrypts a value using the given block in counter mode.
//
// The value to be decrypted must be prepended by a initialization vector
// with the length of the block size.
func decrypt(block cipher.Block, value []byte) ([]byte, error) {
	size := block.BlockSize()
	if len(value) > size {
		// Extract iv.
		iv := value[:size]
		// Extract ciphertext.
		value = value[size:]
		// Decrypt it.
		stream := cipher.NewCTR(block, iv)
		stream.XORKeyStream(value, value)
		return value, nil
	}
	return nil, errors.New("securecookie: the value could not be decrypted")
}

// Serialization --------------------------------------------------------------

// serialize encodes a value using gob.
func serialize(src interface{}) ([]byte, error) {
	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	if err := enc.Encode(src); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// deserialize decodes a value using gob.
func deserialize(src []byte, dst interface{}) error {
	dec := gob.NewDecoder(bytes.NewBuffer(src))
	if err := dec.Decode(dst); err != nil {
		return err
	}
	return nil
}

// Encoding -------------------------------------------------------------------

// encode encodes a value using base64.
func encode(value []byte) []byte {
	encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
	base64.URLEncoding.Encode(encoded, value)
	return encoded
}

// decode decodes a cookie using base64.
func decode(value []byte) ([]byte, error) {
	decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
	b, err := base64.URLEncoding.Decode(decoded, value)
	if err != nil {
		return nil, err
	}
	return decoded[:b], nil
}

// Helpers --------------------------------------------------------------------

// GenerateRandomKey is a convenience to generate a key using crypto/rand.
func GenerateRandomKey(length int) []byte {
	k := make([]byte, length)
	if _, err := rand.Read(k); err != nil {
		return nil
	}
	return k
}
<commit_msg>securecookie: don't use append(bytes, moreBytes...)<commit_after>// Copyright 2012 The Gorilla 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 securecookie

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/gob"
	"encoding/base64"
	"errors"
	"fmt"
	"hash"
	"strconv"
	"time"
)

// Codec defines an interface to encode and decode cookie values.
type Codec interface {
	Encode(name string, value interface{}) (string, error)
	Decode(name, value string) (interface{}, error)
}

// New returns a new SecureCookie.
//
// hashKey is required, used to authenticate values using HMAC. Create it using
// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes.
//
// blockKey is optional, used to encrypt values. Create it using
// GenerateRandomKey(). The key length must correspond to the block size
// of the encryption algorithm. For AES, used by default, valid lengths are
// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
func New(hashKey, blockKey []byte) *SecureCookie {
	s := &SecureCookie{
		hashKey:   hashKey,
		blockKey:  blockKey,
		hashFunc:  sha256.New,
		maxAge:    86400 * 30,
		maxLength: 4096,
	}
	if hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
	}
	if blockKey != nil {
		s.BlockFunc(aes.NewCipher)
	}
	return s
}

// SecureCookie encodes and decodes authenticated and optionally encrypted
// cookie values.
type SecureCookie struct {
	hashKey   []byte
	hashFunc  func() hash.Hash
	blockKey  []byte
	block     cipher.Block
	maxLength int
	maxAge    int64
	minAge    int64
	err       error
	// For testing purposes, the function that returns the current timestamp.
	// If not set, it will use time.Now().UTC().Unix().
	timeFunc func() int64
}

// MaxLength restricts the maximum length, in bytes, for the cookie value.
//
// Default is 4096, which is the maximum value accepted by Internet Explorer.
func (s *SecureCookie) MaxLength(value int) *SecureCookie {
	s.maxLength = value
	return s
}

// MaxAge restricts the maximum age, in seconds, for the cookie value.
//
// Default is 86400 * 30. Set it to 0 for no restriction.
func (s *SecureCookie) MaxAge(value int) *SecureCookie {
	s.maxAge = int64(value)
	return s
}

// MinAge restricts the minimum age, in seconds, for the cookie value.
//
// Default is 0 (no restriction).
func (s *SecureCookie) MinAge(value int) *SecureCookie {
	s.minAge = int64(value)
	return s
}

// HashFunc sets the hash algorithm used to create HMAC.
//
// Default is crypto/sha256.New.
func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie {
	s.hashFunc = f
	return s
}

// BlockFunc sets the encryption algorithm used to create cipher.Block.
//
// Default is crypto/aes.New.
func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie {
	if s.blockKey == nil {
		s.err = errors.New("securecookie: block key is not set")
	} else if block, err := f(s.blockKey); err == nil {
		s.block = block
	} else {
		s.err = err
	}
	return s
}

// Encode encodes a cookie value.
//
// It decodes, verifies a message authentication code, optionally decrypts and
// finally deserializes the value.
//
// The name argument is the cookie name. It is stored with the encoded value.
// The value argument is the map to be encoded.
func (s *SecureCookie) Encode(name string, value interface{}) (string, error) {
	if s.err != nil {
		return "", s.err
	}
	if s.hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
		return "", s.err
	}
	var err error
	var b []byte
	// 1. Serialize.
	if b, err = serialize(value); err != nil {
		return "", err
	}
	// 2. Encrypt and encode to base64 (optional).
	if s.block != nil {
		if b, err = encrypt(s.block, b); err != nil {
			return "", err
		}
		b = encode(b)
	}
	// 3. Create MAC for "date|name|value" and append the result.
	buf := bytes.NewBufferString(fmt.Sprintf("%d|%s|", s.timestamp(), name))
	buf.Write(b)
	mac := createMac(hmac.New(s.hashFunc, s.hashKey), buf.Bytes())
	buf.WriteString("|")
	buf.Write(mac)
	// 4. Encode to base64.
	b = encode(buf.Bytes())
	// 5. Check length.
	if s.maxLength != 0 && len(b) > s.maxLength {
		return "", errors.New("securecookie: the value is too long")
	}
	// Done.
	return string(b), nil
}

// Decode decodes a cookie value.
//
// It decodes, verifies a message authentication code, optionally decrypts and
// finally deserializes the value.
//
// The name argument is the cookie name. It must be the same name used when
// it was stored. The value argument is the encoded cookie value. The dst
// argument is where the cookie will be decoded. It must be a pointer.
func (s *SecureCookie) Decode(name, value string, dst interface{}) error {
	if s.err != nil {
		return s.err
	}
	if s.hashKey == nil {
		s.err = errors.New("securecookie: hash key is not set")
		return s.err
	}
	// 1. Check length.
	if s.maxLength != 0 && len(value) > s.maxLength {
		return errors.New("securecookie: the value is too long")
	}
	// 2. Decode from base64.
	b, err := decode([]byte(value))
	if err != nil {
		return err
	}
	// 3. Value is "date|name|value|mac". Split.
	parts := bytes.SplitN(b, []byte("|"), 4)
	if len(parts) != 4 {
		return errors.New("securecookie: invalid value")
	}
	// 4. Verify name against parts[1] and date ranges against parts[0].
	if name != string(parts[1]) {
		return errors.New("securecookie: invalid name")
	}
	var t1 int64
	if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
		return errors.New("securecookie: invalid timestamp")
	}
	t2 := s.timestamp()
	if s.minAge != 0 && t1 > t2-s.minAge {
		return errors.New("securecookie: timestamp is too new")
	}
	if s.maxAge != 0 && t1 < t2-s.maxAge {
		return errors.New("securecookie: expired timestamp")
	}
	// 5. Verify MAC: "date|name|parts[2]" against parts[3].
	h := hmac.New(s.hashFunc, s.hashKey)
	if err = verifyMac(h, b[:len(b)-len(parts[3])-1], parts[3]); err != nil {
		return err
	}
	// 6. Decode from base64 and decrypt parts[2] (optional).
	if s.block != nil {
		if b, err = decode(parts[2]); err != nil {
			return err
		}
		if b, err = decrypt(s.block, b); err != nil {
			return err
		}
	}
	// 7. Deserialize.
	if err = deserialize(b, dst); err != nil {
		return err
	}
	// Done.
	return nil
}

// timestamp returns the current timestamp, in seconds.
//
// For testing purposes, the function that generates the timestamp can be
// overridden. If not set, it will return time.Now().UTC().Unix().
func (s *SecureCookie) timestamp() int64 {
	if s.timeFunc == nil {
		return time.Now().UTC().Unix()
	}
	return s.timeFunc()
}

// Authentication -------------------------------------------------------------

// createMac creates a message authentication code (MAC).
func createMac(h hash.Hash, value []byte) []byte {
	h.Write(value)
	return h.Sum(nil)
}

// verifyMac verifies that a message authentication code (MAC) is valid.
func verifyMac(h hash.Hash, value []byte, mac []byte) error {
	mac2 := createMac(h, value)
	if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 {
		return nil
	}
	return errors.New("securecookie: the value is not valid")
}

// Encryption -----------------------------------------------------------------

// encrypt encrypts a value using the given block in counter mode.
//
// A random initialization vector with the length of the block size is
// prepended to the resulting ciphertext.
func encrypt(block cipher.Block, value []byte) ([]byte, error) {
	// Initialization vector on wikipedia: http://goo.gl/zF67k
	b := make([]byte, len(value)+block.BlockSize())
	if _, err := rand.Read(b); err != nil {
		return nil, errors.New("securecookie: failed to generate random iv")
	}
	// Encrypt it.
	stream := cipher.NewCTR(block, b[:block.BlockSize()])
	stream.XORKeyStream(value, value)
	// Return iv + ciphertext.
	copy(b[block.BlockSize():], value)
	return b, nil
}

// decrypt decrypts a value using the given block in counter mode.
//
// The value to be decrypted must be prepended by a initialization vector
// with the length of the block size.
func decrypt(block cipher.Block, value []byte) ([]byte, error) {
	size := block.BlockSize()
	if len(value) > size {
		// Extract iv.
		iv := value[:size]
		// Extract ciphertext.
		value = value[size:]
		// Decrypt it.
		stream := cipher.NewCTR(block, iv)
		stream.XORKeyStream(value, value)
		return value, nil
	}
	return nil, errors.New("securecookie: the value could not be decrypted")
}

// Serialization --------------------------------------------------------------

// serialize encodes a value using gob.
func serialize(src interface{}) ([]byte, error) {
	buf := new(bytes.Buffer)
	enc := gob.NewEncoder(buf)
	if err := enc.Encode(src); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// deserialize decodes a value using gob.
func deserialize(src []byte, dst interface{}) error {
	dec := gob.NewDecoder(bytes.NewBuffer(src))
	if err := dec.Decode(dst); err != nil {
		return err
	}
	return nil
}

// Encoding -------------------------------------------------------------------

// encode encodes a value using base64.
func encode(value []byte) []byte {
	encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
	base64.URLEncoding.Encode(encoded, value)
	return encoded
}

// decode decodes a cookie using base64.
func decode(value []byte) ([]byte, error) {
	decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
	b, err := base64.URLEncoding.Decode(decoded, value)
	if err != nil {
		return nil, err
	}
	return decoded[:b], nil
}

// Helpers --------------------------------------------------------------------

// GenerateRandomKey is a convenience to generate a key using crypto/rand.
func GenerateRandomKey(length int) []byte {
	k := make([]byte, length)
	if _, err := rand.Read(k); err != nil {
		return nil
	}
	return k
}
<|endoftext|>


<commit_before>package hosting

import (
	"appstax-cli/appstax/apiclient"
	"appstax-cli/appstax/fail"
	"appstax-cli/appstax/log"
	"archive/tar"
	"bufio"
	"compress/gzip"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

func UploadStatic(archivePath string, progressWriter io.Writer) error {
	_, _, err := apiclient.PostFile(apiclient.Url("/appstax/hosting/static"), archivePath, progressWriter)
	return err
}

func UploadServer(archivePath string, progressWriter io.Writer) error {
	_, _, err := apiclient.PostFile(apiclient.Url("/appstax/hosting/server/code"), archivePath, progressWriter)
	return err
}

func CreateServer() error {
	_, _, err := apiclient.Post(apiclient.Url("/appstax/hosting/server"), "")
	return err
}

func DeleteServer() error {
	_, _, err := apiclient.Delete(apiclient.Url("/appstax/hosting/server"))
	return err
}

func PrepareArchive(rootPath string) (string, int64, error) {
	file, err := ioutil.TempFile("", "")
	fail.Handle(err)
	defer file.Close()
	fileWriter := bufio.NewWriter(file)
	defer fileWriter.Flush()
	gzipWriter, err := gzip.NewWriterLevel(fileWriter, gzip.BestCompression)
	fail.Handle(err)
	defer gzipWriter.Close()
	tarWriter := tar.NewWriter(gzipWriter)
	defer tarWriter.Close()

	fullRootPath, err := filepath.Abs(rootPath)
	fail.Handle(err)
	err = addAllToArchive(fullRootPath, tarWriter)
	if err != nil {
		return "", 0, err
	}

	tarWriter.Close()
	gzipWriter.Close()
	fileWriter.Flush()
	file.Close()

	fileInfo, err := os.Stat(file.Name())
	fail.Handle(err)
	return file.Name(), fileInfo.Size(), nil
}

func addAllToArchive(fullRootPath string, tarWriter *tar.Writer) error {
	log.Debugf("Creating archive by walking from root path %s", fullRootPath)
	return filepath.Walk(fullRootPath, func(path string, fileInfo os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !fileInfo.IsDir() && fileInfo.Name()[:1] != "." {
			err := addFileToArchive(path, path[len(fullRootPath+"/"):], tarWriter, fileInfo)
			if err != nil {
				return err
			}
		} else {
			log.Debugf("Ignoring path %s", path)
		}
		return nil
	})
}

func addFileToArchive(filePath string, addPath string, tarWriter *tar.Writer, fileInfo os.FileInfo) error {
	addPath = filepath.ToSlash(addPath)
	log.Debugf("Adding file %s from %s", addPath, filePath)
	fileReader, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer fileReader.Close()

	header := new(tar.Header)
	header.Name = addPath
	header.Size = fileInfo.Size()
	header.Mode = int64(fileInfo.Mode())
	header.ModTime = fileInfo.ModTime()

	err = tarWriter.WriteHeader(header)
	fail.Handle(err)
	_, err = io.Copy(tarWriter, fileReader)
	return err
}
<commit_msg>Dereferencing symbolic links when creating archive.<commit_after>package hosting

import (
	"appstax-cli/appstax/apiclient"
	"appstax-cli/appstax/fail"
	"appstax-cli/appstax/log"
	"archive/tar"
	"bufio"
	"compress/gzip"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

func UploadStatic(archivePath string, progressWriter io.Writer) error {
	_, _, err := apiclient.PostFile(apiclient.Url("/appstax/hosting/static"), archivePath, progressWriter)
	return err
}

func UploadServer(archivePath string, progressWriter io.Writer) error {
	_, _, err := apiclient.PostFile(apiclient.Url("/appstax/hosting/server/code"), archivePath, progressWriter)
	return err
}

func CreateServer() error {
	_, _, err := apiclient.Post(apiclient.Url("/appstax/hosting/server"), "")
	return err
}

func DeleteServer() error {
	_, _, err := apiclient.Delete(apiclient.Url("/appstax/hosting/server"))
	return err
}

func PrepareArchive(rootPath string) (string, int64, error) {
	file, err := ioutil.TempFile("", "")
	fail.Handle(err)
	defer file.Close()
	fileWriter := bufio.NewWriter(file)
	defer fileWriter.Flush()
	gzipWriter, err := gzip.NewWriterLevel(fileWriter, gzip.BestCompression)
	fail.Handle(err)
	defer gzipWriter.Close()
	tarWriter := tar.NewWriter(gzipWriter)
	defer tarWriter.Close()

	fullRootPath, err := filepath.Abs(rootPath)
	fail.Handle(err)
	err = addAllToArchive(fullRootPath, tarWriter)
	if err != nil {
		return "", 0, err
	}

	tarWriter.Close()
	gzipWriter.Close()
	fileWriter.Flush()
	file.Close()

	fileInfo, err := os.Stat(file.Name())
	fail.Handle(err)
	return file.Name(), fileInfo.Size(), nil
}

func addAllToArchive(fullRootPath string, tarWriter *tar.Writer) error {
	log.Debugf("Creating archive by walking from root path %s", fullRootPath)
	return filepath.Walk(fullRootPath, func(path string, fileInfo os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !fileInfo.IsDir() && fileInfo.Name()[:1] != "." {
			err := addFileToArchive(path, path[len(fullRootPath+"/"):], tarWriter, fileInfo)
			if err != nil {
				return err
			}
		} else {
			log.Debugf("Ignoring path %s", path)
		}
		return nil
	})
}

func addFileToArchive(filePath string, addPath string, tarWriter *tar.Writer, fileInfo os.FileInfo) error {
	addPath = filepath.ToSlash(addPath)
	log.Debugf("Adding file %s from %s", addPath, filePath)

	if isSymlink(fileInfo) {
		link, err := filepath.EvalSymlinks(filePath)
		if err != nil {
			return err
		}
		log.Debugf("Dereferencing symlink %s -> %s", filePath, link)
		filePath = link
		fileInfo, err = os.Lstat(filePath)
		if err != nil {
			return err
		}
	}

	header := new(tar.Header)
	header.Name = addPath
	header.Size = fileInfo.Size()
	header.Mode = int64(fileInfo.Mode())
	header.ModTime = fileInfo.ModTime()

	fileReader, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer fileReader.Close()
	err = tarWriter.WriteHeader(header)
	fail.Handle(err)
	_, err = io.Copy(tarWriter, fileReader)
	return err
}

func isSymlink(fileInfo os.FileInfo) bool {
	return fileInfo.Mode() & os.ModeSymlink != 0
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
	"time"

	"github.com/MustWin/baremetal-sdk-go"
	"github.com/MustWin/terraform-Oracle-BareMetal-Provider/client/mocks"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"

	"github.com/stretchr/testify/suite"
)

type ResourceObjectstorageBucketTestSuite struct {
	suite.Suite
	Client       *mocks.BareMetalClient
	Provider     terraform.ResourceProvider
	Providers    map[string]terraform.ResourceProvider
	TimeCreated  baremetal.Time
	Config       string
	ResourceName string
	Res          *baremetal.Bucket
}

func (s *ResourceObjectstorageBucketTestSuite) SetupTest() {
	s.Client = &mocks.BareMetalClient{}

	s.Provider = Provider(
		func(d *schema.ResourceData) (interface{}, error) {
			return s.Client, nil
		},
	)

	s.Providers = map[string]terraform.ResourceProvider{
		"baremetal": s.Provider,
	}

	s.TimeCreated = baremetal.Time{Time: time.Now()}

	s.Config = `
		resource "baremetal_object_storage_bucket" "t" {
			compartment_id = "compartment_id"
			name = "name"
			namespace = "namespace"
			metadata = {
				"foo" = "bar"
			}
		}
	`

	s.Config += testProviderConfig

	s.ResourceName = "baremetal_object_storage_bucket.t"
	metadata := map[string]string{
		"foo": "bar",
	}
	s.Res = &baremetal.Bucket{
		CompartmentID: "compartment_id",
		Name:          "name",
		Namespace:     "namespace",
		Metadata:      metadata,
		CreatedBy:     "created_by",
		TimeCreated:   s.TimeCreated,
	}
	s.Res.ETag = "etag"
	s.Res.RequestID = "opcrequestid"

	opts := &baremetal.CreateBucketOptions{
		Metadata: metadata,
	}
	s.Client.On(
		"CreateBucket",
		"compartment_id",
		"name",
		"namespace",
		opts).Return(s.Res, nil)
	s.Client.On("DeleteBucket", "name", "namespace", (*baremetal.IfMatchOptions)(nil)).Return(nil)
}

func (s *ResourceObjectstorageBucketTestSuite) TestCreateResourceCoreBucket() {
	s.Client.On("GetBucket", "name", "namespace").Return(s.Res, nil).Times(2)
	s.Client.On("GetBucket", "name", "namespace").Return(nil, nil)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(s.ResourceName, "compartment_id", s.Res.CompartmentID),
					resource.TestCheckResourceAttr(s.ResourceName, "name", s.Res.Name),
					resource.TestCheckResourceAttr(s.ResourceName, "namespace", s.Res.Namespace),
//					resource.TestCheckResourceAttr(s.ResourceName, "created_by", s.Res.CreatedBy),
//					resource.TestCheckResourceAttr(s.ResourceName, "time_created", s.Res.TimeCreated.String()),
				),
			},
		},
	})
}

func TestResourceobjectstorageBucketTestSuite(t *testing.T) {
	suite.Run(t, new(ResourceObjectstorageBucketTestSuite))
}
<commit_msg>Add update & delete tests for BucketResource<commit_after>package main

import (
	"testing"
	"time"

	"github.com/MustWin/baremetal-sdk-go"
	"github.com/MustWin/terraform-Oracle-BareMetal-Provider/client/mocks"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"

	"github.com/stretchr/testify/suite"
)

type ResourceObjectstorageBucketTestSuite struct {
	suite.Suite
	Client       *mocks.BareMetalClient
	Provider     terraform.ResourceProvider
	Providers    map[string]terraform.ResourceProvider
	TimeCreated  baremetal.Time
	Config       string
	ResourceName string
	Res          *baremetal.Bucket
}

func (s *ResourceObjectstorageBucketTestSuite) SetupTest() {
	s.Client = &mocks.BareMetalClient{}

	s.Provider = Provider(
		func(d *schema.ResourceData) (interface{}, error) {
			return s.Client, nil
		},
	)

	s.Providers = map[string]terraform.ResourceProvider{
		"baremetal": s.Provider,
	}

	s.TimeCreated = baremetal.Time{Time: time.Now()}

	s.Config = `
		resource "baremetal_object_storage_bucket" "t" {
			compartment_id = "compartment_id"
			name = "name"
			namespace = "namespace"
			metadata = {
				"foo" = "bar"
			}
		}
	`

	s.Config += testProviderConfig

	s.ResourceName = "baremetal_object_storage_bucket.t"
	metadata := map[string]string{
		"foo": "bar",
	}
	s.Res = &baremetal.Bucket{
		CompartmentID: "compartment_id",
		Name:          "name",
		Namespace:     "namespace",
		Metadata:      metadata,
		CreatedBy:     "created_by",
		TimeCreated:   s.TimeCreated,
	}
	s.Res.ETag = "etag"
	s.Res.RequestID = "opcrequestid"

	opts := &baremetal.CreateBucketOptions{
		Metadata: metadata,
	}
	s.Client.On(
		"CreateBucket",
		"compartment_id",
		"name",
		"namespace",
		opts).Return(s.Res, nil)
	s.Client.On("DeleteBucket", "name", "namespace", (*baremetal.IfMatchOptions)(nil)).Return(nil)
}

func (s *ResourceObjectstorageBucketTestSuite) TestCreateResourceCoreBucket() {
	s.Client.On("GetBucket", "name", "namespace").Return(s.Res, nil).Times(2)
	s.Client.On("GetBucket", "name", "namespace").Return(nil, nil)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(s.ResourceName, "compartment_id", s.Res.CompartmentID),
					resource.TestCheckResourceAttr(s.ResourceName, "name", s.Res.Name),
					resource.TestCheckResourceAttr(s.ResourceName, "namespace", s.Res.Namespace),
				),
			},
		},
	})
}

func (s *ResourceObjectstorageBucketTestSuite) TestUpdateResourceCoreBucket() {
	s.Client.On("GetBucket", "name", "namespace").Return(s.Res, nil).Times(2)

	config := `
		resource "baremetal_object_storage_bucket" "t" {
			compartment_id = "compartment_id"
			name = "new_name"
			namespace = "namespace"
			metadata = {
				"foo" = "bar"
			}
		}
	`
	config += testProviderConfig
	metadata := map[string]string{
		"foo": "bar",
	}

	res := &baremetal.Bucket{
		CompartmentID: "compartment_id",
		Name:          "new_name",
		Namespace:     "namespace",
		Metadata:      metadata,
		CreatedBy:     "created_by",
		TimeCreated:   s.TimeCreated,
	}
	res.ETag = "etag"
	res.RequestID = "opcrequestid"

	opts := &baremetal.UpdateBucketOptions{
		Metadata: metadata,
	}
	s.Client.On("UpdateBucket",
		res.CompartmentID, "new_name", res.Namespace, opts).Return(res, nil)
	s.Client.On("GetBucket", "new_name", "namespace").Return(res, nil)
	s.Client.On("DeleteBucket", "new_name", "namespace", (*baremetal.IfMatchOptions)(nil)).Return(nil)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
			},
			resource.TestStep{
				Config: config,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(s.ResourceName, "name", res.Name),
				),
			},
		},
	})
}

func (s *ResourceObjectstorageBucketTestSuite) TestDeleteResourceCoreBucket() {
	s.Client.On("GetBucket", "name", "namespace").Return(s.Res, nil).Times(2)
	s.Client.On("GetBucket", "name", "namespace").Return(nil, nil)
	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
			},
			resource.TestStep{
				Config:  s.Config,
				Destroy: true,
			},
		},
	})
	s.Client.AssertCalled(s.T(), "DeleteBucket", "name", "namespace", (*baremetal.IfMatchOptions)(nil))
}

func TestResourceobjectstorageBucketTestSuite(t *testing.T) {
	suite.Run(t, new(ResourceObjectstorageBucketTestSuite))
}
<|endoftext|>


<commit_before>// Package herots provide fast way to create TLS services: server and client.
//
// Explanation of the name: HERald Of The Swarm
//
// By the way - have a nice day :)
package herots

import (
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
)

////////////////////////////////////////////////////////////////////////////////
//                       Shared functions and structs                         //
////////////////////////////////////////////////////////////////////////////////

// log - struct for internal log service
type log struct {
	LogLevel       int
	LogDestination io.Writer
}

func (l *log) Log(msg string, lvl int) {
	if l.LogLevel == 0 {
		return
	}

	if lvl <= l.LogLevel {
		fmt.Fprintf(l.LogDestination, "herots: %s\n", msg)
	}
}

// loadKeyPair - internal function for load certificate and private key pair.
func loadKeyPair(cert, key []byte) (tls.Certificate, *x509.Certificate, error) {
	c, err := tls.X509KeyPair(cert, key)
	if err != nil {
		return tls.Certificate{}, &x509.Certificate{}, err
	}

	pemData, _ := pem.Decode(cert)
	ca, err := x509.ParseCertificate(pemData.Bytes)
	if err != nil {
		return tls.Certificate{}, &x509.Certificate{}, err
	}

	return c, ca, nil
}

// Options - structure, which is used to configure a TLS server and client.
type Options struct {
	// Server host.
	//
	// Default: '127.0.0.1'.
	Host string

	// Server port.
	//
	// Default: '9000'.
	Port int

	// LogLevel provides the opportunity to choose the level of
	// information messages.
	// Each level includes the messages from the previous level.
	// 0 - no messages
	// 1 - notice
	// 2 - info
	// 3 - error
	//
	// Default: '0'.
	LogLevel int

	// LogDestination provides the opportunity to choose the own
	// destination for log messages (errors, info, etc).
	//
	// Default: 'os.Stdout'.
	LogDestination io.Writer

	// TLSAuthType - refer to http://golang.org/pkg/crypto/tls/#ClientAuthType
	//
	// This option ignored for client implementation.
	//
	// Default: tls.RequireAnyClientCert
	TLSAuthType tls.ClientAuthType
}

// predefined errors messages
const (
	LoadKeyPairError   = "load key pair error"
	NoKeyPairLoadError = "no load key pair (use LoadKeyPair func)"
)

////////////////////////////////////////////////////////////////////////////////
//                                  Server                                    //
////////////////////////////////////////////////////////////////////////////////

// Server - primary struct for server implementation.
type Server struct {
	options *Options
	certs   struct {
		Cert tls.Certificate
		Pool *x509.CertPool
	}
	listener net.Listener
	logger   *log
}

// NewServer - function for create Server struct
func NewServer(o *Options) *Server {
	s := &Server{}

	// check mandatory options
	if o.LogDestination == nil {
		o.LogDestination = os.Stdout
	}

	if o.Port == 0 {
		o.Port = 9000
	}

	if o.TLSAuthType == 0 {
		o.TLSAuthType = tls.RequireAnyClientCert
	}

	l := &log{
		LogLevel:       o.LogLevel,
		LogDestination: o.LogDestination,
	}

	s.options = o
	s.logger = l

	return s
}

// LoadKeyPair - function for load certificate and private key pair.
//
// Public/private key pair require as PEM encoded data.
func (s *Server) LoadKeyPair(cert, key []byte) error {
	c, ca, err := loadKeyPair(cert, key)
	if err != nil {
		return fmt.Errorf("%s: %v\n", LoadKeyPairError, err)
	}

	s.certs.Cert = c

	s.certs.Pool = x509.NewCertPool()
	s.certs.Pool.AddCert(ca)

	s.logger.Log("load key pair - ok", 2)

	return nil
}

// AddClientCACert - function for adding client CA certificate to
// x509.CertPool (tls.Config.ClientCAs).
//
// By default server add cert from server public/private key pair (LoadKeyPair)
// to cert pool.
func (s *Server) AddClientCACert(cert []byte) error {
	pemData, _ := pem.Decode(cert)
	ca, err := x509.ParseCertificate(pemData.Bytes)
	if err != nil {
		return fmt.Errorf("load client CA cert error: %v\n", err)
	}
	s.certs.Pool.AddCert(ca)

	s.logger.Log("load client CA cert - ok", 2)

	return nil
}

// Accept - accept and return connections.
func (s *Server) Accept() (net.Conn, error) {
	conn, err := s.listener.Accept()
	if err != nil {
		s.logger.Log("accept conn error: "+err.Error(), 3)
		return conn, fmt.Errorf("connection accept fail: %v\n", err)
	}
	s.logger.Log("accepted conn from "+conn.RemoteAddr().String(), 2)
	return conn, nil
}

// Start - function for start server.
func (s *Server) Start() error {
	// load keypair check
	if len(s.certs.Cert.Certificate) == 0 {
		return fmt.Errorf("%s\n", NoKeyPairLoadError)
	}

	config := tls.Config{
		ClientAuth:   s.options.TLSAuthType,
		Certificates: []tls.Certificate{s.certs.Cert},
		ClientCAs:    s.certs.Pool,
		Rand:         rand.Reader,
	}

	service := s.options.Host + ":" + strconv.Itoa(s.options.Port)

	listener, err := tls.Listen("tcp", service, &config)
	if err != nil {
		return fmt.Errorf("start tls server fail: %v\n", err)
	}
	s.listener = listener

	s.logger.Log("listening on "+service, 1)

	return nil
}

////////////////////////////////////////////////////////////////////////////////
//                                  Client                                    //
////////////////////////////////////////////////////////////////////////////////

// Client - primary struct for client implementation.
type Client struct {
	options *Options
	certs   struct {
		Cert tls.Certificate
		Pool *x509.CertPool
	}
	logger *log
}

// NewClient - function for create Client struct
func NewClient(o *Options) *Client {
	c := &Client{}

	// check mandatory options
	if o.LogDestination == nil {
		o.LogDestination = os.Stdout
	}

	if o.Port == 0 {
		o.Port = 9000
	}

	l := &log{
		LogLevel:       o.LogLevel,
		LogDestination: o.LogDestination,
	}

	c.options = o
	c.logger = l
	c.certs.Pool = x509.NewCertPool()

	return c
}

// LoadKeyPair - function for load certificate and private key pair.
//
// Public/private key pair require as PEM encoded data.
func (c *Client) LoadKeyPair(cert, key []byte) error {
	c0, ca, err := loadKeyPair(cert, key)
	if err != nil {
		return fmt.Errorf("%s: %v\n", LoadKeyPairError, err)
	}

	c.certs.Cert = c0
	c.certs.Pool.AddCert(ca)

	c.logger.Log("load key pair - ok", 2)

	return nil
}

// AddCertToRootCA - function to load additional certificates to root CA pool.
func (c *Client) AddCertToRootCA(cert []byte) error {
	pemData, _ := pem.Decode(cert)

	ca, err := x509.ParseCertificate(pemData.Bytes)
	if err != nil {
		return fmt.Errorf("load CA cert error: %v\n", err)
	}

	c.certs.Pool.AddCert(ca)

	c.logger.Log("add cert to root CA - ok", 2)

	return nil
}

// Dial - function for start connection with server.
func (c *Client) Dial() (*tls.Conn, error) {
	// load keypair check
	if len(c.certs.Cert.Certificate) == 0 {
		return nil, fmt.Errorf("%s\n", NoKeyPairLoadError)
	}

	config := &tls.Config{
		Certificates:       []tls.Certificate{c.certs.Cert},
		InsecureSkipVerify: false,
		RootCAs:            c.certs.Pool,
	}

	service := c.options.Host + ":" + strconv.Itoa(c.options.Port)

	conn, err := tls.Dial("tcp", service, config)
	if err != nil {
		return nil, fmt.Errorf("fail to dial with server: %v\n", err)
	}

	return conn, nil
}
<commit_msg>add log message to Client Dial<commit_after>// Package herots provide fast way to create TLS services: server and client.
//
// Explanation of the name: HERald Of The Swarm
//
// By the way - have a nice day :)
package herots

import (
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
)

////////////////////////////////////////////////////////////////////////////////
//                       Shared functions and structs                         //
////////////////////////////////////////////////////////////////////////////////

// log - struct for internal log service
type log struct {
	LogLevel       int
	LogDestination io.Writer
}

func (l *log) Log(msg string, lvl int) {
	if l.LogLevel == 0 {
		return
	}

	if lvl <= l.LogLevel {
		fmt.Fprintf(l.LogDestination, "herots: %s\n", msg)
	}
}

// loadKeyPair - internal function for load certificate and private key pair.
func loadKeyPair(cert, key []byte) (tls.Certificate, *x509.Certificate, error) {
	c, err := tls.X509KeyPair(cert, key)
	if err != nil {
		return tls.Certificate{}, &x509.Certificate{}, err
	}

	pemData, _ := pem.Decode(cert)
	ca, err := x509.ParseCertificate(pemData.Bytes)
	if err != nil {
		return tls.Certificate{}, &x509.Certificate{}, err
	}

	return c, ca, nil
}

// Options - structure, which is used to configure a TLS server and client.
type Options struct {
	// Server host.
	//
	// Default: '127.0.0.1'.
	Host string

	// Server port.
	//
	// Default: '9000'.
	Port int

	// LogLevel provides the opportunity to choose the level of
	// information messages.
	// Each level includes the messages from the previous level.
	// 0 - no messages
	// 1 - notice
	// 2 - info
	// 3 - error
	//
	// Default: '0'.
	LogLevel int

	// LogDestination provides the opportunity to choose the own
	// destination for log messages (errors, info, etc).
	//
	// Default: 'os.Stdout'.
	LogDestination io.Writer

	// TLSAuthType - refer to http://golang.org/pkg/crypto/tls/#ClientAuthType
	//
	// This option ignored for client implementation.
	//
	// Default: tls.RequireAnyClientCert
	TLSAuthType tls.ClientAuthType
}

// predefined errors messages
const (
	LoadKeyPairError   = "load key pair error"
	NoKeyPairLoadError = "no load key pair (use LoadKeyPair func)"
)

////////////////////////////////////////////////////////////////////////////////
//                                  Server                                    //
////////////////////////////////////////////////////////////////////////////////

// Server - primary struct for server implementation.
type Server struct {
	options *Options
	certs   struct {
		Cert tls.Certificate
		Pool *x509.CertPool
	}
	listener net.Listener
	logger   *log
}

// NewServer - function for create Server struct
func NewServer(o *Options) *Server {
	s := &Server{}

	// check mandatory options
	if o.LogDestination == nil {
		o.LogDestination = os.Stdout
	}

	if o.Port == 0 {
		o.Port = 9000
	}

	if o.TLSAuthType == 0 {
		o.TLSAuthType = tls.RequireAnyClientCert
	}

	l := &log{
		LogLevel:       o.LogLevel,
		LogDestination: o.LogDestination,
	}

	s.options = o
	s.logger = l

	return s
}

// LoadKeyPair - function for load certificate and private key pair.
//
// Public/private key pair require as PEM encoded data.
func (s *Server) LoadKeyPair(cert, key []byte) error {
	c, ca, err := loadKeyPair(cert, key)
	if err != nil {
		return fmt.Errorf("%s: %v\n", LoadKeyPairError, err)
	}

	s.certs.Cert = c

	s.certs.Pool = x509.NewCertPool()
	s.certs.Pool.AddCert(ca)

	s.logger.Log("load key pair - ok", 2)

	return nil
}

// AddClientCACert - function for adding client CA certificate to
// x509.CertPool (tls.Config.ClientCAs).
//
// By default server add cert from server public/private key pair (LoadKeyPair)
// to cert pool.
func (s *Server) AddClientCACert(cert []byte) error {
	pemData, _ := pem.Decode(cert)
	ca, err := x509.ParseCertificate(pemData.Bytes)
	if err != nil {
		return fmt.Errorf("load client CA cert error: %v\n", err)
	}
	s.certs.Pool.AddCert(ca)

	s.logger.Log("load client CA cert - ok", 2)

	return nil
}

// Accept - accept and return connections.
func (s *Server) Accept() (net.Conn, error) {
	conn, err := s.listener.Accept()
	if err != nil {
		s.logger.Log("accept conn error: "+err.Error(), 3)
		return conn, fmt.Errorf("connection accept fail: %v\n", err)
	}
	s.logger.Log("accepted conn from "+conn.RemoteAddr().String(), 2)
	return conn, nil
}

// Start - function for start server.
func (s *Server) Start() error {
	// load keypair check
	if len(s.certs.Cert.Certificate) == 0 {
		return fmt.Errorf("%s\n", NoKeyPairLoadError)
	}

	config := tls.Config{
		ClientAuth:   s.options.TLSAuthType,
		Certificates: []tls.Certificate{s.certs.Cert},
		ClientCAs:    s.certs.Pool,
		Rand:         rand.Reader,
	}

	service := s.options.Host + ":" + strconv.Itoa(s.options.Port)

	listener, err := tls.Listen("tcp", service, &config)
	if err != nil {
		return fmt.Errorf("start tls server fail: %v\n", err)
	}
	s.listener = listener

	s.logger.Log("listening on "+service, 1)

	return nil
}

////////////////////////////////////////////////////////////////////////////////
//                                  Client                                    //
////////////////////////////////////////////////////////////////////////////////

// Client - primary struct for client implementation.
type Client struct {
	options *Options
	certs   struct {
		Cert tls.Certificate
		Pool *x509.CertPool
	}
	logger *log
}

// NewClient - function for create Client struct
func NewClient(o *Options) *Client {
	c := &Client{}

	// check mandatory options
	if o.LogDestination == nil {
		o.LogDestination = os.Stdout
	}

	if o.Port == 0 {
		o.Port = 9000
	}

	l := &log{
		LogLevel:       o.LogLevel,
		LogDestination: o.LogDestination,
	}

	c.options = o
	c.logger = l
	c.certs.Pool = x509.NewCertPool()

	return c
}

// LoadKeyPair - function for load certificate and private key pair.
//
// Public/private key pair require as PEM encoded data.
func (c *Client) LoadKeyPair(cert, key []byte) error {
	c0, ca, err := loadKeyPair(cert, key)
	if err != nil {
		return fmt.Errorf("%s: %v\n", LoadKeyPairError, err)
	}

	c.certs.Cert = c0
	c.certs.Pool.AddCert(ca)

	c.logger.Log("load key pair - ok", 2)

	return nil
}

// AddCertToRootCA - function to load additional certificates to root CA pool.
func (c *Client) AddCertToRootCA(cert []byte) error {
	pemData, _ := pem.Decode(cert)

	ca, err := x509.ParseCertificate(pemData.Bytes)
	if err != nil {
		return fmt.Errorf("load CA cert error: %v\n", err)
	}

	c.certs.Pool.AddCert(ca)

	c.logger.Log("add cert to root CA - ok", 2)

	return nil
}

// Dial - function for start connection with server.
func (c *Client) Dial() (*tls.Conn, error) {
	// load keypair check
	if len(c.certs.Cert.Certificate) == 0 {
		return nil, fmt.Errorf("%s\n", NoKeyPairLoadError)
	}

	config := &tls.Config{
		Certificates:       []tls.Certificate{c.certs.Cert},
		InsecureSkipVerify: false,
		RootCAs:            c.certs.Pool,
	}

	service := c.options.Host + ":" + strconv.Itoa(c.options.Port)

	conn, err := tls.Dial("tcp", service, config)
	if err != nil {
		return nil, fmt.Errorf("fail to dial with server: %v\n", err)
	}

	c.logger.Log("dial to "+service+" - ok", 2)

	return conn, nil
}
<|endoftext|>


<commit_before>package snake

import (
	"fmt"

	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
)

const (
	defaultColor = termbox.ColorDefault
	bgColor      = termbox.ColorDefault
	snakeColor   = termbox.ColorGreen
)

func (g *Game) render() error {
	termbox.Clear(defaultColor, defaultColor)

	var (
		w, h   = termbox.Size()
		midY   = h / 2
		midX   = (w - g.Arena.Width) / 2
		left   = midX - 1
		top    = midY - (g.Arena.Height / 2)
		bottom = midY + (g.Arena.Height / 2)
	)

	renderArena(g.Arena, top, bottom, midX)
	renderSnake(left, bottom, g.Arena.Snake)
	renderFood(left, bottom, g.Arena.Food)
	renderScore(midX, bottom, g.Score)

	return termbox.Flush()
}

func renderSnake(left, bottom int, s *Snake) {
	for _, b := range s.Body {
		termbox.SetCell(left+b[0], bottom-b[1], '▇', snakeColor, bgColor)
	}
}

func renderFood(left, bottom int, f *Food) {
	termbox.SetCell(left+f.X, bottom-f.Y, f.Emoji, defaultColor, bgColor)
}

func renderArena(a *Arena, top, bottom, midX int) {
	for i := top; i < bottom; i++ {
		termbox.SetCell(midX-1, i, '│', defaultColor, bgColor)
		termbox.SetCell(midX+a.Width, i, '│', defaultColor, bgColor)
	}

	termbox.SetCell(midX-1, top, '┌', defaultColor, bgColor)
	termbox.SetCell(midX-1, bottom, '└', defaultColor, bgColor)
	termbox.SetCell(midX+a.Width, top, '┐', defaultColor, bgColor)
	termbox.SetCell(midX+a.Width, bottom, '┘', defaultColor, bgColor)

	fill(midX, top, a.Width, 1, termbox.Cell{Ch: '─'})
	fill(midX, bottom, a.Width, 1, termbox.Cell{Ch: '─'})
}

func renderScore(midX, bottom, s int) {
	score := fmt.Sprintf("Score: %v", s)
	tbprint(midX, bottom+1, defaultColor, defaultColor, score)
}

func fill(x, y, w, h int, cell termbox.Cell) {
	for ly := 0; ly < h; ly++ {
		for lx := 0; lx < w; lx++ {
			termbox.SetCell(x+lx, y+ly, cell.Ch, cell.Fg, cell.Bg)
		}
	}
}

func tbprint(x, y int, fg, bg termbox.Attribute, msg string) {
	for _, c := range msg {
		termbox.SetCell(x, y, c, fg, bg)
		x += runewidth.RuneWidth(c)
	}
}
<commit_msg>Render game title<commit_after>package snake

import (
	"fmt"

	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
)

const (
	defaultColor = termbox.ColorDefault
	bgColor      = termbox.ColorDefault
	snakeColor   = termbox.ColorGreen
)

func (g *Game) render() error {
	termbox.Clear(defaultColor, defaultColor)

	var (
		w, h   = termbox.Size()
		midY   = h / 2
		midX   = (w - g.Arena.Width) / 2
		left   = midX - 1
		top    = midY - (g.Arena.Height / 2)
		bottom = midY + (g.Arena.Height / 2)
	)

	renderTitle(midX, top)
	renderArena(g.Arena, top, bottom, midX)
	renderSnake(left, bottom, g.Arena.Snake)
	renderFood(left, bottom, g.Arena.Food)
	renderScore(midX, bottom, g.Score)

	return termbox.Flush()
}

func renderSnake(left, bottom int, s *Snake) {
	for _, b := range s.Body {
		termbox.SetCell(left+b[0], bottom-b[1], '▇', snakeColor, bgColor)
	}
}

func renderFood(left, bottom int, f *Food) {
	termbox.SetCell(left+f.X, bottom-f.Y, f.Emoji, defaultColor, bgColor)
}

func renderArena(a *Arena, top, bottom, midX int) {
	for i := top; i < bottom; i++ {
		termbox.SetCell(midX-1, i, '│', defaultColor, bgColor)
		termbox.SetCell(midX+a.Width, i, '│', defaultColor, bgColor)
	}

	termbox.SetCell(midX-1, top, '┌', defaultColor, bgColor)
	termbox.SetCell(midX-1, bottom, '└', defaultColor, bgColor)
	termbox.SetCell(midX+a.Width, top, '┐', defaultColor, bgColor)
	termbox.SetCell(midX+a.Width, bottom, '┘', defaultColor, bgColor)

	fill(midX, top, a.Width, 1, termbox.Cell{Ch: '─'})
	fill(midX, bottom, a.Width, 1, termbox.Cell{Ch: '─'})
}

func renderScore(midX, bottom, s int) {
	score := fmt.Sprintf("Score: %v", s)
	tbprint(midX-1, bottom+1, defaultColor, defaultColor, score)
}

func renderTitle(midX, top int) {
	tbprint(midX-1, top-1, defaultColor, defaultColor, "Snake Game")
}

func fill(x, y, w, h int, cell termbox.Cell) {
	for ly := 0; ly < h; ly++ {
		for lx := 0; lx < w; lx++ {
			termbox.SetCell(x+lx, y+ly, cell.Ch, cell.Fg, cell.Bg)
		}
	}
}

func tbprint(x, y int, fg, bg termbox.Attribute, msg string) {
	for _, c := range msg {
		termbox.SetCell(x, y, c, fg, bg)
		x += runewidth.RuneWidth(c)
	}
}
<|endoftext|>