<commit_before>// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package viper

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/kr/pretty"
	"github.com/mitchellh/mapstructure"
	"github.com/spf13/cast"
	jww "github.com/spf13/jwalterweatherman"
	"github.com/spf13/pflag"
	"gopkg.in/yaml.v1"
)

// A set of paths to look for the config file in
var configPaths []string

// Name of file to look for inside the path
var configName string = "config"

// extensions Supported
var SupportedExts []string = []string{"json", "toml", "yaml"}
var configFile string
var configType string

var config map[string]interface{} = make(map[string]interface{})
var override map[string]interface{} = make(map[string]interface{})
var defaults map[string]interface{} = make(map[string]interface{})
var pflags map[string]*pflag.Flag = make(map[string]*pflag.Flag)
var aliases map[string]string = make(map[string]string)

func SetConfigFile(in string) {
	if in != "" {
		configFile = in
	}
}

func ConfigFileUsed() string {
	return configFile
}

func AddConfigPath(in string) {
	if in != "" {
		absin := absPathify(in)
		jww.INFO.Println("adding", absin, "to paths to search")
		if !stringInSlice(absin, configPaths) {
			configPaths = append(configPaths, absin)
		}
	}
}

func GetString(key string) string {
	return cast.ToString(Get(key))
}

func GetBool(key string) bool {
	return cast.ToBool(Get(key))
}

func GetInt(key string) int {
	return cast.ToInt(Get(key))
}

func GetFloat64(key string) float64 {
	return cast.ToFloat64(Get(key))
}

func GetTime(key string) time.Time {
	return cast.ToTime(Get(key))
}

func GetStringSlice(key string) []string {
	return cast.ToStringSlice(Get(key))
}

func GetStringMap(key string) map[string]interface{} {
	return cast.ToStringMap(Get(key))
}

func GetStringMapString(key string) map[string]string {
	return cast.ToStringMapString(Get(key))
}

// takes a map and uses reflection to convert it into the given Go native structure.
// rawVal must be a pointer to a struct.
func GetIntoStruct(key string, rawVal interface{}) error {
	return mapstructure.Decode(Get(key), rawVal)
}

func GetAllIntoStruct(rawVal interface{}) (err error) {
	err = mapstructure.Decode(defaults, rawVal)
	if err != nil {
		return
	}
	err = mapstructure.Decode(config, rawVal)
	if err != nil {
		return
	}
	err = mapstructure.Decode(override, rawVal)
	if err != nil {
		return
	}
	return
}

func BindPFlag(key string, flag *pflag.Flag) (err error) {
	if flag == nil {
		return fmt.Errorf("flag for %q is nil", key)
	}
	pflags[key] = flag

	switch flag.Value.Type() {
	case "int", "int8", "int16", "int32", "int64":
		SetDefault(key, cast.ToInt(flag.Value.String()))
	case "bool":
		SetDefault(key, cast.ToBool(flag.Value.String()))
	default:
		SetDefault(key, flag.Value.String())
	}
	return nil
}

func find(key string) interface{} {
	var val interface{}
	var exists bool

	// if the requested key is an alias, then return the proper key
	key = realKey(key)

	// PFlag Override first
	flag, exists := pflags[key]
	if exists {
		if flag.Changed {
			jww.TRACE.Println(key, "found in override (via pflag):", val)
			return flag.Value.String()
		}
	}

	val, exists = override[key]
	if exists {
		jww.TRACE.Println(key, "found in override:", val)
		return val
	}

	val, exists = config[key]
	if exists {
		jww.TRACE.Println(key, "found in config:", val)
		return val
	}

	val, exists = defaults[key]
	if exists {
		jww.TRACE.Println(key, "found in defaults:", val)
		return val
	}

	return nil
}

func Get(key string) interface{} {
	key = strings.ToLower(key)
	v := find(key)

	if v == nil {
		return nil
	}

	switch v.(type) {
	case bool:
		return cast.ToBool(v)
	case string:
		return cast.ToString(v)
	case int64, int32, int16, int8, int:
		return cast.ToInt(v)
	case float64, float32:
		return cast.ToFloat64(v)
	case time.Time:
		return cast.ToTime(v)
	case []string:
		return v
	}
	return v
}

func IsSet(key string) bool {
	t := Get(key)
	return t != nil
}

func RegisterAlias(alias string, key string) {
	registerAlias(alias, strings.ToLower(key))
}

func registerAlias(alias string, key string) {
	alias = strings.ToLower(alias)
	if alias != key && alias != realKey(key) {
		_, exists := aliases[alias]
		if !exists {
			aliases[alias] = key
		}
	} else {
		jww.WARN.Println("Creating circular reference alias", alias, key, realKey(key))
	}
}

func realKey(key string) string {
	newkey, exists := aliases[key]
	if exists {
		jww.DEBUG.Println("Alias", key, "to", newkey)
		return realKey(newkey)
	} else {
		return key
	}
}

func InConfig(key string) bool {
	// if the requested key is an alias, then return the proper key
	key = realKey(key)

	_, exists := config[key]
	return exists
}

func SetDefault(key string, value interface{}) {
	// If alias passed in, then set the proper default
	key = realKey(strings.ToLower(key))
	defaults[key] = value
}

func Set(key string, value interface{}) {
	// If alias passed in, then set the proper override
	key = realKey(strings.ToLower(key))
	override[key] = value
}

type UnsupportedConfigError string

func (str UnsupportedConfigError) Error() string {
	return fmt.Sprintf("Unsupported Config Type %q", string(str))
}

func ReadInConfig() error {
	jww.INFO.Println("Attempting to read in config file")
	if !stringInSlice(getConfigType(), SupportedExts) {
		return UnsupportedConfigError(getConfigType())
	}

	file, err := ioutil.ReadFile(getConfigFile())
	if err != nil {
		return err
	}

	MarshallReader(bytes.NewReader(file))
	return nil
}

func MarshallReader(in io.Reader) {
	buf := new(bytes.Buffer)
	buf.ReadFrom(in)

	switch getConfigType() {
	case "yaml", "yml":
		if err := yaml.Unmarshal(buf.Bytes(), &config); err != nil {
			jww.ERROR.Fatalf("Error parsing config: %s", err)
		}

	case "json":
		if err := json.Unmarshal(buf.Bytes(), &config); err != nil {
			jww.ERROR.Fatalf("Error parsing config: %s", err)
		}

	case "toml":
		if _, err := toml.Decode(buf.String(), &config); err != nil {
			jww.ERROR.Fatalf("Error parsing config: %s", err)
		}
	}

	insensativiseMap()
}

func insensativiseMap() {
	for key, _ := range config {
		if key != strings.ToLower(key) {
			registerAlias(key, key)
		}
	}
}

func SetConfigName(in string) {
	if in != "" {
		configName = in
	}
}

func SetConfigType(in string) {
	if in != "" {
		configType = in
	}
}

func getConfigType() string {
	if configType != "" {
		return configType
	}

	cf := getConfigFile()
	ext := path.Ext(cf)

	if len(ext) > 1 {
		return ext[1:]
	} else {
		return ""
	}
}

func getConfigFile() string {
	// if explicitly set, then use it
	if configFile != "" {
		return configFile
	}

	cf, err := findConfigFile()
	if err != nil {
		return ""
	}

	configFile = cf
	return getConfigFile()
}

func searchInPath(in string) (filename string) {
	jww.DEBUG.Println("Searching for config in ", in)
	for _, ext := range SupportedExts {

		jww.DEBUG.Println("Checking for", path.Join(in, configName+"."+ext))
		if b, _ := exists(path.Join(in, configName+"."+ext)); b {
			jww.DEBUG.Println("Found: ", path.Join(in, configName+"."+ext))
			return path.Join(in, configName+"."+ext)
		}
	}

	return ""
}

func findConfigFile() (string, error) {
	jww.INFO.Println("Searching for config in ", configPaths)

	for _, cp := range configPaths {
		file := searchInPath(cp)
		if file != "" {
			return file, nil
		}
	}
	cwd, _ := findCWD()

	file := searchInPath(cwd)
	if file != "" {
		return file, nil
	}

	return "", fmt.Errorf("config file not found in: %s", configPaths)
}

func findCWD() (string, error) {
	serverFile, err := filepath.Abs(os.Args[0])

	if err != nil {
		return "", fmt.Errorf("Can't get absolute path for executable: %v", err)
	}

	path := filepath.Dir(serverFile)
	realFile, err := filepath.EvalSymlinks(serverFile)

	if err != nil {
		if _, err = os.Stat(serverFile + ".exe"); err == nil {
			realFile = filepath.Clean(serverFile + ".exe")
		}
	}

	if err == nil && realFile != serverFile {
		path = filepath.Dir(realFile)
	}

	return path, nil
}

// Check if File / Directory Exists
func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func userHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}
	return os.Getenv("HOME")
}

func absPathify(inPath string) string {
	jww.INFO.Println("Trying to resolve absolute path to", inPath)

	if strings.HasPrefix(inPath, "$HOME") {
		inPath = userHomeDir() + inPath[5:]
	}

	if strings.HasPrefix(inPath, "$") {
		end := strings.Index(inPath, string(os.PathSeparator))
		inPath = os.Getenv(inPath[1:end]) + inPath[end:]
	}

	if filepath.IsAbs(inPath) {
		return filepath.Clean(inPath)
	}

	p, err := filepath.Abs(inPath)
	if err == nil {
		return filepath.Clean(p)
	} else {
		jww.ERROR.Println("Couldn't discover absolute path")
		jww.ERROR.Println(err)
	}
	return ""
}

func Debug() {
	fmt.Println("Config:")
	pretty.Println(config)
	fmt.Println("Defaults:")
	pretty.Println(defaults)
	fmt.Println("Override:")
	pretty.Println(override)
	fmt.Println("Aliases:")
	pretty.Println(aliases)
}

func Reset() {
	configPaths = nil
	configName = "config"

	// extensions Supported
	SupportedExts = []string{"json", "toml", "yaml", "yml"}
	configFile = ""
	configType = ""

	config = make(map[string]interface{})
	override = make(map[string]interface{})
	defaults = make(map[string]interface{})
	aliases = make(map[string]string)
}
<commit_msg>Better naming of the Marshal functionality.<commit_after>// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package viper

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/kr/pretty"
	"github.com/mitchellh/mapstructure"
	"github.com/spf13/cast"
	jww "github.com/spf13/jwalterweatherman"
	"github.com/spf13/pflag"
	"gopkg.in/yaml.v1"
)

// A set of paths to look for the config file in
var configPaths []string

// Name of file to look for inside the path
var configName string = "config"

// extensions Supported
var SupportedExts []string = []string{"json", "toml", "yaml"}
var configFile string
var configType string

var config map[string]interface{} = make(map[string]interface{})
var override map[string]interface{} = make(map[string]interface{})
var defaults map[string]interface{} = make(map[string]interface{})
var pflags map[string]*pflag.Flag = make(map[string]*pflag.Flag)
var aliases map[string]string = make(map[string]string)

func SetConfigFile(in string) {
	if in != "" {
		configFile = in
	}
}

func ConfigFileUsed() string {
	return configFile
}

func AddConfigPath(in string) {
	if in != "" {
		absin := absPathify(in)
		jww.INFO.Println("adding", absin, "to paths to search")
		if !stringInSlice(absin, configPaths) {
			configPaths = append(configPaths, absin)
		}
	}
}

func GetString(key string) string {
	return cast.ToString(Get(key))
}

func GetBool(key string) bool {
	return cast.ToBool(Get(key))
}

func GetInt(key string) int {
	return cast.ToInt(Get(key))
}

func GetFloat64(key string) float64 {
	return cast.ToFloat64(Get(key))
}

func GetTime(key string) time.Time {
	return cast.ToTime(Get(key))
}

func GetStringSlice(key string) []string {
	return cast.ToStringSlice(Get(key))
}

func GetStringMap(key string) map[string]interface{} {
	return cast.ToStringMap(Get(key))
}

func GetStringMapString(key string) map[string]string {
	return cast.ToStringMapString(Get(key))
}

// Takes a single key and marshals it into a Struct
func MarshalKey(key string, rawVal interface{}) error {
	return mapstructure.Decode(Get(key), rawVal)
}

// Marshals the config into a Struct
func Marshal(rawVal interface{}) (err error) {
	err = mapstructure.Decode(defaults, rawVal)
	if err != nil {
		return
	}
	err = mapstructure.Decode(config, rawVal)
	if err != nil {
		return
	}
	err = mapstructure.Decode(override, rawVal)
	if err != nil {
		return
	}
	return
}

func BindPFlag(key string, flag *pflag.Flag) (err error) {
	if flag == nil {
		return fmt.Errorf("flag for %q is nil", key)
	}
	pflags[key] = flag

	switch flag.Value.Type() {
	case "int", "int8", "int16", "int32", "int64":
		SetDefault(key, cast.ToInt(flag.Value.String()))
	case "bool":
		SetDefault(key, cast.ToBool(flag.Value.String()))
	default:
		SetDefault(key, flag.Value.String())
	}
	return nil
}

func find(key string) interface{} {
	var val interface{}
	var exists bool

	// if the requested key is an alias, then return the proper key
	key = realKey(key)

	// PFlag Override first
	flag, exists := pflags[key]
	if exists {
		if flag.Changed {
			jww.TRACE.Println(key, "found in override (via pflag):", val)
			return flag.Value.String()
		}
	}

	val, exists = override[key]
	if exists {
		jww.TRACE.Println(key, "found in override:", val)
		return val
	}

	val, exists = config[key]
	if exists {
		jww.TRACE.Println(key, "found in config:", val)
		return val
	}

	val, exists = defaults[key]
	if exists {
		jww.TRACE.Println(key, "found in defaults:", val)
		return val
	}

	return nil
}

func Get(key string) interface{} {
	key = strings.ToLower(key)
	v := find(key)

	if v == nil {
		return nil
	}

	switch v.(type) {
	case bool:
		return cast.ToBool(v)
	case string:
		return cast.ToString(v)
	case int64, int32, int16, int8, int:
		return cast.ToInt(v)
	case float64, float32:
		return cast.ToFloat64(v)
	case time.Time:
		return cast.ToTime(v)
	case []string:
		return v
	}
	return v
}

func IsSet(key string) bool {
	t := Get(key)
	return t != nil
}

func RegisterAlias(alias string, key string) {
	registerAlias(alias, strings.ToLower(key))
}

func registerAlias(alias string, key string) {
	alias = strings.ToLower(alias)
	if alias != key && alias != realKey(key) {
		_, exists := aliases[alias]
		if !exists {
			aliases[alias] = key
		}
	} else {
		jww.WARN.Println("Creating circular reference alias", alias, key, realKey(key))
	}
}

func realKey(key string) string {
	newkey, exists := aliases[key]
	if exists {
		jww.DEBUG.Println("Alias", key, "to", newkey)
		return realKey(newkey)
	} else {
		return key
	}
}

func InConfig(key string) bool {
	// if the requested key is an alias, then return the proper key
	key = realKey(key)

	_, exists := config[key]
	return exists
}

func SetDefault(key string, value interface{}) {
	// If alias passed in, then set the proper default
	key = realKey(strings.ToLower(key))
	defaults[key] = value
}

func Set(key string, value interface{}) {
	// If alias passed in, then set the proper override
	key = realKey(strings.ToLower(key))
	override[key] = value
}

type UnsupportedConfigError string

func (str UnsupportedConfigError) Error() string {
	return fmt.Sprintf("Unsupported Config Type %q", string(str))
}

func ReadInConfig() error {
	jww.INFO.Println("Attempting to read in config file")
	if !stringInSlice(getConfigType(), SupportedExts) {
		return UnsupportedConfigError(getConfigType())
	}

	file, err := ioutil.ReadFile(getConfigFile())
	if err != nil {
		return err
	}

	MarshallReader(bytes.NewReader(file))
	return nil
}

func MarshallReader(in io.Reader) {
	buf := new(bytes.Buffer)
	buf.ReadFrom(in)

	switch getConfigType() {
	case "yaml", "yml":
		if err := yaml.Unmarshal(buf.Bytes(), &config); err != nil {
			jww.ERROR.Fatalf("Error parsing config: %s", err)
		}

	case "json":
		if err := json.Unmarshal(buf.Bytes(), &config); err != nil {
			jww.ERROR.Fatalf("Error parsing config: %s", err)
		}

	case "toml":
		if _, err := toml.Decode(buf.String(), &config); err != nil {
			jww.ERROR.Fatalf("Error parsing config: %s", err)
		}
	}

	insensativiseMap()
}

func insensativiseMap() {
	for key, _ := range config {
		if key != strings.ToLower(key) {
			registerAlias(key, key)
		}
	}
}

func SetConfigName(in string) {
	if in != "" {
		configName = in
	}
}

func SetConfigType(in string) {
	if in != "" {
		configType = in
	}
}

func getConfigType() string {
	if configType != "" {
		return configType
	}

	cf := getConfigFile()
	ext := path.Ext(cf)

	if len(ext) > 1 {
		return ext[1:]
	} else {
		return ""
	}
}

func getConfigFile() string {
	// if explicitly set, then use it
	if configFile != "" {
		return configFile
	}

	cf, err := findConfigFile()
	if err != nil {
		return ""
	}

	configFile = cf
	return getConfigFile()
}

func searchInPath(in string) (filename string) {
	jww.DEBUG.Println("Searching for config in ", in)
	for _, ext := range SupportedExts {

		jww.DEBUG.Println("Checking for", path.Join(in, configName+"."+ext))
		if b, _ := exists(path.Join(in, configName+"."+ext)); b {
			jww.DEBUG.Println("Found: ", path.Join(in, configName+"."+ext))
			return path.Join(in, configName+"."+ext)
		}
	}

	return ""
}

func findConfigFile() (string, error) {
	jww.INFO.Println("Searching for config in ", configPaths)

	for _, cp := range configPaths {
		file := searchInPath(cp)
		if file != "" {
			return file, nil
		}
	}
	cwd, _ := findCWD()

	file := searchInPath(cwd)
	if file != "" {
		return file, nil
	}

	return "", fmt.Errorf("config file not found in: %s", configPaths)
}

func findCWD() (string, error) {
	serverFile, err := filepath.Abs(os.Args[0])

	if err != nil {
		return "", fmt.Errorf("Can't get absolute path for executable: %v", err)
	}

	path := filepath.Dir(serverFile)
	realFile, err := filepath.EvalSymlinks(serverFile)

	if err != nil {
		if _, err = os.Stat(serverFile + ".exe"); err == nil {
			realFile = filepath.Clean(serverFile + ".exe")
		}
	}

	if err == nil && realFile != serverFile {
		path = filepath.Dir(realFile)
	}

	return path, nil
}

// Check if File / Directory Exists
func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

func userHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}
	return os.Getenv("HOME")
}

func absPathify(inPath string) string {
	jww.INFO.Println("Trying to resolve absolute path to", inPath)

	if strings.HasPrefix(inPath, "$HOME") {
		inPath = userHomeDir() + inPath[5:]
	}

	if strings.HasPrefix(inPath, "$") {
		end := strings.Index(inPath, string(os.PathSeparator))
		inPath = os.Getenv(inPath[1:end]) + inPath[end:]
	}

	if filepath.IsAbs(inPath) {
		return filepath.Clean(inPath)
	}

	p, err := filepath.Abs(inPath)
	if err == nil {
		return filepath.Clean(p)
	} else {
		jww.ERROR.Println("Couldn't discover absolute path")
		jww.ERROR.Println(err)
	}
	return ""
}

func Debug() {
	fmt.Println("Config:")
	pretty.Println(config)
	fmt.Println("Defaults:")
	pretty.Println(defaults)
	fmt.Println("Override:")
	pretty.Println(override)
	fmt.Println("Aliases:")
	pretty.Println(aliases)
}

func Reset() {
	configPaths = nil
	configName = "config"

	// extensions Supported
	SupportedExts = []string{"json", "toml", "yaml", "yml"}
	configFile = ""
	configType = ""

	config = make(map[string]interface{})
	override = make(map[string]interface{})
	defaults = make(map[string]interface{})
	aliases = make(map[string]string)
}
<|endoftext|>


<commit_before>package dnsp

import (
	"crypto/md5"
	"log"
	"regexp"
	"strings"
	"time"

	"github.com/miekg/dns"
)

type hosts map[checksum]struct{}

type hostsRX map[checksum]*regexp.Regexp

type checksum [md5.Size / 2]byte

// isAllowed returns whether we are allowed to resolve this host.
//
// If the server is whitelisting, the result will be true if the host is on the whitelist.
// If the server is blacklisting, the result will be true if the host is NOT on the blacklist.
//
// NOTE: "host" must end with a dot.
func (s *Server) isAllowed(host string) bool {
	s.m.RLock()
	defer s.m.RUnlock()

	_, ok := s.hosts[hash(host)]

	if s.white { // whitelist mode
		if ok {
			return true
		}
		for _, rx := range s.hostsRX {
			if rx.MatchString(host) {
				return true
			}
		}
		return false
	}

	// blacklist mode
	if ok {
		return false
	}
	for _, rx := range s.hostsRX {
		if rx.MatchString(host) {
			return false
		}
	}
	return true
}

func (s *Server) filter(qs []dns.Question) []dns.Question {
	result := []dns.Question{}
	for _, q := range qs {
		if s.isAllowed(q.Name) {
			result = append(result, q)
		}
	}
	return result
}

// Load the host entries into separate structures and swap the existing entries.
func (s *Server) loadHostEntries() error {
	hosts := hosts{}
	hostsRX := hostsRX{}

	s.m.RLock()
	path := s.hostsFile.path
	s.m.RUnlock()

	if err := readHosts(path, func(host string) {
		if host[len(host)-1] != '.' {
			host += "."
		}

		if !strings.ContainsRune(host, '*') {
			// Plain host string:
			hosts[hash(host)] = struct{}{}
		} else if rx := compilePattern(host); rx != nil {
			// Host pattern (regex):
			hostsRX[hash(rx.String())] = rx
		}
	}); err != nil {
		return err
	}

	s.m.Lock()
	s.hosts = hosts
	s.hostsRX = hostsRX
	s.m.Unlock()

	return nil
}

func (s *Server) monitorHostEntries(poll time.Duration) {
	s.m.RLock()
	hf := s.hostsFile
	s.m.RUnlock()

	for _ = range time.Tick(poll) {
		log.Printf("dnsp: checking %q for updates…", hf.path)

		mtime, size, err := hostsFileMetadata(hf.path)
		if err != nil {
			log.Printf("dnsp: %s", err)
			continue
		}

		if hf.mtime.Equal(mtime) && hf.size == size {
			continue // no updates
		}

		if err := s.loadHostEntries(); err != nil {
			log.Printf("dnsp: %s", err)
		}

		s.m.Lock()
		s.hostsFile.mtime = mtime
		s.hostsFile.size = size
		hf = s.hostsFile
		s.m.Unlock()
	}
}

func (s *Server) addHostEntry(host string) {
	if host == "" {
		return
	}
	if host[len(host)-1] != '.' {
		host += "."
	}

	if !strings.ContainsRune(host, '*') {
		// Plain host string:
		s.m.Lock()
		s.hosts[hash(host)] = struct{}{}
		s.m.Unlock()
	} else if rx := compilePattern(host); rx != nil {
		// Host pattern (regex):
		s.m.Lock()
		s.hostsRX[hash(rx.String())] = rx
		s.m.Unlock()
	}
}

func (s *Server) removeHostEntry(host string) {
	if host == "" {
		return
	}
	if host[len(host)-1] != '.' {
		host += "."
	}

	if !strings.ContainsRune(host, '*') {
		// Plain host string:
		s.m.Lock()
		delete(s.hosts, hash(host))
		s.m.Unlock()
	} else if rx := compilePattern(host); rx != nil {
		// Host pattern (regex):
		s.m.Lock()
		delete(s.hostsRX, hash(rx.String()))
		s.m.Unlock()
	}
}

func compilePattern(pat string) *regexp.Regexp {
	pat = strings.Replace(pat, ".", `\.`, -1)
	pat = strings.Replace(pat, "*", ".*", -1)
	pat = "^" + pat + `$`
	rx, err := regexp.Compile(pat)
	if err != nil {
		log.Printf("dnsp: could not compile %q: %s", pat, err)
		return nil
	}
	return rx
}

func hash(s string) checksum {
	sum := md5.Sum([]byte(s))
	return checksum{
		sum[0] ^ sum[1],
		sum[2] ^ sum[3],
		sum[4] ^ sum[5],
		sum[6] ^ sum[7],
		sum[8] ^ sum[9],
		sum[10] ^ sum[11],
		sum[12] ^ sum[13],
		sum[14] ^ sum[15],
	}
}
<commit_msg>do not break when there is no whitelist or blacklist file provided<commit_after>package dnsp

import (
	"crypto/md5"
	"log"
	"regexp"
	"strings"
	"time"

	"github.com/miekg/dns"
)

type hosts map[checksum]struct{}

type hostsRX map[checksum]*regexp.Regexp

type checksum [md5.Size / 2]byte

// isAllowed returns whether we are allowed to resolve this host.
//
// If the server is whitelisting, the result will be true if the host is on the whitelist.
// If the server is blacklisting, the result will be true if the host is NOT on the blacklist.
//
// NOTE: "host" must end with a dot.
func (s *Server) isAllowed(host string) bool {
	s.m.RLock()
	defer s.m.RUnlock()

	_, ok := s.hosts[hash(host)]

	if s.white { // whitelist mode
		if ok {
			return true
		}
		for _, rx := range s.hostsRX {
			if rx.MatchString(host) {
				return true
			}
		}
		return false
	}

	// blacklist mode
	if ok {
		return false
	}
	for _, rx := range s.hostsRX {
		if rx.MatchString(host) {
			return false
		}
	}
	return true
}

func (s *Server) filter(qs []dns.Question) []dns.Question {
	result := []dns.Question{}
	for _, q := range qs {
		if s.isAllowed(q.Name) {
			result = append(result, q)
		}
	}
	return result
}

// Load the host entries into separate structures and swap the existing entries.
func (s *Server) loadHostEntries() error {
	s.m.RLock()
	path := s.hostsFile.path
	s.m.RUnlock()

	if path == "" {
		return nil
	}

	hosts := hosts{}
	hostsRX := hostsRX{}

	if err := readHosts(path, func(host string) {
		if host[len(host)-1] != '.' {
			host += "."
		}

		if !strings.ContainsRune(host, '*') {
			// Plain host string:
			hosts[hash(host)] = struct{}{}
		} else if rx := compilePattern(host); rx != nil {
			// Host pattern (regex):
			hostsRX[hash(rx.String())] = rx
		}
	}); err != nil {
		return err
	}

	s.m.Lock()
	s.hosts = hosts
	s.hostsRX = hostsRX
	s.m.Unlock()

	return nil
}

func (s *Server) monitorHostEntries(poll time.Duration) {
	s.m.RLock()
	hf := s.hostsFile
	s.m.RUnlock()

	if hf.path == "" {
		return
	}

	for _ = range time.Tick(poll) {
		log.Printf("dnsp: checking %q for updates…", hf.path)

		mtime, size, err := hostsFileMetadata(hf.path)
		if err != nil {
			log.Printf("dnsp: %s", err)
			continue
		}

		if hf.mtime.Equal(mtime) && hf.size == size {
			continue // no updates
		}

		if err := s.loadHostEntries(); err != nil {
			log.Printf("dnsp: %s", err)
		}

		s.m.Lock()
		s.hostsFile.mtime = mtime
		s.hostsFile.size = size
		hf = s.hostsFile
		s.m.Unlock()
	}
}

func (s *Server) addHostEntry(host string) {
	if host == "" {
		return
	}
	if host[len(host)-1] != '.' {
		host += "."
	}

	if !strings.ContainsRune(host, '*') {
		// Plain host string:
		s.m.Lock()
		s.hosts[hash(host)] = struct{}{}
		s.m.Unlock()
	} else if rx := compilePattern(host); rx != nil {
		// Host pattern (regex):
		s.m.Lock()
		s.hostsRX[hash(rx.String())] = rx
		s.m.Unlock()
	}
}

func (s *Server) removeHostEntry(host string) {
	if host == "" {
		return
	}
	if host[len(host)-1] != '.' {
		host += "."
	}

	if !strings.ContainsRune(host, '*') {
		// Plain host string:
		s.m.Lock()
		delete(s.hosts, hash(host))
		s.m.Unlock()
	} else if rx := compilePattern(host); rx != nil {
		// Host pattern (regex):
		s.m.Lock()
		delete(s.hostsRX, hash(rx.String()))
		s.m.Unlock()
	}
}

func compilePattern(pat string) *regexp.Regexp {
	pat = strings.Replace(pat, ".", `\.`, -1)
	pat = strings.Replace(pat, "*", ".*", -1)
	pat = "^" + pat + `$`
	rx, err := regexp.Compile(pat)
	if err != nil {
		log.Printf("dnsp: could not compile %q: %s", pat, err)
		return nil
	}
	return rx
}

func hash(s string) checksum {
	sum := md5.Sum([]byte(s))
	return checksum{
		sum[0] ^ sum[1],
		sum[2] ^ sum[3],
		sum[4] ^ sum[5],
		sum[6] ^ sum[7],
		sum[8] ^ sum[9],
		sum[10] ^ sum[11],
		sum[12] ^ sum[13],
		sum[14] ^ sum[15],
	}
}
<|endoftext|>


<commit_before>package vm

import (
	"fmt"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/vm/classes"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

// Version stores current Goby version
const Version = "0.1.7"

// These are the enums for marking parser's mode, which decides whether it should pop unused values.
const (
	NormalMode int = iota
	REPLMode
	TestMode
)

type isIndexTable struct {
	Data map[string]int
}

func newISIndexTable() *isIndexTable {
	return &isIndexTable{Data: make(map[string]int)}
}

type isTable map[string][]*instructionSet

type filename = string

var standardLibraries = map[string]func(*VM){
	"net/http":           initHTTPClass,
	"net/simple_server":  initSimpleServerClass,
	"uri":                initURIClass,
	"db":                 initDBClass,
	"plugin":             initPluginClass,
	"json":               initJSONClass,
	"concurrent/array":   initConcurrentArrayClass,
	"concurrent/hash":    initConcurrentHashClass,
	"concurrent/rw_lock": initConcurrentRWLockClass,
}

// VM represents a stack based virtual machine.
type VM struct {
	mainObj     *RObject
	mainThread  *thread
	objectClass *RClass
	// a map holds different types of instruction set tables
	isTables map[setType]isTable
	// method instruction set table
	methodISIndexTables map[filename]*isIndexTable
	// class instruction set table
	classISIndexTables map[filename]*isIndexTable
	// block instruction set table
	blockTables map[filename]map[string]*instructionSet
	// fileDir indicates executed file's directory
	fileDir string
	// args are command line arguments
	args []string
	// projectRoot is goby root's absolute path, which is $GOROOT/src/github.com/goby-lang/goby
	projectRoot string

	channelObjectMap *objectMap

	mode int

	libFiles []string
}

// New initializes a vm to initialize state and returns it.
func New(fileDir string, args []string) (vm *VM, e error) {
	vm = &VM{args: args}
	vm.mainThread = vm.newThread()

	vm.methodISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.classISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.blockTables = make(map[filename]map[string]*instructionSet)
	vm.isTables = map[setType]isTable{
		bytecode.MethodDef: make(isTable),
		bytecode.ClassDef:  make(isTable),
	}
	vm.fileDir = fileDir

	gobyRoot := os.Getenv("GOBY_ROOT")

	if len(gobyRoot) == 0 {
		vm.projectRoot = fmt.Sprintf("/usr/local/Cellar/goby/%s", Version)

		_, err := os.Stat(vm.projectRoot)

		if err != nil {
			path, _ := filepath.Abs("$GOPATH/src/github.com/goby-lang/goby")
			_, err = os.Stat(path)

			if err != nil {
				e = fmt.Errorf("You haven't set $GOBY_ROOT properly")
				return nil, e
			}

			vm.projectRoot = path
		}
	} else {
		vm.projectRoot = gobyRoot
	}

	vm.initConstants()
	vm.mainObj = vm.initMainObj()
	vm.channelObjectMap = &objectMap{store: &sync.Map{}}

	for _, fn := range vm.libFiles {
		err := vm.mainThread.execGobyLib(fn)
		if err != nil {
			fmt.Printf("An error occurs when loading lib file %s:\n", string(fn))
			fmt.Println(err.Error())
			break
		}
	}

	return
}

func (vm *VM) newThread() *thread {
	s := &stack{RWMutex: new(sync.RWMutex)}
	cfs := &callFrameStack{callFrames: []callFrame{}}
	t := &thread{stack: s, callFrameStack: cfs, sp: 0, cfp: 0}
	s.thread = t
	cfs.thread = t
	t.vm = vm
	return t
}

// ExecInstructions accepts a sequence of bytecodes and use vm to evaluate them.
func (vm *VM) ExecInstructions(sets []*bytecode.InstructionSet, fn string) {
	translator := newInstructionTranslator(fn)
	translator.vm = vm
	translator.transferInstructionSets(sets)

	// Keep instruction set table updated after parsed new files.
	// TODO: Find more efficient way to do this.
	for setType, table := range translator.setTable {
		for name, is := range table {
			vm.isTables[setType][name] = is
		}
	}

	vm.blockTables[translator.filename] = translator.blockTable
	vm.SetClassISIndexTable(translator.filename)
	vm.SetMethodISIndexTable(translator.filename)

	cf := newNormalCallFrame(translator.program, translator.filename, 1)
	cf.self = vm.mainObj
	vm.mainThread.callFrameStack.push(cf)

	defer func() {
		err, ok := recover().(*Error)

		if ok && vm.mode == NormalMode {
			fmt.Println(err.Message())
		}
	}()

	vm.mainThread.startFromTopFrame()
}

// SetClassISIndexTable adds new instruction set's index table to vm.classISIndexTables
func (vm *VM) SetClassISIndexTable(fn filename) {
	vm.classISIndexTables[fn] = newISIndexTable()
}

// SetMethodISIndexTable adds new instruction set's index table to vm.methodISIndexTables
func (vm *VM) SetMethodISIndexTable(fn filename) {
	vm.methodISIndexTables[fn] = newISIndexTable()
}

// main object singleton methods -----------------------------------------------------
func builtinMainObjSingletonMethods() []*BuiltinMethodObject {
	return []*BuiltinMethodObject{
		{
			Name: "to_s",
			Fn: func(receiver Object, sourceLine int) builtinMethodBody {
				return func(thread *thread, objects []Object, frame *normalCallFrame) Object {
					return thread.vm.initStringObject("main")
				}
			},
		},
	}
}

func (vm *VM) initMainObj() *RObject {
	obj := vm.objectClass.initializeInstance()
	singletonClass := vm.initializeClass(fmt.Sprintf("#<Class:%s>", obj.toString()), false)
	singletonClass.Methods.set("include", vm.topLevelClass(classes.ClassClass).lookupMethod("include"))
	singletonClass.setBuiltinMethods(builtinMainObjSingletonMethods(), false)
	obj.singletonClass = singletonClass

	return obj
}

func (vm *VM) initConstants() {
	// Init Class and Object
	cClass := initClassClass()
	vm.objectClass = initObjectClass(cClass)
	vm.topLevelClass(classes.ObjectClass).setClassConstant(cClass)

	// Init builtin classes
	builtinClasses := []*RClass{
		vm.initIntegerClass(),
		vm.initFloatClass(),
		vm.initStringClass(),
		vm.initBoolClass(),
		vm.initNullClass(),
		vm.initArrayClass(),
		vm.initHashClass(),
		vm.initRangeClass(),
		vm.initMethodClass(),
		vm.initBlockClass(),
		vm.initChannelClass(),
		vm.initGoClass(),
		vm.initFileClass(),
		vm.initRegexpClass(),
		vm.initMatchDataClass(),
		vm.initGoMapClass(),
		vm.initDecimalClass(),
	}

	// Init error classes
	vm.initErrorClasses()

	for _, c := range builtinClasses {
		vm.objectClass.setClassConstant(c)
	}

	// Init ARGV
	args := []Object{}

	for _, arg := range vm.args {
		args = append(args, vm.initStringObject(arg))
	}

	vm.objectClass.constants["ARGV"] = &Pointer{Target: vm.initArrayObject(args)}

	// Init ENV
	envs := map[string]Object{}

	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		envs[pair[0]] = vm.initStringObject(pair[1])
	}

	vm.objectClass.constants["ENV"] = &Pointer{Target: vm.initHashObject(envs)}
	vm.objectClass.constants["STDOUT"] = &Pointer{Target: vm.initFileObject(os.Stdout)}
	vm.objectClass.constants["STDERR"] = &Pointer{Target: vm.initFileObject(os.Stderr)}
	vm.objectClass.constants["STDIN"] = &Pointer{Target: vm.initFileObject(os.Stdin)}
}

func (vm *VM) topLevelClass(cn string) *RClass {
	objClass := vm.objectClass

	if cn == classes.ObjectClass {
		return objClass
	}

	return objClass.constants[cn].Target.(*RClass)
}

func (vm *VM) currentFilePath() string {
	frame := vm.mainThread.callFrameStack.top()
	return frame.FileName()
}

// loadConstant makes sure we don't create a class twice.
func (vm *VM) loadConstant(name string, isModule bool) *RClass {
	var c *RClass
	var ptr *Pointer

	ptr = vm.objectClass.constants[name]

	if ptr == nil {
		c = vm.initializeClass(name, isModule)
		vm.objectClass.setClassConstant(c)
	} else {
		c = ptr.Target.(*RClass)
	}

	return c
}

func (vm *VM) lookupConstant(cf callFrame, constName string) (constant *Pointer) {
	var namespace *RClass
	var hasNamespace bool

	top := vm.mainThread.stack.top()

	if top == nil {
		hasNamespace = false
	} else {
		namespace, hasNamespace = top.Target.(*RClass)
	}

	if hasNamespace {
		constant = namespace.lookupConstantUnderAllScope(constName)

		if constant != nil {
			return
		}
	}

	constant = cf.lookupConstantUnderAllScope(constName)

	if constant == nil {
		constant = vm.objectClass.constants[constName]
	}

	if constName == classes.ObjectClass {
		constant = &Pointer{Target: vm.objectClass}
	}

	return
}
<commit_msg>Bump version to 0.1.8.<commit_after>package vm

import (
	"fmt"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/vm/classes"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

// Version stores current Goby version
const Version = "0.1.8"

// These are the enums for marking parser's mode, which decides whether it should pop unused values.
const (
	NormalMode int = iota
	REPLMode
	TestMode
)

type isIndexTable struct {
	Data map[string]int
}

func newISIndexTable() *isIndexTable {
	return &isIndexTable{Data: make(map[string]int)}
}

type isTable map[string][]*instructionSet

type filename = string

var standardLibraries = map[string]func(*VM){
	"net/http":           initHTTPClass,
	"net/simple_server":  initSimpleServerClass,
	"uri":                initURIClass,
	"db":                 initDBClass,
	"plugin":             initPluginClass,
	"json":               initJSONClass,
	"concurrent/array":   initConcurrentArrayClass,
	"concurrent/hash":    initConcurrentHashClass,
	"concurrent/rw_lock": initConcurrentRWLockClass,
}

// VM represents a stack based virtual machine.
type VM struct {
	mainObj     *RObject
	mainThread  *thread
	objectClass *RClass
	// a map holds different types of instruction set tables
	isTables map[setType]isTable
	// method instruction set table
	methodISIndexTables map[filename]*isIndexTable
	// class instruction set table
	classISIndexTables map[filename]*isIndexTable
	// block instruction set table
	blockTables map[filename]map[string]*instructionSet
	// fileDir indicates executed file's directory
	fileDir string
	// args are command line arguments
	args []string
	// projectRoot is goby root's absolute path, which is $GOROOT/src/github.com/goby-lang/goby
	projectRoot string

	channelObjectMap *objectMap

	mode int

	libFiles []string
}

// New initializes a vm to initialize state and returns it.
func New(fileDir string, args []string) (vm *VM, e error) {
	vm = &VM{args: args}
	vm.mainThread = vm.newThread()

	vm.methodISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.classISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.blockTables = make(map[filename]map[string]*instructionSet)
	vm.isTables = map[setType]isTable{
		bytecode.MethodDef: make(isTable),
		bytecode.ClassDef:  make(isTable),
	}
	vm.fileDir = fileDir

	gobyRoot := os.Getenv("GOBY_ROOT")

	if len(gobyRoot) == 0 {
		vm.projectRoot = fmt.Sprintf("/usr/local/Cellar/goby/%s", Version)

		_, err := os.Stat(vm.projectRoot)

		if err != nil {
			path, _ := filepath.Abs("$GOPATH/src/github.com/goby-lang/goby")
			_, err = os.Stat(path)

			if err != nil {
				e = fmt.Errorf("You haven't set $GOBY_ROOT properly")
				return nil, e
			}

			vm.projectRoot = path
		}
	} else {
		vm.projectRoot = gobyRoot
	}

	vm.initConstants()
	vm.mainObj = vm.initMainObj()
	vm.channelObjectMap = &objectMap{store: &sync.Map{}}

	for _, fn := range vm.libFiles {
		err := vm.mainThread.execGobyLib(fn)
		if err != nil {
			fmt.Printf("An error occurs when loading lib file %s:\n", string(fn))
			fmt.Println(err.Error())
			break
		}
	}

	return
}

func (vm *VM) newThread() *thread {
	s := &stack{RWMutex: new(sync.RWMutex)}
	cfs := &callFrameStack{callFrames: []callFrame{}}
	t := &thread{stack: s, callFrameStack: cfs, sp: 0, cfp: 0}
	s.thread = t
	cfs.thread = t
	t.vm = vm
	return t
}

// ExecInstructions accepts a sequence of bytecodes and use vm to evaluate them.
func (vm *VM) ExecInstructions(sets []*bytecode.InstructionSet, fn string) {
	translator := newInstructionTranslator(fn)
	translator.vm = vm
	translator.transferInstructionSets(sets)

	// Keep instruction set table updated after parsed new files.
	// TODO: Find more efficient way to do this.
	for setType, table := range translator.setTable {
		for name, is := range table {
			vm.isTables[setType][name] = is
		}
	}

	vm.blockTables[translator.filename] = translator.blockTable
	vm.SetClassISIndexTable(translator.filename)
	vm.SetMethodISIndexTable(translator.filename)

	cf := newNormalCallFrame(translator.program, translator.filename, 1)
	cf.self = vm.mainObj
	vm.mainThread.callFrameStack.push(cf)

	defer func() {
		err, ok := recover().(*Error)

		if ok && vm.mode == NormalMode {
			fmt.Println(err.Message())
		}
	}()

	vm.mainThread.startFromTopFrame()
}

// SetClassISIndexTable adds new instruction set's index table to vm.classISIndexTables
func (vm *VM) SetClassISIndexTable(fn filename) {
	vm.classISIndexTables[fn] = newISIndexTable()
}

// SetMethodISIndexTable adds new instruction set's index table to vm.methodISIndexTables
func (vm *VM) SetMethodISIndexTable(fn filename) {
	vm.methodISIndexTables[fn] = newISIndexTable()
}

// main object singleton methods -----------------------------------------------------
func builtinMainObjSingletonMethods() []*BuiltinMethodObject {
	return []*BuiltinMethodObject{
		{
			Name: "to_s",
			Fn: func(receiver Object, sourceLine int) builtinMethodBody {
				return func(thread *thread, objects []Object, frame *normalCallFrame) Object {
					return thread.vm.initStringObject("main")
				}
			},
		},
	}
}

func (vm *VM) initMainObj() *RObject {
	obj := vm.objectClass.initializeInstance()
	singletonClass := vm.initializeClass(fmt.Sprintf("#<Class:%s>", obj.toString()), false)
	singletonClass.Methods.set("include", vm.topLevelClass(classes.ClassClass).lookupMethod("include"))
	singletonClass.setBuiltinMethods(builtinMainObjSingletonMethods(), false)
	obj.singletonClass = singletonClass

	return obj
}

func (vm *VM) initConstants() {
	// Init Class and Object
	cClass := initClassClass()
	vm.objectClass = initObjectClass(cClass)
	vm.topLevelClass(classes.ObjectClass).setClassConstant(cClass)

	// Init builtin classes
	builtinClasses := []*RClass{
		vm.initIntegerClass(),
		vm.initFloatClass(),
		vm.initStringClass(),
		vm.initBoolClass(),
		vm.initNullClass(),
		vm.initArrayClass(),
		vm.initHashClass(),
		vm.initRangeClass(),
		vm.initMethodClass(),
		vm.initBlockClass(),
		vm.initChannelClass(),
		vm.initGoClass(),
		vm.initFileClass(),
		vm.initRegexpClass(),
		vm.initMatchDataClass(),
		vm.initGoMapClass(),
		vm.initDecimalClass(),
	}

	// Init error classes
	vm.initErrorClasses()

	for _, c := range builtinClasses {
		vm.objectClass.setClassConstant(c)
	}

	// Init ARGV
	args := []Object{}

	for _, arg := range vm.args {
		args = append(args, vm.initStringObject(arg))
	}

	vm.objectClass.constants["ARGV"] = &Pointer{Target: vm.initArrayObject(args)}

	// Init ENV
	envs := map[string]Object{}

	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		envs[pair[0]] = vm.initStringObject(pair[1])
	}

	vm.objectClass.constants["ENV"] = &Pointer{Target: vm.initHashObject(envs)}
	vm.objectClass.constants["STDOUT"] = &Pointer{Target: vm.initFileObject(os.Stdout)}
	vm.objectClass.constants["STDERR"] = &Pointer{Target: vm.initFileObject(os.Stderr)}
	vm.objectClass.constants["STDIN"] = &Pointer{Target: vm.initFileObject(os.Stdin)}
}

func (vm *VM) topLevelClass(cn string) *RClass {
	objClass := vm.objectClass

	if cn == classes.ObjectClass {
		return objClass
	}

	return objClass.constants[cn].Target.(*RClass)
}

func (vm *VM) currentFilePath() string {
	frame := vm.mainThread.callFrameStack.top()
	return frame.FileName()
}

// loadConstant makes sure we don't create a class twice.
func (vm *VM) loadConstant(name string, isModule bool) *RClass {
	var c *RClass
	var ptr *Pointer

	ptr = vm.objectClass.constants[name]

	if ptr == nil {
		c = vm.initializeClass(name, isModule)
		vm.objectClass.setClassConstant(c)
	} else {
		c = ptr.Target.(*RClass)
	}

	return c
}

func (vm *VM) lookupConstant(cf callFrame, constName string) (constant *Pointer) {
	var namespace *RClass
	var hasNamespace bool

	top := vm.mainThread.stack.top()

	if top == nil {
		hasNamespace = false
	} else {
		namespace, hasNamespace = top.Target.(*RClass)
	}

	if hasNamespace {
		constant = namespace.lookupConstantUnderAllScope(constName)

		if constant != nil {
			return
		}
	}

	constant = cf.lookupConstantUnderAllScope(constName)

	if constant == nil {
		constant = vm.objectClass.constants[constName]
	}

	if constName == classes.ObjectClass {
		constant = &Pointer{Target: vm.objectClass}
	}

	return
}
<|endoftext|>


<commit_before>package gin_alexa

import (
    "time"
    "encoding/json"
    "errors"
)

// ****************************************************************
//
// Taken from SkillServer (https://github.com/mikeflynn/go-alexa)
//
// ****************************************************************

// Request Functions
func (a *AlexaRequest) VerifyTimestamp() bool {
    reqTimestamp, _ := time.Parse("2006-01-02T15:04:05Z", a.Request.Timestamp)
    if time.Since(reqTimestamp) < time.Duration(150)*time.Second {
        return true
    }

    return false
}

func (a *AlexaRequest) VerifyAppID(myAppID string) bool {
    if a.Session.Application.ApplicationID == myAppID {
        return true
    }

    return false
}

func (a *AlexaRequest) GetSessionID() string {
    return a.Session.SessionID
}

func (a *AlexaRequest) GetUserID() string {
    return a.Session.User.UserID
}

func (a *AlexaRequest) GetRequestType() string {
    return a.Request.Type
}

func (a *AlexaRequest) GetIntentName() string {
    if a.GetRequestType() == "IntentRequest" {
        return a.Request.Intent.Name
    }

    return a.GetRequestType()
}

func (a *AlexaRequest) GetSlotValue(slotName string) (string, error) {
    if _, ok := a.Request.Intent.Slots[slotName]; ok {
        return a.Request.Intent.Slots[slotName].Value, nil
    }

    return "", errors.New("Slot name not found.")
}

func (a *AlexaRequest) AllSlots() map[string]AlexaSlot {
    return a.Request.Intent.Slots
}

// Response Functions
func NewAlexaResponse() *AlexaResponse {
    er := &AlexaResponse{
        Version: "1.0",
        Response: AlexaRespBody{
            ShouldEndSession: true,
        },
    }

    return er
}

func (a *AlexaResponse) OutputSpeech(text string) *AlexaResponse {
    a.Response.OutputSpeech = &AlexaRespPayload{
        Type: "PlainText",
        Text: text,
    }

    return a
}

func (a *AlexaResponse) OutputSpeechSSML(text string) *AlexaResponse {
    a.Response.OutputSpeech = &AlexaRespPayload{
        Type: "SSML",
        SSML: text,
    }

    return a
}

func (a *AlexaResponse) Card(title string, content string) *AlexaResponse {
    return a.SimpleCard(title, content)
}

func (a *AlexaResponse) SimpleCard(title string, content string) *AlexaResponse {
    a.Response.Card = &AlexaRespPayload{
        Type:    "Simple",
        Title:   title,
        Content: content,
    }

    return a
}

func (a *AlexaResponse) StandardCard(title string, content string, smallImg string, largeImg string) *AlexaResponse {
    a.Response.Card = &AlexaRespPayload{
        Type:    "Standard",
        Title:   title,
        Content: content,
    }

    if smallImg != "" {
        a.Response.Card.Image.SmallImageURL = smallImg
    }

    if largeImg != "" {
        a.Response.Card.Image.LargeImageURL = largeImg
    }

    return a
}

func (a *AlexaResponse) LinkAccountCard() *AlexaResponse {
    a.Response.Card = &AlexaRespPayload{
        Type: "LinkAccount",
    }

    return a
}

func (a *AlexaResponse) Reprompt(text string) *AlexaResponse {
    a.Response.Reprompt = &AlexaReprompt{
        OutputSpeech: AlexaRespPayload{
            Type: "PlainText",
            Text: text,
        },
    }

    return a
}

func (a *AlexaResponse) EndSession(flag bool) *AlexaResponse {
    a.Response.ShouldEndSession = flag

    return a
}

func (a *AlexaResponse) String() ([]byte, error) {
    jsonStr, err := json.Marshal(a)
    if err != nil {
        return nil, err
    }

    return jsonStr, nil
}

// Request Types

type AlexaRequest struct {
    Version string      `json:"version"`
    Session AlexaSession `json:"session"`
    Request AlexaReqBody `json:"request"`
}

type AlexaSession struct {
    New         bool   `json:"new"`
    SessionID   string `json:"sessionId"`
    Application struct {
                    ApplicationID string `json:"applicationId"`
                } `json:"application"`
    Attributes struct {
                    String map[string]interface{} `json:"string"`
                } `json:"attributes"`
    User struct {
                    UserID      string `json:"userId"`
                    AccessToken string `json:"accessToken,omitempty"`
                } `json:"user"`
}

type AlexaReqBody struct {
    Type      string     `json:"type"`
    RequestID string     `json:"requestId"`
    Timestamp string     `json:"timestamp"`
    Intent    AlexaIntent `json:"intent,omitempty"`
    Reason    string     `json:"reason,omitempty"`
}

type AlexaIntent struct {
    Name  string              `json:"name"`
    Slots map[string]AlexaSlot `json:"slots"`
}

type AlexaSlot struct {
    Name  string `json:"name"`
    Value string `json:"value"`
}

// Response Types

type AlexaResponse struct {
    Version           string                 `json:"version"`
    SessionAttributes map[string]interface{} `json:"sessionAttributes,omitempty"`
    Response          AlexaRespBody           `json:"response"`
}

type AlexaRespBody struct {
    OutputSpeech     *AlexaRespPayload `json:"outputSpeech,omitempty"`
    Card             *AlexaRespPayload `json:"card,omitempty"`
    Reprompt         *AlexaReprompt    `json:"reprompt,omitempty"` // Pointer so it's dropped if empty in JSON response.
    ShouldEndSession bool             `json:"shouldEndSession"`
}

type AlexaReprompt struct {
    OutputSpeech AlexaRespPayload `json:"outputSpeech,omitempty"`
}

type AlexaRespImage struct {
    SmallImageURL string `json:"smallImageUrl,omitempty"`
    LargeImageURL string `json:"largeImageUrl,omitempty"`
}

type AlexaRespPayload struct {
    Type    string        `json:"type,omitempty"`
    Title   string        `json:"title,omitempty"`
    Text    string        `json:"text,omitempty"`
    SSML    string        `json:"ssml,omitempty"`
    Content string        `json:"content,omitempty"`
    Image   AlexaRespImage `json:"image,omitempty"`
}
<commit_msg>added locale to body<commit_after>package gin_alexa

import (
    "time"
    "encoding/json"
    "errors"
)

// ****************************************************************
//
// Taken from SkillServer (https://github.com/mikeflynn/go-alexa)
//
// ****************************************************************

// Request Functions
func (a *AlexaRequest) VerifyTimestamp() bool {
    reqTimestamp, _ := time.Parse("2006-01-02T15:04:05Z", a.Request.Timestamp)
    if time.Since(reqTimestamp) < time.Duration(150)*time.Second {
        return true
    }

    return false
}

func (a *AlexaRequest) VerifyAppID(myAppID string) bool {
    if a.Session.Application.ApplicationID == myAppID {
        return true
    }

    return false
}

func (a *AlexaRequest) GetSessionID() string {
    return a.Session.SessionID
}

func (a *AlexaRequest) GetUserID() string {
    return a.Session.User.UserID
}

func (a *AlexaRequest) GetRequestType() string {
    return a.Request.Type
}

func (a *AlexaRequest) GetIntentName() string {
    if a.GetRequestType() == "IntentRequest" {
        return a.Request.Intent.Name
    }

    return a.GetRequestType()
}

func (a *AlexaRequest) GetSlotValue(slotName string) (string, error) {
    if _, ok := a.Request.Intent.Slots[slotName]; ok {
        return a.Request.Intent.Slots[slotName].Value, nil
    }

    return "", errors.New("Slot name not found.")
}

func (a *AlexaRequest) AllSlots() map[string]AlexaSlot {
    return a.Request.Intent.Slots
}

// Response Functions
func NewAlexaResponse() *AlexaResponse {
    er := &AlexaResponse{
        Version: "1.0",
        Response: AlexaRespBody{
            ShouldEndSession: true,
        },
    }

    return er
}

func (a *AlexaResponse) OutputSpeech(text string) *AlexaResponse {
    a.Response.OutputSpeech = &AlexaRespPayload{
        Type: "PlainText",
        Text: text,
    }

    return a
}

func (a *AlexaResponse) OutputSpeechSSML(text string) *AlexaResponse {
    a.Response.OutputSpeech = &AlexaRespPayload{
        Type: "SSML",
        SSML: text,
    }

    return a
}

func (a *AlexaResponse) Card(title string, content string) *AlexaResponse {
    return a.SimpleCard(title, content)
}

func (a *AlexaResponse) SimpleCard(title string, content string) *AlexaResponse {
    a.Response.Card = &AlexaRespPayload{
        Type:    "Simple",
        Title:   title,
        Content: content,
    }

    return a
}

func (a *AlexaResponse) StandardCard(title string, content string, smallImg string, largeImg string) *AlexaResponse {
    a.Response.Card = &AlexaRespPayload{
        Type:    "Standard",
        Title:   title,
        Content: content,
    }

    if smallImg != "" {
        a.Response.Card.Image.SmallImageURL = smallImg
    }

    if largeImg != "" {
        a.Response.Card.Image.LargeImageURL = largeImg
    }

    return a
}

func (a *AlexaResponse) LinkAccountCard() *AlexaResponse {
    a.Response.Card = &AlexaRespPayload{
        Type: "LinkAccount",
    }

    return a
}

func (a *AlexaResponse) Reprompt(text string) *AlexaResponse {
    a.Response.Reprompt = &AlexaReprompt{
        OutputSpeech: AlexaRespPayload{
            Type: "PlainText",
            Text: text,
        },
    }

    return a
}

func (a *AlexaResponse) EndSession(flag bool) *AlexaResponse {
    a.Response.ShouldEndSession = flag

    return a
}

func (a *AlexaResponse) String() ([]byte, error) {
    jsonStr, err := json.Marshal(a)
    if err != nil {
        return nil, err
    }

    return jsonStr, nil
}

// Request Types

type AlexaRequest struct {
    Version string      `json:"version"`
    Session AlexaSession `json:"session"`
    Request AlexaReqBody `json:"request"`
}

type AlexaSession struct {
    New         bool   `json:"new"`
    SessionID   string `json:"sessionId"`
    Application struct {
                    ApplicationID string `json:"applicationId"`
                } `json:"application"`
    Attributes struct {
                    String map[string]interface{} `json:"string"`
                } `json:"attributes"`
    User struct {
                    UserID      string `json:"userId"`
                    AccessToken string `json:"accessToken,omitempty"`
                } `json:"user"`
}

type AlexaReqBody struct {
    Type      string     `json:"type"`
    RequestID string     `json:"requestId"`
    Timestamp string     `json:"timestamp"`
    Locale    string     `json:"locale,omitempty"`
    Intent    AlexaIntent `json:"intent,omitempty"`
    Reason    string     `json:"reason,omitempty"`
}

type AlexaIntent struct {
    Name  string              `json:"name"`
    Slots map[string]AlexaSlot `json:"slots"`
}

type AlexaSlot struct {
    Name  string `json:"name"`
    Value string `json:"value"`
}

// Response Types

type AlexaResponse struct {
    Version           string                 `json:"version"`
    SessionAttributes map[string]interface{} `json:"sessionAttributes,omitempty"`
    Response          AlexaRespBody           `json:"response"`
}

type AlexaRespBody struct {
    OutputSpeech     *AlexaRespPayload `json:"outputSpeech,omitempty"`
    Card             *AlexaRespPayload `json:"card,omitempty"`
    Reprompt         *AlexaReprompt    `json:"reprompt,omitempty"` // Pointer so it's dropped if empty in JSON response.
    ShouldEndSession bool             `json:"shouldEndSession"`
}

type AlexaReprompt struct {
    OutputSpeech AlexaRespPayload `json:"outputSpeech,omitempty"`
}

type AlexaRespImage struct {
    SmallImageURL string `json:"smallImageUrl,omitempty"`
    LargeImageURL string `json:"largeImageUrl,omitempty"`
}

type AlexaRespPayload struct {
    Type    string        `json:"type,omitempty"`
    Title   string        `json:"title,omitempty"`
    Text    string        `json:"text,omitempty"`
    SSML    string        `json:"ssml,omitempty"`
    Content string        `json:"content,omitempty"`
    Image   AlexaRespImage `json:"image,omitempty"`
}
<|endoftext|>


<commit_before>// Package letsencrypt integrates Let's Encrypt functionality into Caddy
// with first-class support for creating and renewing certificates
// automatically. It is designed to configure sites for HTTPS by default.
package letsencrypt

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/mholt/caddy/middleware"
	"github.com/mholt/caddy/middleware/redirect"
	"github.com/mholt/caddy/server"
	"github.com/xenolf/lego/acme"
)

// OnRenew is the function that will be used to restart
// the application or the part of the application that uses
// the certificates maintained by this package. When at least
// one certificate is renewed, this function will be called.
var OnRenew func() error

// Activate sets up TLS for each server config in configs
// as needed. It only skips the config if the cert and key
// are already provided or if plaintext http is explicitly
// specified as the port.
//
// This function may prompt the user to provide an email
// address if none is available through other means. It
// prefers the email address specified in the config, but
// if that is not available it will check the command line
// argument. If absent, it will use the most recent email
// address from last time. If there isn't one, the user
// will be prompted. If the user leaves email blank, <TODO>.
func Activate(configs []server.Config) ([]server.Config, error) {
	// First identify and configure any elligible hosts for which
	// we already have certs and keys in storage from last time.
	configLen := len(configs) // avoid infinite loop since this loop appends plaintext to the slice
	for i := 0; i < configLen; i++ {
		if existingCertAndKey(configs[i].Host) && configs[i].TLS.LetsEncryptEmail != "off" {
			configs = autoConfigure(&configs[i], configs)
		}
	}

	// First renew any existing certificates that need it
	processCertificateRenewal(configs)

	// Group configs by LE email address; this will help us
	// reduce round-trips when getting the certs.
	initMap, err := groupConfigsByEmail(configs)
	if err != nil {
		return configs, err
	}

	// Loop through each email address and obtain certs; we can obtain more
	// than one certificate per email address, and still save them individually.
	for leEmail, serverConfigs := range initMap {
		// make client to service this email address with CA server
		client, err := newClient(leEmail)
		if err != nil {
			return configs, err
		}

		// client is ready, so let's get free, trusted SSL certificates! yeah!
		certificates, err := obtainCertificates(client, serverConfigs)
		if err != nil {
			return configs, err
		}

		// ... that's it. save the certs, keys, and metadata files to disk
		err = saveCertsAndKeys(certificates)
		if err != nil {
			return configs, err
		}

		// it all comes down to this: turning TLS on for all the configs
		for _, cfg := range serverConfigs {
			configs = autoConfigure(cfg, configs)
		}
	}

	go keepCertificatesRenewed(configs)

	return configs, nil
}

// groupConfigsByEmail groups configs by the Let's Encrypt email address
// associated to them or to the default Let's Encrypt email address. If the
// default email is not available, the user will be prompted to provide one.
//
// This function also filters out configs that don't need extra TLS help.
// Configurations with a manual TLS configuration or one that is already
// found in storage will not be added to any group.
func groupConfigsByEmail(configs []server.Config) (map[string][]*server.Config, error) {
	// configQualifies returns true if cfg qualifes for automatic LE activation
	configQualifies := func(cfg server.Config) bool {
		return cfg.TLS.Certificate == "" && // user could provide their own cert and key
			cfg.TLS.Key == "" &&

			// user can force-disable automatic HTTPS for this host
			cfg.Port != "http" &&
			cfg.TLS.LetsEncryptEmail != "off" &&

			// obviously we get can't certs for loopback or internal hosts
			cfg.Host != "localhost" &&
			cfg.Host != "" &&
			cfg.Host != "0.0.0.0" &&
			cfg.Host != "::1" &&
			!strings.HasPrefix(cfg.Host, "127.") &&
			!strings.HasPrefix(cfg.Host, "10.") &&

			// make sure an HTTPS version of this config doesn't exist in the list already
			!hostHasOtherScheme(cfg.Host, "https", configs)
	}

	initMap := make(map[string][]*server.Config)
	for i := 0; i < len(configs); i++ {
		if !configQualifies(configs[i]) {
			continue
		}
		leEmail := getEmail(configs[i])
		if leEmail == "" {
			return nil, errors.New("must have email address to serve HTTPS without existing certificate and key")
		}
		initMap[leEmail] = append(initMap[leEmail], &configs[i])
	}
	return initMap, nil
}

// existingCertAndKey returns true if the host has a certificate
// and private key in storage already, false otherwise.
func existingCertAndKey(host string) bool {
	_, err := os.Stat(storage.SiteCertFile(host))
	if err != nil {
		return false
	}
	_, err = os.Stat(storage.SiteKeyFile(host))
	if err != nil {
		return false
	}
	return true
}

// newClient creates a new ACME client to facilitate communication
// with the Let's Encrypt CA server on behalf of the user specified
// by leEmail. As part of this process, a user will be loaded from
// disk (if already exists) or created new and registered via ACME
// and saved to the file system for next time.
func newClient(leEmail string) (*acme.Client, error) {
	// Look up or create the LE user account
	leUser, err := getUser(leEmail)
	if err != nil {
		return nil, err
	}

	// The client facilitates our communication with the CA server.
	client := acme.NewClient(CAUrl, &leUser, rsaKeySizeToUse, exposePort)

	// If not registered, the user must register an account with the CA
	// and agree to terms
	if leUser.Registration == nil {
		reg, err := client.Register()
		if err != nil {
			return nil, errors.New("registration error: " + err.Error())
		}
		leUser.Registration = reg

		// TODO: we can just do the agreement once: when registering, right?
		err = client.AgreeToTOS()
		if err != nil {
			saveUser(leUser) // TODO: Might as well try, right? Error check?
			return nil, errors.New("error agreeing to terms: " + err.Error())
		}

		// save user to the file system
		err = saveUser(leUser)
		if err != nil {
			return nil, errors.New("could not save user: " + err.Error())
		}
	}

	return client, nil
}

// obtainCertificates obtains certificates from the CA server for
// the configurations in serverConfigs using client.
func obtainCertificates(client *acme.Client, serverConfigs []*server.Config) ([]acme.CertificateResource, error) {
	// collect all the hostnames into one slice
	var hosts []string
	for _, cfg := range serverConfigs {
		hosts = append(hosts, cfg.Host)
	}

	certificates, err := client.ObtainCertificates(hosts, true)
	if err != nil {
		return nil, errors.New("error obtaining certs: " + err.Error())
	}

	return certificates, nil
}

// saveCertificates saves each certificate resource to disk. This
// includes the certificate file itself, the private key, and the
// metadata file.
func saveCertsAndKeys(certificates []acme.CertificateResource) error {
	for _, cert := range certificates {
		os.MkdirAll(storage.Site(cert.Domain), 0700)

		// Save cert
		err := ioutil.WriteFile(storage.SiteCertFile(cert.Domain), cert.Certificate, 0600)
		if err != nil {
			return err
		}

		// Save private key
		err = ioutil.WriteFile(storage.SiteKeyFile(cert.Domain), cert.PrivateKey, 0600)
		if err != nil {
			return err
		}

		// Save cert metadata
		jsonBytes, err := json.MarshalIndent(&cert, "", "\t")
		if err != nil {
			return err
		}
		err = ioutil.WriteFile(storage.SiteMetaFile(cert.Domain), jsonBytes, 0600)
		if err != nil {
			return err
		}
	}
	return nil
}

// autoConfigure enables TLS on cfg and appends, if necessary, a new config
// to allConfigs that redirects plaintext HTTP to its new HTTPS counterpart.
func autoConfigure(cfg *server.Config, allConfigs []server.Config) []server.Config {
	bundleBytes, err := ioutil.ReadFile(storage.SiteCertFile(cfg.Host))
	// TODO: Handle these errors better
	if err == nil {
		ocsp, err := acme.GetOCSPForCert(bundleBytes)
		if err == nil {
			cfg.TLS.OCSPStaple = ocsp
		}
	}
	cfg.TLS.Certificate = storage.SiteCertFile(cfg.Host)
	cfg.TLS.Key = storage.SiteKeyFile(cfg.Host)
	cfg.TLS.Enabled = true
	cfg.Port = "https"

	// Set up http->https redirect as long as there isn't already
	// a http counterpart in the configs
	if !hostHasOtherScheme(cfg.Host, "http", allConfigs) {
		allConfigs = append(allConfigs, redirPlaintextHost(*cfg))
	}

	return allConfigs
}

// hostHasOtherScheme tells you whether there is another config in the list
// for the same host but with the port equal to scheme. For example, to see
// if example.com has a https variant already, pass in example.com and
// "https" along with the list of configs.
func hostHasOtherScheme(host, scheme string, allConfigs []server.Config) bool {
	for _, otherCfg := range allConfigs {
		if otherCfg.Host == host && otherCfg.Port == scheme {
			return true
		}
	}
	return false
}

// redirPlaintextHost returns a new plaintext HTTP configuration for
// a virtualHost that simply redirects to cfg, which is assumed to
// be the HTTPS configuration. The returned configuration is set
// to listen on the "http" port (port 80).
func redirPlaintextHost(cfg server.Config) server.Config {
	redirMidware := func(next middleware.Handler) middleware.Handler {
		return redirect.Redirect{Next: next, Rules: []redirect.Rule{
			{
				FromScheme: "http",
				FromPath:   "/",
				To:         "https://" + cfg.Host + "{uri}",
				Code:       http.StatusMovedPermanently,
			},
		}}
	}

	return server.Config{
		Host: cfg.Host,
		Port: "http",
		Middleware: map[string][]middleware.Middleware{
			"/": []middleware.Middleware{redirMidware},
		},
	}
}

// Revoke revokes the certificate for host via ACME protocol.
func Revoke(host string) error {
	if !existingCertAndKey(host) {
		return errors.New("no certificate and key for " + host)
	}

	email := getEmail(server.Config{Host: host})
	if email == "" {
		return errors.New("email is required to revoke")
	}

	client, err := newClient(email)
	if err != nil {
		return err
	}

	certFile := storage.SiteCertFile(host)
	certBytes, err := ioutil.ReadFile(certFile)
	if err != nil {
		return err
	}

	err = client.RevokeCertificate(certBytes)
	if err != nil {
		return err
	}

	err = os.Remove(certFile)
	if err != nil {
		return errors.New("certificate revoked, but unable to delete certificate file: " + err.Error())
	}

	return nil
}

var (
	// Let's Encrypt account email to use if none provided
	DefaultEmail string

	// Whether user has agreed to the Let's Encrypt SA
	Agreed bool

	// The base URL to the CA's ACME endpoint
	CAUrl string
)

// Some essential values related to the Let's Encrypt process
const (
	// The port to expose to the CA server for Simple HTTP Challenge
	exposePort = "5001"

	// How often to check certificates for renewal
	renewInterval = 24 * time.Hour
)

// KeySize represents the length of a key in bits.
type KeySize int

// Key sizes are used to determine the strength of a key.
const (
	ECC_224  KeySize = 224
	ECC_256          = 256
	RSA_2048         = 2048
	RSA_4096         = 4096
)

// rsaKeySizeToUse is the size to use for new RSA keys.
// This shouldn't need to change except for in tests;
// the size can be drastically reduced for speed.
var rsaKeySizeToUse = RSA_2048
<commit_msg>Track the latest lego OCSP changes<commit_after>// Package letsencrypt integrates Let's Encrypt functionality into Caddy
// with first-class support for creating and renewing certificates
// automatically. It is designed to configure sites for HTTPS by default.
package letsencrypt

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/mholt/caddy/middleware"
	"github.com/mholt/caddy/middleware/redirect"
	"github.com/mholt/caddy/server"
	"github.com/xenolf/lego/acme"
)

// OnRenew is the function that will be used to restart
// the application or the part of the application that uses
// the certificates maintained by this package. When at least
// one certificate is renewed, this function will be called.
var OnRenew func() error

// Activate sets up TLS for each server config in configs
// as needed. It only skips the config if the cert and key
// are already provided or if plaintext http is explicitly
// specified as the port.
//
// This function may prompt the user to provide an email
// address if none is available through other means. It
// prefers the email address specified in the config, but
// if that is not available it will check the command line
// argument. If absent, it will use the most recent email
// address from last time. If there isn't one, the user
// will be prompted. If the user leaves email blank, <TODO>.
func Activate(configs []server.Config) ([]server.Config, error) {
	// First identify and configure any elligible hosts for which
	// we already have certs and keys in storage from last time.
	configLen := len(configs) // avoid infinite loop since this loop appends plaintext to the slice
	for i := 0; i < configLen; i++ {
		if existingCertAndKey(configs[i].Host) && configs[i].TLS.LetsEncryptEmail != "off" {
			configs = autoConfigure(&configs[i], configs)
		}
	}

	// First renew any existing certificates that need it
	processCertificateRenewal(configs)

	// Group configs by LE email address; this will help us
	// reduce round-trips when getting the certs.
	initMap, err := groupConfigsByEmail(configs)
	if err != nil {
		return configs, err
	}

	// Loop through each email address and obtain certs; we can obtain more
	// than one certificate per email address, and still save them individually.
	for leEmail, serverConfigs := range initMap {
		// make client to service this email address with CA server
		client, err := newClient(leEmail)
		if err != nil {
			return configs, err
		}

		// client is ready, so let's get free, trusted SSL certificates! yeah!
		certificates, err := obtainCertificates(client, serverConfigs)
		if err != nil {
			return configs, err
		}

		// ... that's it. save the certs, keys, and metadata files to disk
		err = saveCertsAndKeys(certificates)
		if err != nil {
			return configs, err
		}

		// it all comes down to this: turning TLS on for all the configs
		for _, cfg := range serverConfigs {
			configs = autoConfigure(cfg, configs)
		}
	}

	go keepCertificatesRenewed(configs)

	return configs, nil
}

// groupConfigsByEmail groups configs by the Let's Encrypt email address
// associated to them or to the default Let's Encrypt email address. If the
// default email is not available, the user will be prompted to provide one.
//
// This function also filters out configs that don't need extra TLS help.
// Configurations with a manual TLS configuration or one that is already
// found in storage will not be added to any group.
func groupConfigsByEmail(configs []server.Config) (map[string][]*server.Config, error) {
	// configQualifies returns true if cfg qualifes for automatic LE activation
	configQualifies := func(cfg server.Config) bool {
		return cfg.TLS.Certificate == "" && // user could provide their own cert and key
			cfg.TLS.Key == "" &&

			// user can force-disable automatic HTTPS for this host
			cfg.Port != "http" &&
			cfg.TLS.LetsEncryptEmail != "off" &&

			// obviously we get can't certs for loopback or internal hosts
			cfg.Host != "localhost" &&
			cfg.Host != "" &&
			cfg.Host != "0.0.0.0" &&
			cfg.Host != "::1" &&
			!strings.HasPrefix(cfg.Host, "127.") &&
			!strings.HasPrefix(cfg.Host, "10.") &&

			// make sure an HTTPS version of this config doesn't exist in the list already
			!hostHasOtherScheme(cfg.Host, "https", configs)
	}

	initMap := make(map[string][]*server.Config)
	for i := 0; i < len(configs); i++ {
		if !configQualifies(configs[i]) {
			continue
		}
		leEmail := getEmail(configs[i])
		if leEmail == "" {
			return nil, errors.New("must have email address to serve HTTPS without existing certificate and key")
		}
		initMap[leEmail] = append(initMap[leEmail], &configs[i])
	}
	return initMap, nil
}

// existingCertAndKey returns true if the host has a certificate
// and private key in storage already, false otherwise.
func existingCertAndKey(host string) bool {
	_, err := os.Stat(storage.SiteCertFile(host))
	if err != nil {
		return false
	}
	_, err = os.Stat(storage.SiteKeyFile(host))
	if err != nil {
		return false
	}
	return true
}

// newClient creates a new ACME client to facilitate communication
// with the Let's Encrypt CA server on behalf of the user specified
// by leEmail. As part of this process, a user will be loaded from
// disk (if already exists) or created new and registered via ACME
// and saved to the file system for next time.
func newClient(leEmail string) (*acme.Client, error) {
	// Look up or create the LE user account
	leUser, err := getUser(leEmail)
	if err != nil {
		return nil, err
	}

	// The client facilitates our communication with the CA server.
	client := acme.NewClient(CAUrl, &leUser, rsaKeySizeToUse, exposePort)

	// If not registered, the user must register an account with the CA
	// and agree to terms
	if leUser.Registration == nil {
		reg, err := client.Register()
		if err != nil {
			return nil, errors.New("registration error: " + err.Error())
		}
		leUser.Registration = reg

		// TODO: we can just do the agreement once: when registering, right?
		err = client.AgreeToTOS()
		if err != nil {
			saveUser(leUser) // TODO: Might as well try, right? Error check?
			return nil, errors.New("error agreeing to terms: " + err.Error())
		}

		// save user to the file system
		err = saveUser(leUser)
		if err != nil {
			return nil, errors.New("could not save user: " + err.Error())
		}
	}

	return client, nil
}

// obtainCertificates obtains certificates from the CA server for
// the configurations in serverConfigs using client.
func obtainCertificates(client *acme.Client, serverConfigs []*server.Config) ([]acme.CertificateResource, error) {
	// collect all the hostnames into one slice
	var hosts []string
	for _, cfg := range serverConfigs {
		hosts = append(hosts, cfg.Host)
	}

	certificates, err := client.ObtainCertificates(hosts, true)
	if err != nil {
		return nil, errors.New("error obtaining certs: " + err.Error())
	}

	return certificates, nil
}

// saveCertificates saves each certificate resource to disk. This
// includes the certificate file itself, the private key, and the
// metadata file.
func saveCertsAndKeys(certificates []acme.CertificateResource) error {
	for _, cert := range certificates {
		os.MkdirAll(storage.Site(cert.Domain), 0700)

		// Save cert
		err := ioutil.WriteFile(storage.SiteCertFile(cert.Domain), cert.Certificate, 0600)
		if err != nil {
			return err
		}

		// Save private key
		err = ioutil.WriteFile(storage.SiteKeyFile(cert.Domain), cert.PrivateKey, 0600)
		if err != nil {
			return err
		}

		// Save cert metadata
		jsonBytes, err := json.MarshalIndent(&cert, "", "\t")
		if err != nil {
			return err
		}
		err = ioutil.WriteFile(storage.SiteMetaFile(cert.Domain), jsonBytes, 0600)
		if err != nil {
			return err
		}
	}
	return nil
}

// autoConfigure enables TLS on cfg and appends, if necessary, a new config
// to allConfigs that redirects plaintext HTTP to its new HTTPS counterpart.
func autoConfigure(cfg *server.Config, allConfigs []server.Config) []server.Config {
	bundleBytes, err := ioutil.ReadFile(storage.SiteCertFile(cfg.Host))
	// TODO: Handle these errors better
	if err == nil {
		ocsp, status, err := acme.GetOCSPForCert(bundleBytes)
		if err == nil && status == acme.OCSPGood {
			cfg.TLS.OCSPStaple = ocsp
		}
	}
	cfg.TLS.Certificate = storage.SiteCertFile(cfg.Host)
	cfg.TLS.Key = storage.SiteKeyFile(cfg.Host)
	cfg.TLS.Enabled = true
	cfg.Port = "https"

	// Set up http->https redirect as long as there isn't already
	// a http counterpart in the configs
	if !hostHasOtherScheme(cfg.Host, "http", allConfigs) {
		allConfigs = append(allConfigs, redirPlaintextHost(*cfg))
	}

	return allConfigs
}

// hostHasOtherScheme tells you whether there is another config in the list
// for the same host but with the port equal to scheme. For example, to see
// if example.com has a https variant already, pass in example.com and
// "https" along with the list of configs.
func hostHasOtherScheme(host, scheme string, allConfigs []server.Config) bool {
	for _, otherCfg := range allConfigs {
		if otherCfg.Host == host && otherCfg.Port == scheme {
			return true
		}
	}
	return false
}

// redirPlaintextHost returns a new plaintext HTTP configuration for
// a virtualHost that simply redirects to cfg, which is assumed to
// be the HTTPS configuration. The returned configuration is set
// to listen on the "http" port (port 80).
func redirPlaintextHost(cfg server.Config) server.Config {
	redirMidware := func(next middleware.Handler) middleware.Handler {
		return redirect.Redirect{Next: next, Rules: []redirect.Rule{
			{
				FromScheme: "http",
				FromPath:   "/",
				To:         "https://" + cfg.Host + "{uri}",
				Code:       http.StatusMovedPermanently,
			},
		}}
	}

	return server.Config{
		Host: cfg.Host,
		Port: "http",
		Middleware: map[string][]middleware.Middleware{
			"/": []middleware.Middleware{redirMidware},
		},
	}
}

// Revoke revokes the certificate for host via ACME protocol.
func Revoke(host string) error {
	if !existingCertAndKey(host) {
		return errors.New("no certificate and key for " + host)
	}

	email := getEmail(server.Config{Host: host})
	if email == "" {
		return errors.New("email is required to revoke")
	}

	client, err := newClient(email)
	if err != nil {
		return err
	}

	certFile := storage.SiteCertFile(host)
	certBytes, err := ioutil.ReadFile(certFile)
	if err != nil {
		return err
	}

	err = client.RevokeCertificate(certBytes)
	if err != nil {
		return err
	}

	err = os.Remove(certFile)
	if err != nil {
		return errors.New("certificate revoked, but unable to delete certificate file: " + err.Error())
	}

	return nil
}

var (
	// Let's Encrypt account email to use if none provided
	DefaultEmail string

	// Whether user has agreed to the Let's Encrypt SA
	Agreed bool

	// The base URL to the CA's ACME endpoint
	CAUrl string
)

// Some essential values related to the Let's Encrypt process
const (
	// The port to expose to the CA server for Simple HTTP Challenge
	exposePort = "5001"

	// How often to check certificates for renewal
	renewInterval = 24 * time.Hour
)

// KeySize represents the length of a key in bits.
type KeySize int

// Key sizes are used to determine the strength of a key.
const (
	ECC_224  KeySize = 224
	ECC_256          = 256
	RSA_2048         = 2048
	RSA_4096         = 4096
)

// rsaKeySizeToUse is the size to use for new RSA keys.
// This shouldn't need to change except for in tests;
// the size can be drastically reduced for speed.
var rsaKeySizeToUse = RSA_2048
<|endoftext|>


<commit_before>package twitter

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/gorilla/pat"
	"github.com/markbates/goth"
	"github.com/mrjones/oauth"
	"github.com/stretchr/testify/assert"
)

func Test_New(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	provider := twitterProvider()
	a.Equal(provider.ClientKey, os.Getenv("TWITTER_KEY"))
	a.Equal(provider.Secret, os.Getenv("TWITTER_SECRET"))
	a.Equal(provider.CallbackURL, "/foo")
}

func Test_Implements_Provider(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	a.Implements((*goth.Provider)(nil), twitterProvider())
}

func Test_BeginAuth(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	mockTwitter(func(ts *httptest.Server) {
		provider := twitterProvider()
		session, err := provider.BeginAuth("state")
		s := session.(*Session)
		a.NoError(err)
		a.Contains(s.AuthURL, "authorize?oauth_token=TOKEN")
		a.Equal("TOKEN", s.RequestToken.Token)
		a.Equal("SECRET", s.RequestToken.Secret)
	})
	mockTwitter(func(ts *httptest.Server) {
		provider := twitterProviderAuthenticate()
		session, err := provider.BeginAuth("state")
		s := session.(*Session)
		a.NoError(err)
		a.Contains(s.AuthURL, "authenticate?oauth_token=TOKEN")
		a.Equal("TOKEN", s.RequestToken.Token)
		a.Equal("SECRET", s.RequestToken.Secret)
	})
}

func Test_FetchUser(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	mockTwitter(func(ts *httptest.Server) {
		provider := twitterProvider()
		session := Session{AccessToken: &oauth.AccessToken{Token: "TOKEN", Secret: "SECRET"}}

		user, err := provider.FetchUser(&session)
		a.NoError(err)

		a.Equal("Homer", user.Name)
		a.Equal("duffman", user.NickName)
		a.Equal("Duff rules!!", user.Description)
		a.Equal("http://example.com/image.jpg", user.AvatarURL)
		a.Equal("1234", user.UserID)
		a.Equal("Springfield", user.Location)
		a.Equal("TOKEN", user.AccessToken)
		a.Empty(user.Email)
	})
}

func Test_SessionFromJSON(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	provider := twitterProvider()

	s, err := provider.UnmarshalSession(`{"AuthURL":"http://com/auth_url","AccessToken":{"Token":"1234567890","Secret":"secret!!","AdditionalData":{}},"RequestToken":{"Token":"0987654321","Secret":"!!secret"}}`)
	a.NoError(err)
	session := s.(*Session)
	a.Equal(session.AuthURL, "http://com/auth_url")
	a.Equal(session.AccessToken.Token, "1234567890")
	a.Equal(session.AccessToken.Secret, "secret!!")
	a.Equal(session.RequestToken.Token, "0987654321")
	a.Equal(session.RequestToken.Secret, "!!secret")
}

func twitterProvider() *Provider {
	return New(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "/foo")
}

func twitterProviderAuthenticate() *Provider {
	return NewAuthenticate(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "/foo")
}

func mockTwitter(f func(*httptest.Server)) {
	p := pat.New()
	p.Get("/oauth/request_token", func(res http.ResponseWriter, req *http.Request) {
		fmt.Fprint(res, "oauth_token=TOKEN&oauth_token_secret=SECRET")
	})
	p.Get("/1.1/account/verify_credentials.json", func(res http.ResponseWriter, req *http.Request) {
		data := map[string]string{
			"name":              "Homer",
			"screen_name":       "duffman",
			"description":       "Duff rules!!",
			"profile_image_url": "http://example.com/image.jpg",
			"id_str":            "1234",
			"location":          "Springfield",
		}
		json.NewEncoder(res).Encode(&data)
	})
	ts := httptest.NewServer(p)
	defer ts.Close()

	originalRequestURL := requestURL
	originalEndpointProfile := endpointProfile

	requestURL = ts.URL + "/oauth/request_token"
	endpointProfile = ts.URL + "/1.1/account/verify_credentials.json"

	f(ts)

	requestURL = originalRequestURL
	endpointProfile = originalEndpointProfile
}
<commit_msg>Updated twitter test to handle email data.<commit_after>package twitter

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/gorilla/pat"
	"github.com/markbates/goth"
	"github.com/mrjones/oauth"
	"github.com/stretchr/testify/assert"
)

func Test_New(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	provider := twitterProvider()
	a.Equal(provider.ClientKey, os.Getenv("TWITTER_KEY"))
	a.Equal(provider.Secret, os.Getenv("TWITTER_SECRET"))
	a.Equal(provider.CallbackURL, "/foo")
}

func Test_Implements_Provider(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	a.Implements((*goth.Provider)(nil), twitterProvider())
}

func Test_BeginAuth(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	mockTwitter(func(ts *httptest.Server) {
		provider := twitterProvider()
		session, err := provider.BeginAuth("state")
		s := session.(*Session)
		a.NoError(err)
		a.Contains(s.AuthURL, "authorize?oauth_token=TOKEN")
		a.Equal("TOKEN", s.RequestToken.Token)
		a.Equal("SECRET", s.RequestToken.Secret)
	})
	mockTwitter(func(ts *httptest.Server) {
		provider := twitterProviderAuthenticate()
		session, err := provider.BeginAuth("state")
		s := session.(*Session)
		a.NoError(err)
		a.Contains(s.AuthURL, "authenticate?oauth_token=TOKEN")
		a.Equal("TOKEN", s.RequestToken.Token)
		a.Equal("SECRET", s.RequestToken.Secret)
	})
}

func Test_FetchUser(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	mockTwitter(func(ts *httptest.Server) {
		provider := twitterProvider()
		session := Session{AccessToken: &oauth.AccessToken{Token: "TOKEN", Secret: "SECRET"}}

		user, err := provider.FetchUser(&session)
		a.NoError(err)

		a.Equal("Homer", user.Name)
		a.Equal("duffman", user.NickName)
		a.Equal("Duff rules!!", user.Description)
		a.Equal("http://example.com/image.jpg", user.AvatarURL)
		a.Equal("1234", user.UserID)
		a.Equal("Springfield", user.Location)
		a.Equal("TOKEN", user.AccessToken)
		a.Equal("duffman@springfield.com", user.Email)
	})
}

func Test_SessionFromJSON(t *testing.T) {
	t.Parallel()
	a := assert.New(t)

	provider := twitterProvider()

	s, err := provider.UnmarshalSession(`{"AuthURL":"http://com/auth_url","AccessToken":{"Token":"1234567890","Secret":"secret!!","AdditionalData":{}},"RequestToken":{"Token":"0987654321","Secret":"!!secret"}}`)
	a.NoError(err)
	session := s.(*Session)
	a.Equal(session.AuthURL, "http://com/auth_url")
	a.Equal(session.AccessToken.Token, "1234567890")
	a.Equal(session.AccessToken.Secret, "secret!!")
	a.Equal(session.RequestToken.Token, "0987654321")
	a.Equal(session.RequestToken.Secret, "!!secret")
}

func twitterProvider() *Provider {
	return New(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "/foo")
}

func twitterProviderAuthenticate() *Provider {
	return NewAuthenticate(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "/foo")
}

func mockTwitter(f func(*httptest.Server)) {
	p := pat.New()
	p.Get("/oauth/request_token", func(res http.ResponseWriter, req *http.Request) {
		fmt.Fprint(res, "oauth_token=TOKEN&oauth_token_secret=SECRET")
	})
	p.Get("/1.1/account/verify_credentials.json", func(res http.ResponseWriter, req *http.Request) {
		data := map[string]string{
			"name":              "Homer",
			"screen_name":       "duffman",
			"description":       "Duff rules!!",
			"profile_image_url": "http://example.com/image.jpg",
			"id_str":            "1234",
			"location":          "Springfield",
			"email":			 "duffman@springfield.com",
		}
		json.NewEncoder(res).Encode(&data)
	})
	ts := httptest.NewServer(p)
	defer ts.Close()

	originalRequestURL := requestURL
	originalEndpointProfile := endpointProfile

	requestURL = ts.URL + "/oauth/request_token"
	endpointProfile = ts.URL + "/1.1/account/verify_credentials.json"

	f(ts)

	requestURL = originalRequestURL
	endpointProfile = originalEndpointProfile
}
<|endoftext|>