<commit_before>// Package browse provides middleware for listing files in a directory
// when directory path is requested instead of a specific file.
package browse

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/url"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/mholt/caddy/caddyhttp/staticfiles"
)

const (
	sortByName = "name"
	sortBySize = "size"
	sortByTime = "time"
)

// Browse is an http.Handler that can show a file listing when
// directories in the given paths are specified.
type Browse struct {
	Next          httpserver.Handler
	Configs       []Config
	IgnoreIndexes bool
}

// Config is a configuration for browsing in a particular path.
type Config struct {
	PathScope string
	Fs        staticfiles.FileServer
	Variables interface{}
	Template  *template.Template
}

// A Listing is the context used to fill out a template.
type Listing struct {
	// The name of the directory (the last element of the path)
	Name string

	// The full path of the request
	Path string

	// Whether the parent directory is browsable
	CanGoUp bool

	// The items (files and folders) in the path
	Items []FileInfo

	// The number of directories in the listing
	NumDirs int

	// The number of files (items that aren't directories) in the listing
	NumFiles int

	// Which sorting order is used
	Sort string

	// And which order
	Order string

	// If ≠0 then Items have been limited to that many elements
	ItemsLimitedTo int

	// Optional custom variables for use in browse templates
	User interface{}

	httpserver.Context
}

// BreadcrumbMap returns l.Path where every element is a map
// of URLs and path segment names.
func (l Listing) BreadcrumbMap() map[string]string {
	result := map[string]string{}

	if len(l.Path) == 0 {
		return result
	}

	// skip trailing slash
	lpath := l.Path
	if lpath[len(lpath)-1] == '/' {
		lpath = lpath[:len(lpath)-1]
	}

	parts := strings.Split(lpath, "/")
	for i, part := range parts {
		if i == 0 && part == "" {
			// Leading slash (root)
			result["/"] = "/"
			continue
		}
		result[strings.Join(parts[:i+1], "/")] = part
	}

	return result
}

// FileInfo is the info about a particular file or directory
type FileInfo struct {
	Name    string
	Size    int64
	URL     string
	ModTime time.Time
	Mode    os.FileMode
	IsDir   bool
}

// HumanSize returns the size of the file as a human-readable string
// in IEC format (i.e. power of 2 or base 1024).
func (fi FileInfo) HumanSize() string {
	return humanize.IBytes(uint64(fi.Size))
}

// HumanModTime returns the modified time of the file as a human-readable string.
func (fi FileInfo) HumanModTime(format string) string {
	return fi.ModTime.Format(format)
}

// Implement sorting for Listing
type byName Listing
type bySize Listing
type byTime Listing

// By Name
func (l byName) Len() int      { return len(l.Items) }
func (l byName) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }

// Treat upper and lower case equally
func (l byName) Less(i, j int) bool {

	// if both are dir or file sort normally
	if l.Items[i].IsDir == l.Items[j].IsDir {
		return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
	} else {
		// always sort dir ahead of file
		return l.Items[i].IsDir
	}
}

// By Size
func (l bySize) Len() int      { return len(l.Items) }
func (l bySize) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }

const directoryOffset = -1 << 31 // = math.MinInt32
func (l bySize) Less(i, j int) bool {
	iSize, jSize := l.Items[i].Size, l.Items[j].Size
	if l.Items[i].IsDir {
		iSize = directoryOffset + iSize
	}
	if l.Items[j].IsDir {
		jSize = directoryOffset + jSize
	}
	return iSize < jSize
}

// By Time
func (l byTime) Len() int           { return len(l.Items) }
func (l byTime) Swap(i, j int)      { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }
func (l byTime) Less(i, j int) bool { return l.Items[i].ModTime.Before(l.Items[j].ModTime) }

// Add sorting method to "Listing"
// it will apply what's in ".Sort" and ".Order"
func (l Listing) applySort() {
	// Check '.Order' to know how to sort
	if l.Order == "desc" {
		switch l.Sort {
		case sortByName:
			sort.Sort(sort.Reverse(byName(l)))
		case sortBySize:
			sort.Sort(sort.Reverse(bySize(l)))
		case sortByTime:
			sort.Sort(sort.Reverse(byTime(l)))
		default:
			// If not one of the above, do nothing
			return
		}
	} else { // If we had more Orderings we could add them here
		switch l.Sort {
		case sortByName:
			sort.Sort(byName(l))
		case sortBySize:
			sort.Sort(bySize(l))
		case sortByTime:
			sort.Sort(byTime(l))
		default:
			// If not one of the above, do nothing
			return
		}
	}
}

func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, config *Config) (Listing, bool) {
	var (
		fileinfos           []FileInfo
		dirCount, fileCount int
		hasIndexFile        bool
	)

	for _, f := range files {
		name := f.Name()

		for _, indexName := range staticfiles.IndexPages {
			if name == indexName {
				hasIndexFile = true
				break
			}
		}

		if f.IsDir() {
			name += "/"
			dirCount++
		} else {
			fileCount++
		}

		url := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name

		if config.Fs.IsHidden(f) {
			continue
		}

		fileinfos = append(fileinfos, FileInfo{
			IsDir:   f.IsDir(),
			Name:    f.Name(),
			Size:    f.Size(),
			URL:     url.String(),
			ModTime: f.ModTime().UTC(),
			Mode:    f.Mode(),
		})
	}

	return Listing{
		Name:     path.Base(urlPath),
		Path:     urlPath,
		CanGoUp:  canGoUp,
		Items:    fileinfos,
		NumDirs:  dirCount,
		NumFiles: fileCount,
	}, hasIndexFile
}

// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
// If so, control is handed over to ServeListing.
func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	// See if there's a browse configuration to match the path
	var bc *Config
	for i := range b.Configs {
		if httpserver.Path(r.URL.Path).Matches(b.Configs[i].PathScope) {
			bc = &b.Configs[i]
			break
		}
	}
	if bc == nil {
		return b.Next.ServeHTTP(w, r)
	}

	// Browse works on existing directories; delegate everything else
	requestedFilepath, err := bc.Fs.Root.Open(r.URL.Path)
	if err != nil {
		switch {
		case os.IsPermission(err):
			return http.StatusForbidden, err
		case os.IsExist(err):
			return http.StatusNotFound, err
		default:
			return b.Next.ServeHTTP(w, r)
		}
	}
	defer requestedFilepath.Close()

	info, err := requestedFilepath.Stat()
	if err != nil {
		switch {
		case os.IsPermission(err):
			return http.StatusForbidden, err
		case os.IsExist(err):
			return http.StatusGone, err
		default:
			return b.Next.ServeHTTP(w, r)
		}
	}
	if !info.IsDir() {
		return b.Next.ServeHTTP(w, r)
	}

	// Do not reply to anything else because it might be nonsensical
	switch r.Method {
	case http.MethodGet, http.MethodHead:
		// proceed, noop
	case "PROPFIND", http.MethodOptions:
		return http.StatusNotImplemented, nil
	default:
		return b.Next.ServeHTTP(w, r)
	}

	// Browsing navigation gets messed up if browsing a directory
	// that doesn't end in "/" (which it should, anyway)
	if !strings.HasSuffix(r.URL.Path, "/") {
		staticfiles.RedirectToDir(w, r)
		return 0, nil
	}

	return b.ServeListing(w, r, requestedFilepath, bc)
}

func (b Browse) loadDirectoryContents(requestedFilepath http.File, urlPath string, config *Config) (*Listing, bool, error) {
	files, err := requestedFilepath.Readdir(-1)
	if err != nil {
		return nil, false, err
	}

	// Determine if user can browse up another folder
	var canGoUp bool
	curPathDir := path.Dir(strings.TrimSuffix(urlPath, "/"))
	for _, other := range b.Configs {
		if strings.HasPrefix(curPathDir, other.PathScope) {
			canGoUp = true
			break
		}
	}

	// Assemble listing of directory contents
	listing, hasIndex := directoryListing(files, canGoUp, urlPath, config)

	return &listing, hasIndex, nil
}

// handleSortOrder gets and stores for a Listing the 'sort' and 'order',
// and reads 'limit' if given. The latter is 0 if not given.
//
// This sets Cookies.
func (b Browse) handleSortOrder(w http.ResponseWriter, r *http.Request, scope string) (sort string, order string, limit int, err error) {
	sort, order, limitQuery := r.URL.Query().Get("sort"), r.URL.Query().Get("order"), r.URL.Query().Get("limit")

	// If the query 'sort' or 'order' is empty, use defaults or any values previously saved in Cookies
	switch sort {
	case "":
		sort = sortByName
		if sortCookie, sortErr := r.Cookie("sort"); sortErr == nil {
			sort = sortCookie.Value
		}
	case sortByName, sortBySize, sortByTime:
		http.SetCookie(w, &http.Cookie{Name: "sort", Value: sort, Path: scope, Secure: r.TLS != nil})
	}

	switch order {
	case "":
		order = "asc"
		if orderCookie, orderErr := r.Cookie("order"); orderErr == nil {
			order = orderCookie.Value
		}
	case "asc", "desc":
		http.SetCookie(w, &http.Cookie{Name: "order", Value: order, Path: scope, Secure: r.TLS != nil})
	}

	if limitQuery != "" {
		limit, err = strconv.Atoi(limitQuery)
		if err != nil { // if the 'limit' query can't be interpreted as a number, return err
			return
		}
	}

	return
}

// ServeListing returns a formatted view of 'requestedFilepath' contents'.
func (b Browse) ServeListing(w http.ResponseWriter, r *http.Request, requestedFilepath http.File, bc *Config) (int, error) {
	listing, containsIndex, err := b.loadDirectoryContents(requestedFilepath, r.URL.Path, bc)
	if err != nil {
		switch {
		case os.IsPermission(err):
			return http.StatusForbidden, err
		case os.IsExist(err):
			return http.StatusGone, err
		default:
			return http.StatusInternalServerError, err
		}
	}
	if containsIndex && !b.IgnoreIndexes { // directory isn't browsable
		return b.Next.ServeHTTP(w, r)
	}
	listing.Context = httpserver.Context{
		Root: bc.Fs.Root,
		Req:  r,
		URL:  r.URL,
	}
	listing.User = bc.Variables

	// Copy the query values into the Listing struct
	var limit int
	listing.Sort, listing.Order, limit, err = b.handleSortOrder(w, r, bc.PathScope)
	if err != nil {
		return http.StatusBadRequest, err
	}

	listing.applySort()

	if limit > 0 && limit <= len(listing.Items) {
		listing.Items = listing.Items[:limit]
		listing.ItemsLimitedTo = limit
	}

	var buf *bytes.Buffer
	acceptHeader := strings.ToLower(strings.Join(r.Header["Accept"], ","))
	switch {
	case strings.Contains(acceptHeader, "application/json"):
		if buf, err = b.formatAsJSON(listing, bc); err != nil {
			return http.StatusInternalServerError, err
		}
		w.Header().Set("Content-Type", "application/json; charset=utf-8")

	default: // There's no 'application/json' in the 'Accept' header; browse normally
		if buf, err = b.formatAsHTML(listing, bc); err != nil {
			return http.StatusInternalServerError, err
		}
		w.Header().Set("Content-Type", "text/html; charset=utf-8")

	}

	buf.WriteTo(w)

	return http.StatusOK, nil
}

func (b Browse) formatAsJSON(listing *Listing, bc *Config) (*bytes.Buffer, error) {
	marsh, err := json.Marshal(listing.Items)
	if err != nil {
		return nil, err
	}

	buf := new(bytes.Buffer)
	_, err = buf.Write(marsh)
	return buf, err
}

func (b Browse) formatAsHTML(listing *Listing, bc *Config) (*bytes.Buffer, error) {
	buf := new(bytes.Buffer)
	err := bc.Template.Execute(buf, listing)
	return buf, err
}
<commit_msg>browse: when sorting by size, sort directory section by name<commit_after>// Package browse provides middleware for listing files in a directory
// when directory path is requested instead of a specific file.
package browse

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/url"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/mholt/caddy/caddyhttp/staticfiles"
)

const (
	sortByName = "name"
	sortBySize = "size"
	sortByTime = "time"
)

// Browse is an http.Handler that can show a file listing when
// directories in the given paths are specified.
type Browse struct {
	Next          httpserver.Handler
	Configs       []Config
	IgnoreIndexes bool
}

// Config is a configuration for browsing in a particular path.
type Config struct {
	PathScope string
	Fs        staticfiles.FileServer
	Variables interface{}
	Template  *template.Template
}

// A Listing is the context used to fill out a template.
type Listing struct {
	// The name of the directory (the last element of the path)
	Name string

	// The full path of the request
	Path string

	// Whether the parent directory is browsable
	CanGoUp bool

	// The items (files and folders) in the path
	Items []FileInfo

	// The number of directories in the listing
	NumDirs int

	// The number of files (items that aren't directories) in the listing
	NumFiles int

	// Which sorting order is used
	Sort string

	// And which order
	Order string

	// If ≠0 then Items have been limited to that many elements
	ItemsLimitedTo int

	// Optional custom variables for use in browse templates
	User interface{}

	httpserver.Context
}

// BreadcrumbMap returns l.Path where every element is a map
// of URLs and path segment names.
func (l Listing) BreadcrumbMap() map[string]string {
	result := map[string]string{}

	if len(l.Path) == 0 {
		return result
	}

	// skip trailing slash
	lpath := l.Path
	if lpath[len(lpath)-1] == '/' {
		lpath = lpath[:len(lpath)-1]
	}

	parts := strings.Split(lpath, "/")
	for i, part := range parts {
		if i == 0 && part == "" {
			// Leading slash (root)
			result["/"] = "/"
			continue
		}
		result[strings.Join(parts[:i+1], "/")] = part
	}

	return result
}

// FileInfo is the info about a particular file or directory
type FileInfo struct {
	Name    string
	Size    int64
	URL     string
	ModTime time.Time
	Mode    os.FileMode
	IsDir   bool
}

// HumanSize returns the size of the file as a human-readable string
// in IEC format (i.e. power of 2 or base 1024).
func (fi FileInfo) HumanSize() string {
	return humanize.IBytes(uint64(fi.Size))
}

// HumanModTime returns the modified time of the file as a human-readable string.
func (fi FileInfo) HumanModTime(format string) string {
	return fi.ModTime.Format(format)
}

// Implement sorting for Listing
type byName Listing
type bySize Listing
type byTime Listing

// By Name
func (l byName) Len() int      { return len(l.Items) }
func (l byName) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }

// Treat upper and lower case equally
func (l byName) Less(i, j int) bool {

	// if both are dir or file sort normally
	if l.Items[i].IsDir == l.Items[j].IsDir {
		return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
	} else {
		// always sort dir ahead of file
		return l.Items[i].IsDir
	}
}

// By Size
func (l bySize) Len() int      { return len(l.Items) }
func (l bySize) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }

const directoryOffset = -1 << 31 // = math.MinInt32
func (l bySize) Less(i, j int) bool {
	iSize, jSize := l.Items[i].Size, l.Items[j].Size

	// Directory sizes depend on the filesystem implementation,
	// which is opaque to a visitor, and should indeed does not change if the operator choses to change the fs.
	// For a consistent user experience directories are pulled to the front…
	if l.Items[i].IsDir {
		iSize = directoryOffset
	}
	if l.Items[j].IsDir {
		jSize = directoryOffset
	}
	// … and sorted by name.
	if l.Items[i].IsDir && l.Items[j].IsDir {
		return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
	}

	return iSize < jSize
}

// By Time
func (l byTime) Len() int           { return len(l.Items) }
func (l byTime) Swap(i, j int)      { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }
func (l byTime) Less(i, j int) bool { return l.Items[i].ModTime.Before(l.Items[j].ModTime) }

// Add sorting method to "Listing"
// it will apply what's in ".Sort" and ".Order"
func (l Listing) applySort() {
	// Check '.Order' to know how to sort
	if l.Order == "desc" {
		switch l.Sort {
		case sortByName:
			sort.Sort(sort.Reverse(byName(l)))
		case sortBySize:
			sort.Sort(sort.Reverse(bySize(l)))
		case sortByTime:
			sort.Sort(sort.Reverse(byTime(l)))
		default:
			// If not one of the above, do nothing
			return
		}
	} else { // If we had more Orderings we could add them here
		switch l.Sort {
		case sortByName:
			sort.Sort(byName(l))
		case sortBySize:
			sort.Sort(bySize(l))
		case sortByTime:
			sort.Sort(byTime(l))
		default:
			// If not one of the above, do nothing
			return
		}
	}
}

func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, config *Config) (Listing, bool) {
	var (
		fileinfos           []FileInfo
		dirCount, fileCount int
		hasIndexFile        bool
	)

	for _, f := range files {
		name := f.Name()

		for _, indexName := range staticfiles.IndexPages {
			if name == indexName {
				hasIndexFile = true
				break
			}
		}

		if f.IsDir() {
			name += "/"
			dirCount++
		} else {
			fileCount++
		}

		url := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name

		if config.Fs.IsHidden(f) {
			continue
		}

		fileinfos = append(fileinfos, FileInfo{
			IsDir:   f.IsDir(),
			Name:    f.Name(),
			Size:    f.Size(),
			URL:     url.String(),
			ModTime: f.ModTime().UTC(),
			Mode:    f.Mode(),
		})
	}

	return Listing{
		Name:     path.Base(urlPath),
		Path:     urlPath,
		CanGoUp:  canGoUp,
		Items:    fileinfos,
		NumDirs:  dirCount,
		NumFiles: fileCount,
	}, hasIndexFile
}

// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
// If so, control is handed over to ServeListing.
func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	// See if there's a browse configuration to match the path
	var bc *Config
	for i := range b.Configs {
		if httpserver.Path(r.URL.Path).Matches(b.Configs[i].PathScope) {
			bc = &b.Configs[i]
			break
		}
	}
	if bc == nil {
		return b.Next.ServeHTTP(w, r)
	}

	// Browse works on existing directories; delegate everything else
	requestedFilepath, err := bc.Fs.Root.Open(r.URL.Path)
	if err != nil {
		switch {
		case os.IsPermission(err):
			return http.StatusForbidden, err
		case os.IsExist(err):
			return http.StatusNotFound, err
		default:
			return b.Next.ServeHTTP(w, r)
		}
	}
	defer requestedFilepath.Close()

	info, err := requestedFilepath.Stat()
	if err != nil {
		switch {
		case os.IsPermission(err):
			return http.StatusForbidden, err
		case os.IsExist(err):
			return http.StatusGone, err
		default:
			return b.Next.ServeHTTP(w, r)
		}
	}
	if !info.IsDir() {
		return b.Next.ServeHTTP(w, r)
	}

	// Do not reply to anything else because it might be nonsensical
	switch r.Method {
	case http.MethodGet, http.MethodHead:
		// proceed, noop
	case "PROPFIND", http.MethodOptions:
		return http.StatusNotImplemented, nil
	default:
		return b.Next.ServeHTTP(w, r)
	}

	// Browsing navigation gets messed up if browsing a directory
	// that doesn't end in "/" (which it should, anyway)
	if !strings.HasSuffix(r.URL.Path, "/") {
		staticfiles.RedirectToDir(w, r)
		return 0, nil
	}

	return b.ServeListing(w, r, requestedFilepath, bc)
}

func (b Browse) loadDirectoryContents(requestedFilepath http.File, urlPath string, config *Config) (*Listing, bool, error) {
	files, err := requestedFilepath.Readdir(-1)
	if err != nil {
		return nil, false, err
	}

	// Determine if user can browse up another folder
	var canGoUp bool
	curPathDir := path.Dir(strings.TrimSuffix(urlPath, "/"))
	for _, other := range b.Configs {
		if strings.HasPrefix(curPathDir, other.PathScope) {
			canGoUp = true
			break
		}
	}

	// Assemble listing of directory contents
	listing, hasIndex := directoryListing(files, canGoUp, urlPath, config)

	return &listing, hasIndex, nil
}

// handleSortOrder gets and stores for a Listing the 'sort' and 'order',
// and reads 'limit' if given. The latter is 0 if not given.
//
// This sets Cookies.
func (b Browse) handleSortOrder(w http.ResponseWriter, r *http.Request, scope string) (sort string, order string, limit int, err error) {
	sort, order, limitQuery := r.URL.Query().Get("sort"), r.URL.Query().Get("order"), r.URL.Query().Get("limit")

	// If the query 'sort' or 'order' is empty, use defaults or any values previously saved in Cookies
	switch sort {
	case "":
		sort = sortByName
		if sortCookie, sortErr := r.Cookie("sort"); sortErr == nil {
			sort = sortCookie.Value
		}
	case sortByName, sortBySize, sortByTime:
		http.SetCookie(w, &http.Cookie{Name: "sort", Value: sort, Path: scope, Secure: r.TLS != nil})
	}

	switch order {
	case "":
		order = "asc"
		if orderCookie, orderErr := r.Cookie("order"); orderErr == nil {
			order = orderCookie.Value
		}
	case "asc", "desc":
		http.SetCookie(w, &http.Cookie{Name: "order", Value: order, Path: scope, Secure: r.TLS != nil})
	}

	if limitQuery != "" {
		limit, err = strconv.Atoi(limitQuery)
		if err != nil { // if the 'limit' query can't be interpreted as a number, return err
			return
		}
	}

	return
}

// ServeListing returns a formatted view of 'requestedFilepath' contents'.
func (b Browse) ServeListing(w http.ResponseWriter, r *http.Request, requestedFilepath http.File, bc *Config) (int, error) {
	listing, containsIndex, err := b.loadDirectoryContents(requestedFilepath, r.URL.Path, bc)
	if err != nil {
		switch {
		case os.IsPermission(err):
			return http.StatusForbidden, err
		case os.IsExist(err):
			return http.StatusGone, err
		default:
			return http.StatusInternalServerError, err
		}
	}
	if containsIndex && !b.IgnoreIndexes { // directory isn't browsable
		return b.Next.ServeHTTP(w, r)
	}
	listing.Context = httpserver.Context{
		Root: bc.Fs.Root,
		Req:  r,
		URL:  r.URL,
	}
	listing.User = bc.Variables

	// Copy the query values into the Listing struct
	var limit int
	listing.Sort, listing.Order, limit, err = b.handleSortOrder(w, r, bc.PathScope)
	if err != nil {
		return http.StatusBadRequest, err
	}

	listing.applySort()

	if limit > 0 && limit <= len(listing.Items) {
		listing.Items = listing.Items[:limit]
		listing.ItemsLimitedTo = limit
	}

	var buf *bytes.Buffer
	acceptHeader := strings.ToLower(strings.Join(r.Header["Accept"], ","))
	switch {
	case strings.Contains(acceptHeader, "application/json"):
		if buf, err = b.formatAsJSON(listing, bc); err != nil {
			return http.StatusInternalServerError, err
		}
		w.Header().Set("Content-Type", "application/json; charset=utf-8")

	default: // There's no 'application/json' in the 'Accept' header; browse normally
		if buf, err = b.formatAsHTML(listing, bc); err != nil {
			return http.StatusInternalServerError, err
		}
		w.Header().Set("Content-Type", "text/html; charset=utf-8")

	}

	buf.WriteTo(w)

	return http.StatusOK, nil
}

func (b Browse) formatAsJSON(listing *Listing, bc *Config) (*bytes.Buffer, error) {
	marsh, err := json.Marshal(listing.Items)
	if err != nil {
		return nil, err
	}

	buf := new(bytes.Buffer)
	_, err = buf.Write(marsh)
	return buf, err
}

func (b Browse) formatAsHTML(listing *Listing, bc *Config) (*bytes.Buffer, error) {
	buf := new(bytes.Buffer)
	err := bc.Template.Execute(buf, listing)
	return buf, err
}
<|endoftext|>


<commit_before>package utils

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

// Signer interface describes facility implementing signing of files
type Signer interface {
	Init() error
	SetKey(keyRef string)
	SetKeyRing(keyring, secretKeyring string)
	SetPassphrase(passphrase, passphraseFile string)
	SetBatch(batch bool)
	DetachedSign(source string, destination string) error
	ClearSign(source string, destination string) error
}

// Verifier interface describes signature verification factility
type Verifier interface {
	InitKeyring() error
	AddKeyring(keyring string)
	VerifyDetachedSignature(signature, cleartext io.Reader) error
	VerifyClearsigned(clearsigned io.Reader) error
	ExtractClearsigned(clearsigned io.Reader) (text *os.File, err error)
}

// Test interface
var (
	_ Signer   = &GpgSigner{}
	_ Verifier = &GpgVerifier{}
)

// GpgSigner is implementation of Signer interface using gpg
type GpgSigner struct {
	keyRef                     string
	keyring, secretKeyring     string
	passphrase, passphraseFile string
	batch                      bool
}

// SetBatch control --no-tty flag to gpg
func (g *GpgSigner) SetBatch(batch bool) {
	g.batch = batch
}

// SetKey sets key ID to use when signing files
func (g *GpgSigner) SetKey(keyRef string) {
	g.keyRef = keyRef
}

// SetKeyRing allows to set custom keyring and secretkeyring
func (g *GpgSigner) SetKeyRing(keyring, secretKeyring string) {
	g.keyring, g.secretKeyring = keyring, secretKeyring
}

// SetPassphrase sets passhprase params
func (g *GpgSigner) SetPassphrase(passphrase, passphraseFile string) {
	g.passphrase, g.passphraseFile = passphrase, passphraseFile
}

func (g *GpgSigner) gpgArgs() []string {
	args := []string{}
	if g.keyring != "" {
		args = append(args, "--no-auto-check-trustdb", "--no-default-keyring", "--keyring", g.keyring)
	}
	if g.secretKeyring != "" {
		args = append(args, "--secret-keyring", g.secretKeyring)
	}

	if g.keyRef != "" {
		args = append(args, "-u", g.keyRef)
	}

	if g.passphrase != "" {
		args = append(args, "--passphrase", g.passphrase)
	}

	if g.passphraseFile != "" {
		args = append(args, "--passphrase-file", g.passphraseFile)
	}
	if g.batch {
		args = append(args, "--no-tty")
	}

	return args
}

// Init verifies availability of gpg & presence of keys
func (g *GpgSigner) Init() error {
	output, err := exec.Command("gpg", "--list-keys", "--dry-run", "--no-auto-check-trustdb").CombinedOutput()
	if err != nil {
		return fmt.Errorf("unable to execute gpg: %s (is gpg installed?): %s", err, string(output))
	}

	if g.keyring == "" && g.secretKeyring == "" && len(output) == 0 {
		return fmt.Errorf("looks like there are no keys in gpg, please create one (official manual: http://www.gnupg.org/gph/en/manual.html)")
	}

	return err
}

// DetachedSign signs file with detached signature in ASCII format
func (g *GpgSigner) DetachedSign(source string, destination string) error {
	fmt.Printf("Signing file '%s' with gpg, please enter your passphrase when prompted:\n", filepath.Base(source))

	args := []string{"-o", destination, "--armor", "--yes"}
	args = append(args, g.gpgArgs()...)
	args = append(args, "--detach-sign", source)
	cmd := exec.Command("gpg", args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// ClearSign clear-signs the file
func (g *GpgSigner) ClearSign(source string, destination string) error {
	fmt.Printf("Clearsigning file '%s' with gpg, please enter your passphrase when prompted:\n", filepath.Base(source))
	args := []string{"-o", destination, "--yes"}
	args = append(args, g.gpgArgs()...)
	args = append(args, "--clearsign", source)
	cmd := exec.Command("gpg", args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// GpgVerifier is implementation of Verifier interface using gpgv
type GpgVerifier struct {
	keyRings []string
}

// InitKeyring verifies that gpg is installed and some keys are trusted
func (g *GpgVerifier) InitKeyring() error {
	err := exec.Command("gpgv", "--version").Run()
	if err != nil {
		return fmt.Errorf("unable to execute gpgv: %s (is gpg installed?)", err)
	}

	if len(g.keyRings) == 0 {
		// using default keyring
		output, err := exec.Command("gpg", "--no-default-keyring", "--no-auto-check-trustdb", "--keyring", "trustedkeys.gpg", "--list-keys").Output()
		if err == nil && len(output) == 0 {
			fmt.Printf("\nLooks like your keyring with trusted keys is empty. You might consider importing some keys.\n")
			fmt.Printf("If you're running Debian or Ubuntu, it's a good idea to import current archive keys by running:\n\n")
			fmt.Printf("  gpg --keyring /usr/share/keyrings/debian-archive-keyring.gpg --export | gpg --no-default-keyring --keyring trustedkeys.gpg --import\n")
			fmt.Printf("\n(for Ubuntu, use /usr/share/keyrings/ubuntu-archive-keyring.gpg)\n\n")
		}
	}

	return nil
}

// AddKeyring adds custom keyring to GPG parameters
func (g *GpgVerifier) AddKeyring(keyring string) {
	g.keyRings = append(g.keyRings, keyring)
}

func (g *GpgVerifier) argsKeyrings() (args []string) {
	if len(g.keyRings) > 0 {
		args = make([]string, 0, 2*len(g.keyRings))
		for _, keyring := range g.keyRings {
			args = append(args, "--keyring", keyring)
		}
	} else {
		args = []string{"--keyring", "trustedkeys.gpg"}
	}
	return
}

func (g *GpgVerifier) runGpgv(args []string, context string) error {
	cmd := exec.Command("gpgv", args...)

	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	defer stderr.Close()

	err = cmd.Start()
	if err != nil {
		return err
	}

	buffer := &bytes.Buffer{}

	_, err = io.Copy(io.MultiWriter(os.Stderr, buffer), stderr)
	if err != nil {
		return err
	}

	matches := regexp.MustCompile("ID ([0-9A-F]{8})").FindAllStringSubmatch(buffer.String(), -1)

	err = cmd.Wait()
	if err != nil {
		if len(g.keyRings) == 0 && len(matches) > 0 {
			fmt.Printf("\nLooks like some keys are missing in your trusted keyring, you may consider importing them from keyserver:\n\n")

			keyIDs := []string{}
			for _, match := range matches {
				keyIDs = append(keyIDs, match[1])
			}
			fmt.Printf("gpg --no-default-keyring --keyring trustedkeys.gpg --keyserver keys.gnupg.net --recv-keys %s\n\n",
				strings.Join(keyIDs, " "))

			fmt.Printf("Sometimes keys are stored in repository root in file named Release.key, to import such key:\n\n")
			fmt.Printf("wget -O - http://some.repo/repository/Release.key | gpg --no-default-keyring --keyring trustedkeys.gpg --import\n\n")
		}
		return fmt.Errorf("verification of %s failed: %s", context, err)
	}
	return nil
}

// VerifyDetachedSignature verifies combination of signature and cleartext using gpgv
func (g *GpgVerifier) VerifyDetachedSignature(signature, cleartext io.Reader) error {
	args := g.argsKeyrings()

	sigf, err := ioutil.TempFile("", "aptly-gpg")
	if err != nil {
		return err
	}
	defer os.Remove(sigf.Name())
	defer sigf.Close()

	_, err = io.Copy(sigf, signature)
	if err != nil {
		return err
	}

	clearf, err := ioutil.TempFile("", "aptly-gpg")
	if err != nil {
		return err
	}
	defer os.Remove(clearf.Name())
	defer clearf.Close()

	_, err = io.Copy(clearf, cleartext)
	if err != nil {
		return err
	}

	args = append(args, sigf.Name(), clearf.Name())
	return g.runGpgv(args, "detached signature")
}

// VerifyClearsigned verifies clearsigned file using gpgv
func (g *GpgVerifier) VerifyClearsigned(clearsigned io.Reader) error {
	args := g.argsKeyrings()

	clearf, err := ioutil.TempFile("", "aptly-gpg")
	if err != nil {
		return err
	}
	defer os.Remove(clearf.Name())
	defer clearf.Close()

	_, err = io.Copy(clearf, clearsigned)
	if err != nil {
		return err
	}

	args = append(args, clearf.Name())
	return g.runGpgv(args, "clearsigned file")
}

// ExtractClearsigned extracts cleartext from clearsigned file WITHOUT signature verification
func (g *GpgVerifier) ExtractClearsigned(clearsigned io.Reader) (text *os.File, err error) {
	clearf, err := ioutil.TempFile("", "aptly-gpg")
	if err != nil {
		return
	}
	defer os.Remove(clearf.Name())
	defer clearf.Close()

	_, err = io.Copy(clearf, clearsigned)
	if err != nil {
		return
	}

	text, err = ioutil.TempFile("", "aptly-gpg")
	if err != nil {
		return
	}
	defer os.Remove(text.Name())

	args := []string{"--no-auto-check-trustdb", "--decrypt", "--batch", "--skip-verify", "--output", "-", clearf.Name()}

	cmd := exec.Command("gpg", args...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	defer stdout.Close()

	err = cmd.Start()
	if err != nil {
		return nil, err
	}

	_, err = io.Copy(text, stdout)
	if err != nil {
		return nil, err
	}

	err = cmd.Wait()

	if err != nil {
		return nil, fmt.Errorf("extraction of clearsigned file failed: %s", err)
	}

	_, err = text.Seek(0, 0)
	if err != nil {
		return nil, err
	}

	return
}
<commit_msg>Pass --no-use-agent when running with --passphare flag. #162<commit_after>package utils

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

// Signer interface describes facility implementing signing of files
type Signer interface {
	Init() error
	SetKey(keyRef string)
	SetKeyRing(keyring, secretKeyring string)
	SetPassphrase(passphrase, passphraseFile string)
	SetBatch(batch bool)
	DetachedSign(source string, destination string) error
	ClearSign(source string, destination string) error
}

// Verifier interface describes signature verification factility
type Verifier interface {
	InitKeyring() error
	AddKeyring(keyring string)
	VerifyDetachedSignature(signature, cleartext io.Reader) error
	VerifyClearsigned(clearsigned io.Reader) error
	ExtractClearsigned(clearsigned io.Reader) (text *os.File, err error)
}

// Test interface
var (
	_ Signer   = &GpgSigner{}
	_ Verifier = &GpgVerifier{}
)

// GpgSigner is implementation of Signer interface using gpg
type GpgSigner struct {
	keyRef                     string
	keyring, secretKeyring     string
	passphrase, passphraseFile string
	batch                      bool
}

// SetBatch control --no-tty flag to gpg
func (g *GpgSigner) SetBatch(batch bool) {
	g.batch = batch
}

// SetKey sets key ID to use when signing files
func (g *GpgSigner) SetKey(keyRef string) {
	g.keyRef = keyRef
}

// SetKeyRing allows to set custom keyring and secretkeyring
func (g *GpgSigner) SetKeyRing(keyring, secretKeyring string) {
	g.keyring, g.secretKeyring = keyring, secretKeyring
}

// SetPassphrase sets passhprase params
func (g *GpgSigner) SetPassphrase(passphrase, passphraseFile string) {
	g.passphrase, g.passphraseFile = passphrase, passphraseFile
}

func (g *GpgSigner) gpgArgs() []string {
	args := []string{}
	if g.keyring != "" {
		args = append(args, "--no-auto-check-trustdb", "--no-default-keyring", "--keyring", g.keyring)
	}
	if g.secretKeyring != "" {
		args = append(args, "--secret-keyring", g.secretKeyring)
	}

	if g.keyRef != "" {
		args = append(args, "-u", g.keyRef)
	}

	if g.passphrase != "" || g.passphraseFile != "" {
		args = append(args, "--no-use-agent")
	}

	if g.passphrase != "" {
		args = append(args, "--passphrase", g.passphrase)
	}

	if g.passphraseFile != "" {
		args = append(args, "--passphrase-file", g.passphraseFile)
	}

	if g.batch {
		args = append(args, "--no-tty")
	}

	return args
}

// Init verifies availability of gpg & presence of keys
func (g *GpgSigner) Init() error {
	output, err := exec.Command("gpg", "--list-keys", "--dry-run", "--no-auto-check-trustdb").CombinedOutput()
	if err != nil {
		return fmt.Errorf("unable to execute gpg: %s (is gpg installed?): %s", err, string(output))
	}

	if g.keyring == "" && g.secretKeyring == "" && len(output) == 0 {
		return fmt.Errorf("looks like there are no keys in gpg, please create one (official manual: http://www.gnupg.org/gph/en/manual.html)")
	}

	return err
}

// DetachedSign signs file with detached signature in ASCII format
func (g *GpgSigner) DetachedSign(source string, destination string) error {
	fmt.Printf("Signing file '%s' with gpg, please enter your passphrase when prompted:\n", filepath.Base(source))

	args := []string{"-o", destination, "--armor", "--yes"}
	args = append(args, g.gpgArgs()...)
	args = append(args, "--detach-sign", source)
	cmd := exec.Command("gpg", args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// ClearSign clear-signs the file
func (g *GpgSigner) ClearSign(source string, destination string) error {
	fmt.Printf("Clearsigning file '%s' with gpg, please enter your passphrase when prompted:\n", filepath.Base(source))
	args := []string{"-o", destination, "--yes"}
	args = append(args, g.gpgArgs()...)
	args = append(args, "--clearsign", source)
	cmd := exec.Command("gpg", args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// GpgVerifier is implementation of Verifier interface using gpgv
type GpgVerifier struct {
	keyRings []string
}

// InitKeyring verifies that gpg is installed and some keys are trusted
func (g *GpgVerifier) InitKeyring() error {
	err := exec.Command("gpgv", "--version").Run()
	if err != nil {
		return fmt.Errorf("unable to execute gpgv: %s (is gpg installed?)", err)
	}

	if len(g.keyRings) == 0 {
		// using default keyring
		output, err := exec.Command("gpg", "--no-default-keyring", "--no-auto-check-trustdb", "--keyring", "trustedkeys.gpg", "--list-keys").Output()
		if err == nil && len(output) == 0 {
			fmt.Printf("\nLooks like your keyring with trusted keys is empty. You might consider importing some keys.\n")
			fmt.Printf("If you're running Debian or Ubuntu, it's a good idea to import current archive keys by running:\n\n")
			fmt.Printf("  gpg --keyring /usr/share/keyrings/debian-archive-keyring.gpg --export | gpg --no-default-keyring --keyring trustedkeys.gpg --import\n")
			fmt.Printf("\n(for Ubuntu, use /usr/share/keyrings/ubuntu-archive-keyring.gpg)\n\n")
		}
	}

	return nil
}

// AddKeyring adds custom keyring to GPG parameters
func (g *GpgVerifier) AddKeyring(keyring string) {
	g.keyRings = append(g.keyRings, keyring)
}

func (g *GpgVerifier) argsKeyrings() (args []string) {
	if len(g.keyRings) > 0 {
		args = make([]string, 0, 2*len(g.keyRings))
		for _, keyring := range g.keyRings {
			args = append(args, "--keyring", keyring)
		}
	} else {
		args = []string{"--keyring", "trustedkeys.gpg"}
	}
	return
}

func (g *GpgVerifier) runGpgv(args []string, context string) error {
	cmd := exec.Command("gpgv", args...)

	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	defer stderr.Close()

	err = cmd.Start()
	if err != nil {
		return err
	}

	buffer := &bytes.Buffer{}

	_, err = io.Copy(io.MultiWriter(os.Stderr, buffer), stderr)
	if err != nil {
		return err
	}

	matches := regexp.MustCompile("ID ([0-9A-F]{8})").FindAllStringSubmatch(buffer.String(), -1)

	err = cmd.Wait()
	if err != nil {
		if len(g.keyRings) == 0 && len(matches) > 0 {
			fmt.Printf("\nLooks like some keys are missing in your trusted keyring, you may consider importing them from keyserver:\n\n")

			keyIDs := []string{}
			for _, match := range matches {
				keyIDs = append(keyIDs, match[1])
			}
			fmt.Printf("gpg --no-default-keyring --keyring trustedkeys.gpg --keyserver keys.gnupg.net --recv-keys %s\n\n",
				strings.Join(keyIDs, " "))

			fmt.Printf("Sometimes keys are stored in repository root in file named Release.key, to import such key:\n\n")
			fmt.Printf("wget -O - http://some.repo/repository/Release.key | gpg --no-default-keyring --keyring trustedkeys.gpg --import\n\n")
		}
		return fmt.Errorf("verification of %s failed: %s", context, err)
	}
	return nil
}

// VerifyDetachedSignature verifies combination of signature and cleartext using gpgv
func (g *GpgVerifier) VerifyDetachedSignature(signature, cleartext io.Reader) error {
	args := g.argsKeyrings()

	sigf, err := ioutil.TempFile("", "aptly-gpg")
	if err != nil {
		return err
	}
	defer os.Remove(sigf.Name())
	defer sigf.Close()

	_, err = io.Copy(sigf, signature)
	if err != nil {
		return err
	}

	clearf, err := ioutil.TempFile("", "aptly-gpg")
	if err != nil {
		return err
	}
	defer os.Remove(clearf.Name())
	defer clearf.Close()

	_, err = io.Copy(clearf, cleartext)
	if err != nil {
		return err
	}

	args = append(args, sigf.Name(), clearf.Name())
	return g.runGpgv(args, "detached signature")
}

// VerifyClearsigned verifies clearsigned file using gpgv
func (g *GpgVerifier) VerifyClearsigned(clearsigned io.Reader) error {
	args := g.argsKeyrings()

	clearf, err := ioutil.TempFile("", "aptly-gpg")
	if err != nil {
		return err
	}
	defer os.Remove(clearf.Name())
	defer clearf.Close()

	_, err = io.Copy(clearf, clearsigned)
	if err != nil {
		return err
	}

	args = append(args, clearf.Name())
	return g.runGpgv(args, "clearsigned file")
}

// ExtractClearsigned extracts cleartext from clearsigned file WITHOUT signature verification
func (g *GpgVerifier) ExtractClearsigned(clearsigned io.Reader) (text *os.File, err error) {
	clearf, err := ioutil.TempFile("", "aptly-gpg")
	if err != nil {
		return
	}
	defer os.Remove(clearf.Name())
	defer clearf.Close()

	_, err = io.Copy(clearf, clearsigned)
	if err != nil {
		return
	}

	text, err = ioutil.TempFile("", "aptly-gpg")
	if err != nil {
		return
	}
	defer os.Remove(text.Name())

	args := []string{"--no-auto-check-trustdb", "--decrypt", "--batch", "--skip-verify", "--output", "-", clearf.Name()}

	cmd := exec.Command("gpg", args...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	defer stdout.Close()

	err = cmd.Start()
	if err != nil {
		return nil, err
	}

	_, err = io.Copy(text, stdout)
	if err != nil {
		return nil, err
	}

	err = cmd.Wait()

	if err != nil {
		return nil, fmt.Errorf("extraction of clearsigned file failed: %s", err)
	}

	_, err = text.Seek(0, 0)
	if err != nil {
		return nil, err
	}

	return
}
<|endoftext|>


<commit_before>package models

import (
	"gopkg.in/yaml.v2"
	"io/ioutil"
)

type Setting struct {
	Database struct {
		Host       string
		DbName     string
		TokenTable string
		UserTable  string
	}
}

var Set Setting

func LoadSettings() error {
	text, err := ioutil.ReadFile("./settings.yml")
	if err != nil {
		return err
	}

	if err = yaml.Unmarshal(text, &Set); err != nil {
		return err
	}

	return nil
}
<commit_msg>Add Ssl config. Add Router config.<commit_after>package models

import (
	"gopkg.in/yaml.v2"
	"io/ioutil"
)

type Setting struct {
	Database struct {
		Host       string
		DbName     string
		TokenTable string
		UserTable  string
	}
	Ssl struct {
		Key         string
		Sertificate string
	}
	Router struct {
		Register string
		Login    string
		Validate string
	}
}

var Set Setting

func LoadSettings() error {
	text, err := ioutil.ReadFile("./settings.yml")
	if err != nil {
		return err
	}

	if err = yaml.Unmarshal(text, &Set); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package utils

import (
	"bufio"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"
)

// Mfa object encapsulates the setup of MFA for API calls
type Mfa struct {
	UseMfa  bool
	MfaCode string
}

func (m *Mfa) parseMfaFlags(cmd *cobra.Command) error {
	flags := cmd.Flags()
	var err error
	m.UseMfa, err = flags.GetBool("mfa")
	if err != nil {
		return err
	}
	m.MfaCode, err = flags.GetString("mfacode")
	if err != nil {
		return err
	}
	return nil
}

func promptForMfa() (string, error) {
	mfaReader := bufio.NewReader(os.Stdin)
	fmt.Fprint(os.Stderr, "MFA Code: ")
	mfa, err := mfaReader.ReadString('\n')
	if err != nil {
		return "", err
	}
	mfa = strings.TrimRight(mfa, "\n")
	return mfa, nil
}

func (m *Mfa) configureSigninMfaParams(params *sts.GetSessionTokenInput) error {
	serialNumber, tokenCode, err := m.mfaParams()
	if err != nil {
		return err
	}
	params.SerialNumber = serialNumber
	params.TokenCode = tokenCode
}

func (m *Mfa) configureAssumptionMfaParams(params *sts.AssumeRoleInput) error {
	serialNumber, tokenCode, err := m.mfaParams()
	if err != nil {
		return err
	}
	params.SerialNumber = serialNumber
	params.TokenCode = tokenCode
}

func (m *Mfa) mfaParams() (string, string, error) {
	if !m.UseMfa && m.MfaCode == "" {
		return nil
	}

	serialNumber, err := API.MfaArn()
	if err != nil {
		return err
	}

	if m.MfaCode == "" {
		m.MfaCode, err = promptForMfa()
		if err != nil {
			return err
		}
	}
	return serialNumber, m.MfaCode, nil
}
<commit_msg>fix returns and typing<commit_after>package utils

import (
	"bufio"
	"fmt"
	"os"
	"strings"

	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/spf13/cobra"
)

// Mfa object encapsulates the setup of MFA for API calls
type Mfa struct {
	UseMfa  bool
	MfaCode string
}

func (m *Mfa) parseMfaFlags(cmd *cobra.Command) error {
	flags := cmd.Flags()
	var err error
	m.UseMfa, err = flags.GetBool("mfa")
	if err != nil {
		return err
	}
	m.MfaCode, err = flags.GetString("mfacode")
	if err != nil {
		return err
	}
	return nil
}

func promptForMfa() (string, error) {
	mfaReader := bufio.NewReader(os.Stdin)
	fmt.Fprint(os.Stderr, "MFA Code: ")
	mfa, err := mfaReader.ReadString('\n')
	if err != nil {
		return "", err
	}
	mfa = strings.TrimRight(mfa, "\n")
	return mfa, nil
}

func (m *Mfa) configureSigninMfaParams(params *sts.GetSessionTokenInput) error {
	serialNumber, tokenCode, err := m.mfaParams()
	if err != nil {
		return err
	}
	params.SerialNumber = &serialNumber
	params.TokenCode = &tokenCode
	return nil
}

func (m *Mfa) configureAssumptionMfaParams(params *sts.AssumeRoleInput) error {
	serialNumber, tokenCode, err := m.mfaParams()
	if err != nil {
		return err
	}
	params.SerialNumber = &serialNumber
	params.TokenCode = &tokenCode
	return nil
}

func (m *Mfa) mfaParams() (string, string, error) {
	if !m.UseMfa && m.MfaCode == "" {
		return "", "", nil
	}

	serialNumber, err := API.MfaArn()
	if err != nil {
		return "", "", err
	}

	if m.MfaCode == "" {
		m.MfaCode, err = promptForMfa()
		if err != nil {
			return "", "", err
		}
	}
	return serialNumber, m.MfaCode, nil
}
<|endoftext|>


<commit_before>package merry

import (
	"errors"
	"fmt"
	"github.com/ansel1/merry/v2/internal"
	"runtime"
)

// New creates a new error, with a stack attached.  The equivalent of golang's errors.New()
func New(msg string, wrappers ...Wrapper) error {
	return WrapSkipping(errors.New(msg), 1, wrappers...)
}

// Errorf creates a new error with a formatted message and a stack.  The equivalent of golang's fmt.Errorf().
// args may contain either arguments to format, or Wrapper options, which will be applied to the error.
func Errorf(format string, args ...interface{}) error {
	fmtArgs, wrappers := splitWrappers(args)

	return WrapSkipping(fmt.Errorf(format, fmtArgs...), 1, wrappers...)
}

// Sentinel creates an error without running hooks or capturing a stack.  It is intended
// to create sentinel errors, which will be wrapped with a stack later from where the
// error is returned.  At that time, a stack will be captured and hooks will be run.
//
//     var ErrNotFound = merry.Sentinel("not found", merry.WithHTTPCode(404))
//
//     func FindUser(name string) (*User, error) {
//       // some db code which fails to find a user
//       return nil, merry.Wrap(ErrNotFound)
//     }
//
//     func main() {
//       _, err := FindUser("bob")
//       fmt.Println(errors.Is(err, ErrNotFound) // "true"
//       fmt.Println(merry.Details(err))         // stacktrace will start at the return statement
//                                               // in FindUser()
//     }
func Sentinel(msg string, wrappers ...Wrapper) error {
	return apply(errors.New(msg), 1, false, false, wrappers...)
}

// Sentinelf is like Sentinel, but takes a formatted message.  args can be a mix of
// format arguments and Wrappers.
func Sentinelf(format string, args ...interface{}) error {
	fmtArgs, wrappers := splitWrappers(args)

	return apply(fmt.Errorf(format, fmtArgs...), 1, false, false, wrappers...)
}

func splitWrappers(args []interface{}) ([]interface{}, []Wrapper) {
	var wrappers []Wrapper

	// pull out the args which are wrappers
	n := 0
	for _, arg := range args {
		if w, ok := arg.(Wrapper); ok {
			wrappers = append(wrappers, w)
		} else {
			args[n] = arg
			n++
		}
	}
	args = args[:n]

	return args, wrappers
}

// Wrap adds context to errors by applying Wrappers.  See WithXXX() functions for Wrappers supplied
// by this package.
//
// If StackCaptureEnabled is true, a stack starting at the caller will be automatically captured
// and attached to the error.  This behavior can be overridden with wrappers which either capture
// their own stacks, or suppress auto capture.
//
// If err is nil, returns nil.
func Wrap(err error, wrappers ...Wrapper) error {
	return WrapSkipping(err, 1, wrappers...)
}

// WrapSkipping is like Wrap, but the captured stacks will start `skip` frames
// further up the call stack.  If skip is 0, it behaves the same as Wrap.
func WrapSkipping(err error, skip int, wrappers ...Wrapper) error {
	return apply(err, skip+1, true, true, wrappers...)
}

// apply wraps an error with wrappers, and optionally applies hooks and ensures
// the error has a stack.  This is a low-level API intended for granular control
// over how the error is processed.
//
// todo: consider making public
func apply(err error, skip int, applyHooks, autocapture bool, wrappers ...Wrapper) error {
	if err == nil {
		return nil
	}

	if applyHooks {
		for _, h := range hooks {
			err = h.Wrap(err, skip+1)
		}
	}

	for _, w := range wrappers {
		err = w.Wrap(err, skip+1)
	}

	if autocapture {
		err = captureStack(err, skip+1, false)
	}

	return err
}

// Prepend is a convenience function for the PrependMessage wrapper.  It eases migration
// from merry v1.  It accepts a varargs of additional Wrappers.
func Prepend(err error, msg string, wrappers ...Wrapper) error {
	return WrapSkipping(err, 1, append(wrappers, PrependMessage(msg))...)
}

// Prependf is a convenience function for the PrependMessagef wrapper.  It eases migration
// from merry v1.  The args can be format arguments mixed with Wrappers.
func Prependf(err error, format string, args ...interface{}) error {
	fmtArgs, wrappers := splitWrappers(args)

	return WrapSkipping(err, 1, append(wrappers, PrependMessagef(format, fmtArgs...))...)
}

// Append is a convenience function for the AppendMessage wrapper.  It eases migration
// from merry v1.  It accepts a varargs of additional Wrappers.
func Append(err error, msg string, wrappers ...Wrapper) error {
	return WrapSkipping(err, 1, append(wrappers, AppendMessage(msg))...)
}

// Appendf is a convenience function for the AppendMessagef wrapper.  It eases migration
// from merry v1.  The args can be format arguments mixed with Wrappers.
func Appendf(err error, format string, args ...interface{}) error {
	fmtArgs, wrappers := splitWrappers(args)

	return WrapSkipping(err, 1, append(wrappers, AppendMessagef(format, fmtArgs...))...)
}

// Value returns the value for key, or nil if not set.
// If e is nil, returns nil.  Will not search causes.
func Value(err error, key interface{}) interface{} {
	v, _ := Lookup(err, key)
	return v
}

// Lookup returns the value for the key, and a boolean indicating
// whether the value was set.  Will not search causes.
//
// if err is nil, returns nil and false.
func Lookup(err error, key interface{}) (interface{}, bool) {
	var merr interface {
		error
		isMerryError()
	}

	// I've tried implementing this logic a few different ways.  It's tricky:
	//
	// - Lookup should only search the current error, but not causes.  errWithCause's
	//   Unwrap() will eventually unwrap to the cause, so we don't want to just
	//   search the entire stream of errors returned by Unwrap.
	// - We need to handle cases where error implementations created outside
	//   this package are in the middle of the chain.  We need to use Unwrap
	//   in these cases to traverse those errors and dig down to the next
	//   merry error.
	// - Some error packages, including our own, do funky stuff with Unwrap(),
	//   returning shims types to control the unwrapping order, rather than
	//   the actual, raw wrapped error.  Typically, these shims implement
	//   Is/As to delegate to the raw error they encapsulate, but implement
	//   Unwrap by encapsulating the raw error in another shim.  So if we're looking
	//   for a raw error type, we can't just use Unwrap() and do type assertions
	//   against the result.  We have to use errors.As(), to allow the shims to delegate
	//   the type assertion to the raw error correctly.
	//
	// Based on all these constraints, we use errors.As() with an internal interface
	// that can only be implemented by our internal error types.  When one is found,
	// we handle each of our internal types as a special case.  For errWithCause, we
	// traverse to the wrapped error, ignoring the cause and the funky Unwrap logic.
	// We could have just used errors.As(err, *errWithValue), but that would have
	// traversed into the causes.

	for {
		switch t := err.(type) {
		case *errWithValue:
			if t.key == key {
				return t.value, true
			}
			err = t.err
		case *errWithCause:
			err = t.err
		default:
			if errors.As(err, &merr) {
				err = merr
			} else {
				return nil, false
			}
		}
	}
}

// Values returns a map of all values attached to the error
// If a key has been attached multiple times, the map will
// contain the last value mapped
// If e is nil, returns nil.
func Values(err error) map[interface{}]interface{} {
	var values map[interface{}]interface{}

	var merr interface {
		error
		isMerryError()
	}

	for {
		switch t := err.(type) {
		case *errWithValue:
			if _, ok := values[t.key]; !ok {
				if values == nil {
					values = map[interface{}]interface{}{}
				}
				values[t.key] = t.value
			}
			err = t.err
		case *errWithCause:
			err = t.err
		default:
			if errors.As(err, &merr) {
				err = merr
			} else {
				return values
			}
		}
	}

	for err != nil {
		if e, ok := err.(*errWithValue); ok {
			if _, ok := values[e.key]; !ok {
				if values == nil {
					values = map[interface{}]interface{}{}
				}
				values[e.key] = e.value
			}
		}
		err = internal.Unwrap(err)
	}

	return values
}

// Stack returns the stack attached to an error, or nil if one is not attached
// If e is nil, returns nil.
func Stack(err error) []uintptr {
	stack, _ := Value(err, errKeyStack).([]uintptr)
	return stack
}

// HTTPCode converts an error to an http status code.  All errors
// map to 500, unless the error has an http code attached.
// If e is nil, returns 200.
func HTTPCode(err error) int {
	if err == nil {
		return 200
	}

	code, _ := Value(err, errKeyHTTPCode).(int)
	if code == 0 {
		return 500
	}

	return code
}

// UserMessage returns the end-user safe message.  Returns empty if not set.
// If e is nil, returns "".
func UserMessage(err error) string {
	msg, _ := Value(err, errKeyUserMessage).(string)
	return msg
}

// Cause returns the cause of the argument.  If e is nil, or has no cause,
// nil is returned.
func Cause(err error) error {
	var causer *errWithCause
	if internal.As(err, &causer) {
		return causer.cause
	}
	return nil
}

// captureStack: return an error with a stack attached.  Stack will skip
// specified frames.  skip = 0 will start at caller.
// If the err already has a stack, to auto-stack-capture is disabled globally,
// this is a no-op.  Use force to override and force a stack capture
// in all cases.
func captureStack(err error, skip int, force bool) error {
	if err == nil {
		return nil
	}
	if !force && (!captureStacks || HasStack(err)) {
		return err
	}

	s := make([]uintptr, MaxStackDepth())
	length := runtime.Callers(2+skip, s[:])
	return Set(err, errKeyStack, s[:length])
}

// HasStack returns true if a stack is already attached to the err.
// If err == nil, returns false.
//
// If a stack capture was suppressed with NoCaptureStack(), this will
// still return true, indicating that stack capture processing has already
// occurred on this error.
func HasStack(err error) bool {
	_, ok := Lookup(err, errKeyStack)
	return ok
}
<commit_msg>fix for pre-1.13<commit_after>package merry

import (
	"errors"
	"fmt"
	"github.com/ansel1/merry/v2/internal"
	"runtime"
)

// New creates a new error, with a stack attached.  The equivalent of golang's errors.New()
func New(msg string, wrappers ...Wrapper) error {
	return WrapSkipping(errors.New(msg), 1, wrappers...)
}

// Errorf creates a new error with a formatted message and a stack.  The equivalent of golang's fmt.Errorf().
// args may contain either arguments to format, or Wrapper options, which will be applied to the error.
func Errorf(format string, args ...interface{}) error {
	fmtArgs, wrappers := splitWrappers(args)

	return WrapSkipping(fmt.Errorf(format, fmtArgs...), 1, wrappers...)
}

// Sentinel creates an error without running hooks or capturing a stack.  It is intended
// to create sentinel errors, which will be wrapped with a stack later from where the
// error is returned.  At that time, a stack will be captured and hooks will be run.
//
//     var ErrNotFound = merry.Sentinel("not found", merry.WithHTTPCode(404))
//
//     func FindUser(name string) (*User, error) {
//       // some db code which fails to find a user
//       return nil, merry.Wrap(ErrNotFound)
//     }
//
//     func main() {
//       _, err := FindUser("bob")
//       fmt.Println(errors.Is(err, ErrNotFound) // "true"
//       fmt.Println(merry.Details(err))         // stacktrace will start at the return statement
//                                               // in FindUser()
//     }
func Sentinel(msg string, wrappers ...Wrapper) error {
	return apply(errors.New(msg), 1, false, false, wrappers...)
}

// Sentinelf is like Sentinel, but takes a formatted message.  args can be a mix of
// format arguments and Wrappers.
func Sentinelf(format string, args ...interface{}) error {
	fmtArgs, wrappers := splitWrappers(args)

	return apply(fmt.Errorf(format, fmtArgs...), 1, false, false, wrappers...)
}

func splitWrappers(args []interface{}) ([]interface{}, []Wrapper) {
	var wrappers []Wrapper

	// pull out the args which are wrappers
	n := 0
	for _, arg := range args {
		if w, ok := arg.(Wrapper); ok {
			wrappers = append(wrappers, w)
		} else {
			args[n] = arg
			n++
		}
	}
	args = args[:n]

	return args, wrappers
}

// Wrap adds context to errors by applying Wrappers.  See WithXXX() functions for Wrappers supplied
// by this package.
//
// If StackCaptureEnabled is true, a stack starting at the caller will be automatically captured
// and attached to the error.  This behavior can be overridden with wrappers which either capture
// their own stacks, or suppress auto capture.
//
// If err is nil, returns nil.
func Wrap(err error, wrappers ...Wrapper) error {
	return WrapSkipping(err, 1, wrappers...)
}

// WrapSkipping is like Wrap, but the captured stacks will start `skip` frames
// further up the call stack.  If skip is 0, it behaves the same as Wrap.
func WrapSkipping(err error, skip int, wrappers ...Wrapper) error {
	return apply(err, skip+1, true, true, wrappers...)
}

// apply wraps an error with wrappers, and optionally applies hooks and ensures
// the error has a stack.  This is a low-level API intended for granular control
// over how the error is processed.
//
// todo: consider making public
func apply(err error, skip int, applyHooks, autocapture bool, wrappers ...Wrapper) error {
	if err == nil {
		return nil
	}

	if applyHooks {
		for _, h := range hooks {
			err = h.Wrap(err, skip+1)
		}
	}

	for _, w := range wrappers {
		err = w.Wrap(err, skip+1)
	}

	if autocapture {
		err = captureStack(err, skip+1, false)
	}

	return err
}

// Prepend is a convenience function for the PrependMessage wrapper.  It eases migration
// from merry v1.  It accepts a varargs of additional Wrappers.
func Prepend(err error, msg string, wrappers ...Wrapper) error {
	return WrapSkipping(err, 1, append(wrappers, PrependMessage(msg))...)
}

// Prependf is a convenience function for the PrependMessagef wrapper.  It eases migration
// from merry v1.  The args can be format arguments mixed with Wrappers.
func Prependf(err error, format string, args ...interface{}) error {
	fmtArgs, wrappers := splitWrappers(args)

	return WrapSkipping(err, 1, append(wrappers, PrependMessagef(format, fmtArgs...))...)
}

// Append is a convenience function for the AppendMessage wrapper.  It eases migration
// from merry v1.  It accepts a varargs of additional Wrappers.
func Append(err error, msg string, wrappers ...Wrapper) error {
	return WrapSkipping(err, 1, append(wrappers, AppendMessage(msg))...)
}

// Appendf is a convenience function for the AppendMessagef wrapper.  It eases migration
// from merry v1.  The args can be format arguments mixed with Wrappers.
func Appendf(err error, format string, args ...interface{}) error {
	fmtArgs, wrappers := splitWrappers(args)

	return WrapSkipping(err, 1, append(wrappers, AppendMessagef(format, fmtArgs...))...)
}

// Value returns the value for key, or nil if not set.
// If e is nil, returns nil.  Will not search causes.
func Value(err error, key interface{}) interface{} {
	v, _ := Lookup(err, key)
	return v
}

// Lookup returns the value for the key, and a boolean indicating
// whether the value was set.  Will not search causes.
//
// if err is nil, returns nil and false.
func Lookup(err error, key interface{}) (interface{}, bool) {
	var merr interface {
		error
		isMerryError()
	}

	// I've tried implementing this logic a few different ways.  It's tricky:
	//
	// - Lookup should only search the current error, but not causes.  errWithCause's
	//   Unwrap() will eventually unwrap to the cause, so we don't want to just
	//   search the entire stream of errors returned by Unwrap.
	// - We need to handle cases where error implementations created outside
	//   this package are in the middle of the chain.  We need to use Unwrap
	//   in these cases to traverse those errors and dig down to the next
	//   merry error.
	// - Some error packages, including our own, do funky stuff with Unwrap(),
	//   returning shims types to control the unwrapping order, rather than
	//   the actual, raw wrapped error.  Typically, these shims implement
	//   Is/As to delegate to the raw error they encapsulate, but implement
	//   Unwrap by encapsulating the raw error in another shim.  So if we're looking
	//   for a raw error type, we can't just use Unwrap() and do type assertions
	//   against the result.  We have to use errors.As(), to allow the shims to delegate
	//   the type assertion to the raw error correctly.
	//
	// Based on all these constraints, we use errors.As() with an internal interface
	// that can only be implemented by our internal error types.  When one is found,
	// we handle each of our internal types as a special case.  For errWithCause, we
	// traverse to the wrapped error, ignoring the cause and the funky Unwrap logic.
	// We could have just used errors.As(err, *errWithValue), but that would have
	// traversed into the causes.

	for {
		switch t := err.(type) {
		case *errWithValue:
			if t.key == key {
				return t.value, true
			}
			err = t.err
		case *errWithCause:
			err = t.err
		default:
			if internal.As(err, &merr) {
				err = merr
			} else {
				return nil, false
			}
		}
	}
}

// Values returns a map of all values attached to the error
// If a key has been attached multiple times, the map will
// contain the last value mapped
// If e is nil, returns nil.
func Values(err error) map[interface{}]interface{} {
	var values map[interface{}]interface{}

	var merr interface {
		error
		isMerryError()
	}

	for {
		switch t := err.(type) {
		case *errWithValue:
			if _, ok := values[t.key]; !ok {
				if values == nil {
					values = map[interface{}]interface{}{}
				}
				values[t.key] = t.value
			}
			err = t.err
		case *errWithCause:
			err = t.err
		default:
			if internal.As(err, &merr) {
				err = merr
			} else {
				return values
			}
		}
	}

	for err != nil {
		if e, ok := err.(*errWithValue); ok {
			if _, ok := values[e.key]; !ok {
				if values == nil {
					values = map[interface{}]interface{}{}
				}
				values[e.key] = e.value
			}
		}
		err = internal.Unwrap(err)
	}

	return values
}

// Stack returns the stack attached to an error, or nil if one is not attached
// If e is nil, returns nil.
func Stack(err error) []uintptr {
	stack, _ := Value(err, errKeyStack).([]uintptr)
	return stack
}

// HTTPCode converts an error to an http status code.  All errors
// map to 500, unless the error has an http code attached.
// If e is nil, returns 200.
func HTTPCode(err error) int {
	if err == nil {
		return 200
	}

	code, _ := Value(err, errKeyHTTPCode).(int)
	if code == 0 {
		return 500
	}

	return code
}

// UserMessage returns the end-user safe message.  Returns empty if not set.
// If e is nil, returns "".
func UserMessage(err error) string {
	msg, _ := Value(err, errKeyUserMessage).(string)
	return msg
}

// Cause returns the cause of the argument.  If e is nil, or has no cause,
// nil is returned.
func Cause(err error) error {
	var causer *errWithCause
	if internal.As(err, &causer) {
		return causer.cause
	}
	return nil
}

// captureStack: return an error with a stack attached.  Stack will skip
// specified frames.  skip = 0 will start at caller.
// If the err already has a stack, to auto-stack-capture is disabled globally,
// this is a no-op.  Use force to override and force a stack capture
// in all cases.
func captureStack(err error, skip int, force bool) error {
	if err == nil {
		return nil
	}
	if !force && (!captureStacks || HasStack(err)) {
		return err
	}

	s := make([]uintptr, MaxStackDepth())
	length := runtime.Callers(2+skip, s[:])
	return Set(err, errKeyStack, s[:length])
}

// HasStack returns true if a stack is already attached to the err.
// If err == nil, returns false.
//
// If a stack capture was suppressed with NoCaptureStack(), this will
// still return true, indicating that stack capture processing has already
// occurred on this error.
func HasStack(err error) bool {
	_, ok := Lookup(err, errKeyStack)
	return ok
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd Authors.

   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 v2

type Memory struct {
	Swap *int64
	Max  *int64
	Low  *int64
}

func (r *Memory) Values() (o []Value) {
	if r.Swap != nil {
		o = append(o, Value{
			filename: "memory.swap_max",
			value:    *r.Swap,
		})
	}
	if r.Max != nil {
		o = append(o, Value{
			filename: "memory.max",
			value:    *r.Max,
		})
	}
	if r.Low != nil {
		o = append(o, Value{
			filename: "memory.low",
			value:    *r.Low,
		})
	}
	return o
}
<commit_msg>Return memory `high` property<commit_after>/*
   Copyright The containerd Authors.

   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 v2

type Memory struct {
	Swap *int64
	Max  *int64
	Low  *int64
	High *int64
}

func (r *Memory) Values() (o []Value) {
	if r.Swap != nil {
		o = append(o, Value{
			filename: "memory.swap_max",
			value:    *r.Swap,
		})
	}
	if r.Max != nil {
		o = append(o, Value{
			filename: "memory.max",
			value:    *r.Max,
		})
	}
	if r.Low != nil {
		o = append(o, Value{
			filename: "memory.low",
			value:    *r.Low,
		})
	}
	if r.High != nil {
		o = append(o, Value{
			filename: "memory.high",
			value:    *r.High,
		})
	}
	return o
}
<|endoftext|>


<commit_before>package vago

import (
	"strings"
	"testing"
)

func TestOpenFail(t *testing.T) {
	_, err := Open("/nonexistent")
	if err == nil {
		t.Fatal("Expected nil")
	}
}

func TestOpenOK(t *testing.T) {
	v, err := Open("")
	defer v.Close()
	if err != nil {
		t.Fatal("Expected non nil")
	}
}

func TestLog(t *testing.T) {
	v, err := Open("")
	defer v.Close()
	if err != nil {
		t.Fatal(err)
	}
	v.Log("", RAW, func(vxid uint32, tag, _type, data string) int {
		if vxid == 0 && tag == "CLI" && _type == "-" && strings.Contains(data, "PONG") {
			return -1
		}
		return 0
	})
}

func TestStats(t *testing.T) {
	v, err := Open("")
	defer v.Close()
	if err != nil {
		t.Fatal(err)
	}
	items := v.Stats()
	if len(items) == 0 {
		t.Fatal("Expected map with elements")
	}
}

func TestStat(t *testing.T) {
	v, err := Open("")
	defer v.Close()
	if err != nil {
		t.Fatal(err)
	}
	uptime := v.Stat("MAIN.uptime")
	if uptime < 0 {
		t.Fatal("Expected value > 0")
	}
}
<commit_msg>Don't close the VSM too early<commit_after>package vago

import (
	"strings"
	"testing"
)

func TestOpenFail(t *testing.T) {
	_, err := Open("/nonexistent")
	if err == nil {
		t.Fatal("Expected nil")
	}
}

func TestOpenOK(t *testing.T) {
	v, err := Open("")
	if err != nil {
		t.Fatal("Expected non nil")
	}
	v.Close()
}

func TestLog(t *testing.T) {
	v, err := Open("")
	if err != nil {
		t.Fatal(err)
	}
	v.Log("", RAW, func(vxid uint32, tag, _type, data string) int {
		if vxid == 0 && tag == "CLI" && _type == "-" && strings.Contains(data, "PONG") {
			return -1
		}
		return 0
	})
	v.Close()
}

func TestStats(t *testing.T) {
	v, err := Open("")
	if err != nil {
		t.Fatal(err)
	}
	defer v.Close()
	items := v.Stats()
	if len(items) == 0 {
		t.Fatal("Expected map with elements")
	}
}

func TestStat(t *testing.T) {
	v, err := Open("")
	if err != nil {
		t.Fatal(err)
	}
	defer v.Close()
	uptime := v.Stat("MAIN.uptime")
	if uptime < 0 {
		t.Fatal("Expected value > 0")
	}
}
<|endoftext|>


<commit_before>/**
 * Package validator
 *
 * MISC:
 * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank
 *
 */

package validator

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"
	"unicode"
)

const (
	tagSeparator    = ","
	orSeparator     = "|"
	noValidationTag = "-"
	tagKeySeparator = "="
	structOnlyTag   = "structonly"
	omitempty       = "omitempty"
	fieldErrMsg     = "Field validation for \"%s\" failed on the \"%s\" tag"
	structErrMsg    = "Struct:%s\n"
)

// FieldError contains a single field's validation error along
// with other properties that may be needed for error message creation
type FieldError struct {
	Field string
	Tag   string
	Kind  reflect.Kind
	Type  reflect.Type
	Param string
	Value interface{}
}

// This is intended for use in development + debugging and not intended to be a production error message.
// it also allows FieldError to be used as an Error interface
func (e *FieldError) Error() string {
	return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag)
}

// StructErrors is hierarchical list of field and struct validation errors
// for a non hierarchical representation please see the Flatten method for StructErrors
type StructErrors struct {
	// Name of the Struct
	Struct string
	// Struct Field Errors
	Errors map[string]*FieldError
	// Struct Fields of type struct and their errors
	// key = Field Name of current struct, but internally Struct will be the actual struct name unless anonymous struct, it will be blank
	StructErrors map[string]*StructErrors
}

// This is intended for use in development + debugging and not intended to be a production error message.
// it also allows StructErrors to be used as an Error interface
func (e *StructErrors) Error() string {
	buff := bytes.NewBufferString(fmt.Sprintf(structErrMsg, e.Struct))

	for _, err := range e.Errors {
		buff.WriteString(err.Error())
		buff.WriteString("\n")
	}

	for _, err := range e.StructErrors {
		buff.WriteString(err.Error())
		buff.WriteString("\n\n")
	}

	return buff.String()
}

// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name
// for those that want/need it
func (e *StructErrors) Flatten() map[string]*FieldError {

	if e == nil {
		return nil
	}

	errs := map[string]*FieldError{}

	for _, f := range e.Errors {

		errs[f.Field] = f
	}

	for key, val := range e.StructErrors {

		otherErrs := val.Flatten()

		for _, f2 := range otherErrs {

			f2.Field = fmt.Sprintf("%s.%s", key, f2.Field)
			errs[f2.Field] = f2
		}
	}

	return errs
}

// Func accepts all values needed for file and cross field validation
// top     = top level struct when validating by struct otherwise nil
// current = current level struct when validating by struct otherwise optional comparison value
// f       = field value for validation
// param   = parameter used in validation i.e. gt=0 param would be 0
type Func func(top interface{}, current interface{}, f interface{}, param string) bool

// Validate implements the Validate Struct
// NOTE: Fields within are not thread safe and that is on purpose
// Functions and Tags should all be predifined before use, so subscribe to the philosiphy
// or make it thread safe on your end
type Validate struct {
	// tagName being used.
	tagName string
	// validateFuncs is a map of validation functions and the tag keys
	validationFuncs map[string]Func
}

// New creates a new Validate instance for use.
func New(tagName string, funcs map[string]Func) *Validate {
	return &Validate{
		tagName:         tagName,
		validationFuncs: funcs,
	}
}

// SetTag sets tagName of the Validator to one of your choosing after creation
// perhaps to dodge a tag name conflict in a specific section of code
func (v *Validate) SetTag(tagName string) {
	v.tagName = tagName
}

// AddFunction adds a validation Func to a Validate's map of validators denoted by the key
// NOTE: if the key already exists, it will get replaced.
func (v *Validate) AddFunction(key string, f Func) error {

	if len(key) == 0 {
		return errors.New("Function Key cannot be empty")
	}

	if f == nil {
		return errors.New("Function cannot be empty")
	}

	v.validationFuncs[key] = f

	return nil
}

// Struct validates a struct, even it's nested structs, and returns a struct containing the errors
// NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good
// way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to
// the Array or Map.
func (v *Validate) Struct(s interface{}) *StructErrors {

	return v.structRecursive(s, s, s)
}

// structRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors
func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}) *StructErrors {

	structValue := reflect.ValueOf(s)
	structType := reflect.TypeOf(s)
	structName := structType.Name()

	if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
		return v.structRecursive(top, current, structValue.Elem().Interface())
	}

	if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
		panic("interface passed for validation is not a struct")
	}

	validationErrors := &StructErrors{
		Struct:       structName,
		Errors:       map[string]*FieldError{},
		StructErrors: map[string]*StructErrors{},
	}

	var numFields = structValue.NumField()

	for i := 0; i < numFields; i++ {

		valueField := structValue.Field(i)
		typeField := structType.Field(i)

		if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
			valueField = valueField.Elem()
		}

		tag := typeField.Tag.Get(v.tagName)

		if tag == noValidationTag {
			continue
		}

		// if no validation and not a struct (which may containt fields for validation)
		if tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) {
			continue
		}

		switch valueField.Kind() {

		case reflect.Struct, reflect.Interface:

			if !unicode.IsUpper(rune(typeField.Name[0])) {
				continue
			}

			if valueField.Type() == reflect.TypeOf(time.Time{}) {

				if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), typeField.Name, tag); fieldError != nil {
					validationErrors.Errors[fieldError.Field] = fieldError
					// free up memory reference
					fieldError = nil
				}

			} else {

				if strings.Contains(tag, structOnlyTag) {
					continue
				}

				if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
					validationErrors.StructErrors[typeField.Name] = structErrors
					// free up memory map no longer needed
					structErrors = nil
				}
			}

		default:

			if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), typeField.Name, tag); fieldError != nil {
				validationErrors.Errors[fieldError.Field] = fieldError
				// free up memory reference
				fieldError = nil
			}
		}
	}

	if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
		return nil
	}

	return validationErrors
}

// Field allows validation of a single field, still using tag style validation to check multiple errors
func (v *Validate) Field(f interface{}, tag string) *FieldError {

	return v.FieldWithValue(nil, f, tag)
}

// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError {

	return v.fieldWithNameAndValue(nil, val, f, "", tag)
}

func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, name string, tag string) *FieldError {

	// This is a double check if coming from validate.Struct but need to be here in case function is called directly
	if tag == noValidationTag {
		return nil
	}

	if strings.Contains(tag, omitempty) && !hasValue(val, current, f, "") {
		return nil
	}

	valueField := reflect.ValueOf(f)
	fieldKind := valueField.Kind()

	if fieldKind == reflect.Ptr && !valueField.IsNil() {
		return v.fieldWithNameAndValue(val, current, valueField.Elem().Interface(), name, tag)
	}

	fieldType := valueField.Type()

	switch fieldKind {

	case reflect.Struct, reflect.Interface, reflect.Invalid:

		if fieldType != reflect.TypeOf(time.Time{}) {
			panic("Invalid field passed to ValidateFieldWithTag")
		}
	}

	var valErr *FieldError
	var err error
	valTags := strings.Split(tag, tagSeparator)

	for _, valTag := range valTags {

		orVals := strings.Split(valTag, orSeparator)

		if len(orVals) > 1 {

			errTag := ""

			for _, val := range orVals {

				valErr, err = v.fieldWithNameAndSingleTag(val, current, f, name, val)

				if err == nil {
					return nil
				}

				errTag += orSeparator + valErr.Tag

			}

			errTag = strings.TrimLeft(errTag, orSeparator)

			valErr.Tag = errTag
			valErr.Kind = fieldKind

			return valErr
		}

		if valErr, err = v.fieldWithNameAndSingleTag(val, current, f, name, valTag); err != nil {

			valErr.Kind = valueField.Kind()
			valErr.Type = fieldType

			return valErr
		}
	}

	return nil
}

func (v *Validate) fieldWithNameAndSingleTag(val interface{}, current interface{}, f interface{}, name string, valTag string) (*FieldError, error) {

	vals := strings.Split(valTag, tagKeySeparator)
	key := strings.Trim(vals[0], " ")

	if len(key) == 0 {
		panic(fmt.Sprintf("Invalid validation tag on field %s", name))
	}

	valErr := &FieldError{
		Field: name,
		Tag:   key,
		Value: f,
		Param: "",
	}

	// OK to continue because we checked it's existance before getting into this loop
	if key == omitempty {
		return valErr, nil
	}

	valFunc, ok := v.validationFuncs[key]
	if !ok {
		panic(fmt.Sprintf("Undefined validation function on field %s", name))
	}

	param := ""
	if len(vals) > 1 {
		param = strings.Trim(vals[1], " ")
	}

	if err := valFunc(val, current, f, param); !err {
		valErr.Param = param
		return valErr, errors.New(key)
	}

	return valErr, nil
}
<commit_msg>remove extra carriage returns<commit_after>/**
 * Package validator
 *
 * MISC:
 * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank
 *
 */

package validator

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"
	"unicode"
)

const (
	tagSeparator    = ","
	orSeparator     = "|"
	noValidationTag = "-"
	tagKeySeparator = "="
	structOnlyTag   = "structonly"
	omitempty       = "omitempty"
	fieldErrMsg     = "Field validation for \"%s\" failed on the \"%s\" tag"
	structErrMsg    = "Struct:%s\n"
)

// FieldError contains a single field's validation error along
// with other properties that may be needed for error message creation
type FieldError struct {
	Field string
	Tag   string
	Kind  reflect.Kind
	Type  reflect.Type
	Param string
	Value interface{}
}

// This is intended for use in development + debugging and not intended to be a production error message.
// it also allows FieldError to be used as an Error interface
func (e *FieldError) Error() string {
	return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag)
}

// StructErrors is hierarchical list of field and struct validation errors
// for a non hierarchical representation please see the Flatten method for StructErrors
type StructErrors struct {
	// Name of the Struct
	Struct string
	// Struct Field Errors
	Errors map[string]*FieldError
	// Struct Fields of type struct and their errors
	// key = Field Name of current struct, but internally Struct will be the actual struct name unless anonymous struct, it will be blank
	StructErrors map[string]*StructErrors
}

// This is intended for use in development + debugging and not intended to be a production error message.
// it also allows StructErrors to be used as an Error interface
func (e *StructErrors) Error() string {
	buff := bytes.NewBufferString(fmt.Sprintf(structErrMsg, e.Struct))

	for _, err := range e.Errors {
		buff.WriteString(err.Error())
		buff.WriteString("\n")
	}

	for _, err := range e.StructErrors {
		buff.WriteString(err.Error())
	}

	return buff.String()
}

// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name
// for those that want/need it
func (e *StructErrors) Flatten() map[string]*FieldError {

	if e == nil {
		return nil
	}

	errs := map[string]*FieldError{}

	for _, f := range e.Errors {

		errs[f.Field] = f
	}

	for key, val := range e.StructErrors {

		otherErrs := val.Flatten()

		for _, f2 := range otherErrs {

			f2.Field = fmt.Sprintf("%s.%s", key, f2.Field)
			errs[f2.Field] = f2
		}
	}

	return errs
}

// Func accepts all values needed for file and cross field validation
// top     = top level struct when validating by struct otherwise nil
// current = current level struct when validating by struct otherwise optional comparison value
// f       = field value for validation
// param   = parameter used in validation i.e. gt=0 param would be 0
type Func func(top interface{}, current interface{}, f interface{}, param string) bool

// Validate implements the Validate Struct
// NOTE: Fields within are not thread safe and that is on purpose
// Functions and Tags should all be predifined before use, so subscribe to the philosiphy
// or make it thread safe on your end
type Validate struct {
	// tagName being used.
	tagName string
	// validateFuncs is a map of validation functions and the tag keys
	validationFuncs map[string]Func
}

// New creates a new Validate instance for use.
func New(tagName string, funcs map[string]Func) *Validate {
	return &Validate{
		tagName:         tagName,
		validationFuncs: funcs,
	}
}

// SetTag sets tagName of the Validator to one of your choosing after creation
// perhaps to dodge a tag name conflict in a specific section of code
func (v *Validate) SetTag(tagName string) {
	v.tagName = tagName
}

// AddFunction adds a validation Func to a Validate's map of validators denoted by the key
// NOTE: if the key already exists, it will get replaced.
func (v *Validate) AddFunction(key string, f Func) error {

	if len(key) == 0 {
		return errors.New("Function Key cannot be empty")
	}

	if f == nil {
		return errors.New("Function cannot be empty")
	}

	v.validationFuncs[key] = f

	return nil
}

// Struct validates a struct, even it's nested structs, and returns a struct containing the errors
// NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good
// way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to
// the Array or Map.
func (v *Validate) Struct(s interface{}) *StructErrors {

	return v.structRecursive(s, s, s)
}

// structRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors
func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}) *StructErrors {

	structValue := reflect.ValueOf(s)
	structType := reflect.TypeOf(s)
	structName := structType.Name()

	if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
		return v.structRecursive(top, current, structValue.Elem().Interface())
	}

	if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
		panic("interface passed for validation is not a struct")
	}

	validationErrors := &StructErrors{
		Struct:       structName,
		Errors:       map[string]*FieldError{},
		StructErrors: map[string]*StructErrors{},
	}

	var numFields = structValue.NumField()

	for i := 0; i < numFields; i++ {

		valueField := structValue.Field(i)
		typeField := structType.Field(i)

		if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
			valueField = valueField.Elem()
		}

		tag := typeField.Tag.Get(v.tagName)

		if tag == noValidationTag {
			continue
		}

		// if no validation and not a struct (which may containt fields for validation)
		if tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) {
			continue
		}

		switch valueField.Kind() {

		case reflect.Struct, reflect.Interface:

			if !unicode.IsUpper(rune(typeField.Name[0])) {
				continue
			}

			if valueField.Type() == reflect.TypeOf(time.Time{}) {

				if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), typeField.Name, tag); fieldError != nil {
					validationErrors.Errors[fieldError.Field] = fieldError
					// free up memory reference
					fieldError = nil
				}

			} else {

				if strings.Contains(tag, structOnlyTag) {
					continue
				}

				if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
					validationErrors.StructErrors[typeField.Name] = structErrors
					// free up memory map no longer needed
					structErrors = nil
				}
			}

		default:

			if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), typeField.Name, tag); fieldError != nil {
				validationErrors.Errors[fieldError.Field] = fieldError
				// free up memory reference
				fieldError = nil
			}
		}
	}

	if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
		return nil
	}

	return validationErrors
}

// Field allows validation of a single field, still using tag style validation to check multiple errors
func (v *Validate) Field(f interface{}, tag string) *FieldError {

	return v.FieldWithValue(nil, f, tag)
}

// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError {

	return v.fieldWithNameAndValue(nil, val, f, "", tag)
}

func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, name string, tag string) *FieldError {

	// This is a double check if coming from validate.Struct but need to be here in case function is called directly
	if tag == noValidationTag {
		return nil
	}

	if strings.Contains(tag, omitempty) && !hasValue(val, current, f, "") {
		return nil
	}

	valueField := reflect.ValueOf(f)
	fieldKind := valueField.Kind()

	if fieldKind == reflect.Ptr && !valueField.IsNil() {
		return v.fieldWithNameAndValue(val, current, valueField.Elem().Interface(), name, tag)
	}

	fieldType := valueField.Type()

	switch fieldKind {

	case reflect.Struct, reflect.Interface, reflect.Invalid:

		if fieldType != reflect.TypeOf(time.Time{}) {
			panic("Invalid field passed to ValidateFieldWithTag")
		}
	}

	var valErr *FieldError
	var err error
	valTags := strings.Split(tag, tagSeparator)

	for _, valTag := range valTags {

		orVals := strings.Split(valTag, orSeparator)

		if len(orVals) > 1 {

			errTag := ""

			for _, val := range orVals {

				valErr, err = v.fieldWithNameAndSingleTag(val, current, f, name, val)

				if err == nil {
					return nil
				}

				errTag += orSeparator + valErr.Tag

			}

			errTag = strings.TrimLeft(errTag, orSeparator)

			valErr.Tag = errTag
			valErr.Kind = fieldKind

			return valErr
		}

		if valErr, err = v.fieldWithNameAndSingleTag(val, current, f, name, valTag); err != nil {

			valErr.Kind = valueField.Kind()
			valErr.Type = fieldType

			return valErr
		}
	}

	return nil
}

func (v *Validate) fieldWithNameAndSingleTag(val interface{}, current interface{}, f interface{}, name string, valTag string) (*FieldError, error) {

	vals := strings.Split(valTag, tagKeySeparator)
	key := strings.Trim(vals[0], " ")

	if len(key) == 0 {
		panic(fmt.Sprintf("Invalid validation tag on field %s", name))
	}

	valErr := &FieldError{
		Field: name,
		Tag:   key,
		Value: f,
		Param: "",
	}

	// OK to continue because we checked it's existance before getting into this loop
	if key == omitempty {
		return valErr, nil
	}

	valFunc, ok := v.validationFuncs[key]
	if !ok {
		panic(fmt.Sprintf("Undefined validation function on field %s", name))
	}

	param := ""
	if len(vals) > 1 {
		param = strings.Trim(vals[1], " ")
	}

	if err := valFunc(val, current, f, param); !err {
		valErr.Param = param
		return valErr, errors.New(key)
	}

	return valErr, nil
}
<|endoftext|>