<commit_before>package uman

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"golang.org/x/crypto/bcrypt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"
)

type Session struct {
	User      string
	Timestamp int64
	Lifespan  int64
	Cookie    string
}

type UserManager struct {
	Users        map[string][]byte
	DatabasePath string
	Sessions     map[string]*Session
	Request      *http.Request
	Writer       http.ResponseWriter
	Cookies      bool
	Debugging    bool
}

const lifespan int64 = 3600

/**
 * Constructor of UserManager.
 *
 **/
func New(databasePath string) *UserManager {
	um := &UserManager{
		DatabasePath: databasePath,
		Sessions:     make(map[string]*Session),
		Debugging:    true,
		Request:      nil,
		Writer:       nil,
		Cookies:      false,
	}

	um.Reload()
	return um
}

/**
 * Handles checking for debug mode and if so prints text.
 *
 **/
func (um *UserManager) Debug(message string) {
	if um.Debugging {
		fmt.Println("[UserManager]: " + message)
	}
}

/**
 * Hashes any given input using bcrypt.
 *
 **/
func (um *UserManager) Hash(this []byte) []byte {
	// cost: minimum is 4, max is 31, default is 10
	// (https://godoc.org/golang.org/x/crypto/bcrypt)
	cost := 10

	hash, err := bcrypt.GenerateFromPassword(this, cost)
	um.Check(err)

	return hash
}

/**
 * Checks a hash against its possible plaintext. This exists because of
 * bcrypt's mechanism, we shouldn't just um.Hash() and check it ourselves.
 *
 **/
func (um *UserManager) CheckHash(hash []byte, original []byte) bool {
	if bcrypt.CompareHashAndPassword(hash, original) != nil {
		return false
	}

	return true
}

/**
 * Internal UserManager error handling. Specifically, if a path error occurs
 * that means we just need to create the database. Furthermore in debug mode
 * a stdout message pops before panic()'ing, due to a panic possibility occuring
 * out of the blue.
 *
 **/
func (um *UserManager) Check(err error) {
	if err != nil {
		if _, ok := err.(*os.PathError); ok {
			um.Debug("Path error occured, creating database now.")
			os.Create(um.DatabasePath)
		} else {
			um.Debug(err.Error() + "\nPanicking.")
			panic(err)
		}
	}
}

/**
 * Reloads the database into memory, filling the appropriate variables.
 *
 **/
func (um *UserManager) Reload() {
	um.Users = make(map[string][]byte) // reset

	data, err := ioutil.ReadFile(um.DatabasePath)
	um.Check(err)

	accounts := strings.Split(string(data), "\n")
	for _, acc := range accounts {
		creds := strings.Split(acc, ":")

		if len(creds) != 2 {
			// at times for unknown reasons the length of creds is 1
			// which causes an unchecked panic to pop. it shouldn't mind us.
			break
		}

		um.Users[creds[0]] = []byte(creds[1])
	}
}

/**
 * Registers a new User, writing both into the database file and the memory.
 *
 **/
func (um *UserManager) Register(user string, pass string) bool {
	if _, exists := um.Users[user]; exists {
		return false
	}

	um.Users[user] = um.Hash([]byte(pass))
	pass = string(um.Users[user])

	f, err := os.OpenFile(um.DatabasePath, os.O_APPEND|os.O_WRONLY, 0666)
	defer f.Close()
	um.Check(err)

	_, err = f.WriteString(user + ":" + pass + "\n")
	um.Check(err)

	um.Debug("Registered user[" + user + "] with password[" + pass + "].")
	return true
}

/**
 * Changes the password of a User, given his old password matches the oldpass input variable.
 * Writes both into the database file and the memory.
 *
 * Returns false if the old password given doesn't match the actual old password, or User
 * doesn't exist.
 *
 **/
func (um *UserManager) ChangePass(User string, oldpass string, newpass string) bool {
	if um.CheckHash(um.Users[User], []byte(oldpass)) {
		oldpass := string(um.Users[User])
		um.Users[User] = um.Hash([]byte(newpass))
		newpass = string(um.Users[User])

		data, err := ioutil.ReadFile(um.DatabasePath)
		um.Check(err)

		lines := strings.Split(string(data), "\n")
		for i, line := range lines {
			if strings.Contains(line, User) && strings.Contains(line, oldpass) {
				lines[i] = User + ":" + newpass
				break
			}
		}

		output := strings.Join(lines, "\n")
		err = ioutil.WriteFile(um.DatabasePath, []byte(output), 0666)
		um.Check(err)

		um.Debug("Changed the password of User[" + User + "],\n\t" +
			"from[" + oldpass + "] to[" + newpass + "].")
		return true
	}

	um.Debug("Failed to change the password of User[" + User + "].")
	return false
}

/**
 * Changes the lifespan of a Session.
 * Mainly exists to handle the typecasting between int and int64.
 *
 **/
func (sess *Session) SetLifespan(seconds int) {
	sess.Lifespan = int64(seconds)
}

/**
 * Checks if a Session is logged.
 *
 **/
func (sess *Session) IsLogged() bool {
	if sess.User != "" {
		return true
	}

	return false
}

/**
 * Checks for expired Sessions.
 *
 **/
func (um *UserManager) CheckSessions() {
	for hash, sess := range um.Sessions {
		if sess.Timestamp+sess.Lifespan < time.Now().Unix() {
			um.Debug("Session[" + hash + "] has expired.")
			delete(um.Sessions, hash)
		}
	}
}

/**
 * Uses SHA256 to hash a Session token (the sum of identifiers).
 *
 **/
func (um *UserManager) HashSessionToken() string {
	hash := sha256.New()
	hash.Write([]byte(um.Request.UserAgent() + um.Request.RemoteAddr))

	return hex.EncodeToString(hash.Sum(nil))
}

/**
 * Generates a unique cookie hash (it keeps trying if not).
 *
 **/
func (um *UserManager) GenerateCookieHash() string {
	randBytes := make([]byte, 32)
	_, err := rand.Read(randBytes)
	um.Check(err)

    hash := sha256.New()
    hash.Write(randBytes)

    result := hex.EncodeToString(hash.Sum(nil))
    for _, sess := range um.Sessions {
    	if sess.Cookie == result {
    		return um.GenerateCookieHash()
    	}
    }

    return result
}

/**
 * Sets the HTTP cookie given the Session.
 *
 **/
func (um *UserManager) SetHTTPCookie(sess *Session) {
	http.SetCookie(um.Writer, &http.Cookie{
		Name:     "session",
		Value:    sess.Cookie,
		HttpOnly: true,
		Expires:  time.Unix(sess.Timestamp + sess.Lifespan, 0),
		MaxAge:   int(sess.Lifespan),
	})
}

/**
 * Attempts to find an existing Session. If it exists, it validates the given cookie.
 * If vaildation fails it's like the Session never existed in the first place, having
 * a fresh one taking its place.
 *
 **/
func (um *UserManager) GetSession() *Session {
	hash := um.HashSessionToken()

	if sess, exists := um.Sessions[hash]; exists {
		if um.Cookies {
			userCookie, err := um.Request.Cookie("session")

			if err == nil && userCookie.Value == sess.Cookie {
				return sess
			}
		} else {
			return sess
		}
	}

	um.Sessions[hash] = new(Session)
	sess := um.Sessions[hash]
	sess.User = ""
	sess.Lifespan = lifespan
	sess.Timestamp = time.Now().Unix()

	if um.Cookies {
		sess.Cookie = um.GenerateCookieHash()
		um.SetHTTPCookie(sess)
	}

	return sess
}

/**
 * Pretty simple.
 *
 **/
func (um *UserManager) Login(user string, pass string, sess *Session) bool {
	if um.CheckHash(um.Users[user], []byte(pass)) {
		sess.User = user

		um.Debug("User[" + user + "] has logged in.")
		return true
	}

	um.Debug("User[" + user + "] failed to log in.")
	return false
}

/**
 * Logs out the Session.
 *
 **/
func (um *UserManager) Logout(sess *Session) {
	um.Debug("Logging out user[" + sess.User + "].")
	sess.User = ""
}<commit_msg>Fixed abstract mode<commit_after>package uman

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"golang.org/x/crypto/bcrypt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"
)

type Session struct {
	User      string
	Timestamp int64
	Lifespan  int64
	Cookie    string
}

type UserManager struct {
	Users        map[string][]byte
	DatabasePath string
	Sessions     map[string]*Session
	Request      *http.Request
	Writer       http.ResponseWriter
	Debugging    bool
}

const lifespan int64 = 3600

/**
 * Constructor of UserManager.
 *
 **/
func New(databasePath string) *UserManager {
	um := &UserManager{
		DatabasePath: databasePath,
		Sessions:     make(map[string]*Session),
		Debugging:    true,
		Request:      nil,
		Writer:       nil,
	}

	um.Reload()
	return um
}

/**
 * Constructor of Session.
 *
 **/
func CreateSession() *Session {
	return &Session{
		User:      "",
		Lifespan:  lifespan,
		Timestamp: time.Now().Unix(),
	}
}

/**
 * Handles checking for debug mode and if so prints text.
 *
 **/
func (um *UserManager) Debug(message string) {
	if um.Debugging {
		fmt.Println("[UserManager]: " + message)
	}
}

/**
 * Hashes any given input using bcrypt.
 *
 **/
func (um *UserManager) Hash(this []byte) []byte {
	// cost: minimum is 4, max is 31, default is 10
	// (https://godoc.org/golang.org/x/crypto/bcrypt)
	cost := 10

	hash, err := bcrypt.GenerateFromPassword(this, cost)
	um.Check(err)

	return hash
}

/**
 * Checks a hash against its possible plaintext. This exists because of
 * bcrypt's mechanism, we shouldn't just um.Hash() and check it ourselves.
 *
 **/
func (um *UserManager) CheckHash(hash []byte, original []byte) bool {
	if bcrypt.CompareHashAndPassword(hash, original) != nil {
		return false
	}

	return true
}

/**
 * Internal UserManager error handling. Specifically, if a path error occurs
 * that means we just need to create the database. Furthermore in debug mode
 * a stdout message pops before panic()'ing, due to a panic possibility occuring
 * out of the blue.
 *
 **/
func (um *UserManager) Check(err error) {
	if err != nil {
		if _, ok := err.(*os.PathError); ok {
			um.Debug("Path error occured, creating database now.")
			os.Create(um.DatabasePath)
		} else {
			um.Debug(err.Error() + "\nPanicking.")
			panic(err)
		}
	}
}

/**
 * Reloads the database into memory, filling the appropriate variables.
 *
 **/
func (um *UserManager) Reload() {
	um.Users = make(map[string][]byte) // reset

	data, err := ioutil.ReadFile(um.DatabasePath)
	um.Check(err)

	accounts := strings.Split(string(data), "\n")
	for _, acc := range accounts {
		creds := strings.Split(acc, ":")

		if len(creds) != 2 {
			// at times for unknown reasons the length of creds is 1
			// which causes an unchecked panic to pop. it shouldn't mind us.
			break
		}

		um.Users[creds[0]] = []byte(creds[1])
	}
}

/**
 * Registers a new User, writing both into the database file and the memory.
 *
 **/
func (um *UserManager) Register(user string, pass string) bool {
	if _, exists := um.Users[user]; exists {
		return false
	}

	um.Users[user] = um.Hash([]byte(pass))
	pass = string(um.Users[user])

	f, err := os.OpenFile(um.DatabasePath, os.O_APPEND|os.O_WRONLY, 0666)
	defer f.Close()
	um.Check(err)

	_, err = f.WriteString(user + ":" + pass + "\n")
	um.Check(err)

	um.Debug("Registered user[" + user + "] with password[" + pass + "].")
	return true
}

/**
 * Changes the password of a User, given his old password matches the oldpass input variable.
 * Writes both into the database file and the memory.
 *
 * Returns false if the old password given doesn't match the actual old password, or User
 * doesn't exist.
 *
 **/
func (um *UserManager) ChangePass(User string, oldpass string, newpass string) bool {
	if um.CheckHash(um.Users[User], []byte(oldpass)) {
		oldpass := string(um.Users[User])
		um.Users[User] = um.Hash([]byte(newpass))
		newpass = string(um.Users[User])

		data, err := ioutil.ReadFile(um.DatabasePath)
		um.Check(err)

		lines := strings.Split(string(data), "\n")
		for i, line := range lines {
			if strings.Contains(line, User) && strings.Contains(line, oldpass) {
				lines[i] = User + ":" + newpass
				break
			}
		}

		output := strings.Join(lines, "\n")
		err = ioutil.WriteFile(um.DatabasePath, []byte(output), 0666)
		um.Check(err)

		um.Debug("Changed the password of User[" + User + "],\n\t" +
			"from[" + oldpass + "] to[" + newpass + "].")
		return true
	}

	um.Debug("Failed to change the password of User[" + User + "].")
	return false
}

/**
 * Changes the lifespan of a Session.
 * Mainly exists to handle the typecasting between int and int64.
 *
 **/
func (sess *Session) SetLifespan(seconds int) {
	sess.Lifespan = int64(seconds)
}

/**
 * Checks if a Session is logged.
 *
 **/
func (sess *Session) IsLogged() bool {
	if sess.User != "" {
		return true
	}

	return false
}

/**
 * Checks for expired Sessions.
 *
 **/
func (um *UserManager) CheckSessions() {
	for hash, sess := range um.Sessions {
		if sess.Timestamp+sess.Lifespan < time.Now().Unix() {
			um.Debug("Session[" + hash + "] has expired.")
			delete(um.Sessions, hash)
		}
	}
}

/**
 * Uses SHA256 to hash a Session token (the sum of identifiers).
 *
 **/
func (um *UserManager) HashHTTPSessionToken() string {
	hash := sha256.New()

	hash.Write([]byte(um.Request.UserAgent() + um.Request.RemoteAddr))

	return hex.EncodeToString(hash.Sum(nil))
}

/**
 * Generates a unique cookie hash (it keeps trying if not).
 *
 **/
func (um *UserManager) GenerateCookieHash() string {
	randBytes := make([]byte, 32)
	_, err := rand.Read(randBytes)
	um.Check(err)

    hash := sha256.New()
    hash.Write(randBytes)

    result := hex.EncodeToString(hash.Sum(nil))
    for _, sess := range um.Sessions {
    	if sess.Cookie == result {
    		return um.GenerateCookieHash()
    	}
    }

    return result
}

/**
 * Sets the HTTP cookie given the Session.
 *
 **/
func (um *UserManager) SetHTTPCookie(sess *Session) {
	http.SetCookie(um.Writer, &http.Cookie{
		Name:     "session",
		Value:    sess.Cookie,
		HttpOnly: true,
		Expires:  time.Unix(sess.Timestamp + sess.Lifespan, 0),
		MaxAge:   int(sess.Lifespan),
	})
}

/**
 * This function is made for the cookie mode. It attempts to find an existing Session.
 * If it exists, it validates the given cookie.
 * If vaildation fails it's like the Session never existed in the first place, having
 * a fresh one taking its place.
 * Returns nil if there is no http.Request object (abstract mode is on).
 *
 **/
func (um *UserManager) GetHTTPSession() *Session {
	if um.Request == nil {
		return nil
	}

	hash := um.HashHTTPSessionToken()

	if sess, exists := um.Sessions[hash]; exists {
		userCookie, err := um.Request.Cookie("session")

		if err == nil && userCookie.Value == sess.Cookie {
			return sess
		}
	}

	um.Sessions[hash] = CreateSession()
	sess := um.Sessions[hash]

	sess.Cookie = um.GenerateCookieHash()
	um.SetHTTPCookie(sess)

	return sess
}

/**
 * This function is made for the non cookie (abstract) mode. If a Session is not found
 * a new one takes its place.
 *
 **/
func (um *UserManager) GetSessionFromID(id string) *Session {
	if sess, exists := um.Sessions[id]; exists {
		return sess
	}

	um.Sessions[id] = CreateSession()
	return um.Sessions[id]
}

/**
 * Pretty simple.
 *
 **/
func (um *UserManager) Login(user string, pass string, sess *Session) bool {
	if um.CheckHash(um.Users[user], []byte(pass)) {
		sess.User = user

		um.Debug("User[" + user + "] has logged in.")
		return true
	}

	um.Debug("User[" + user + "] failed to log in.")
	return false
}

/**
 * Logs out the Session.
 *
 **/
func (um *UserManager) Logout(sess *Session) {
	um.Debug("Logging out user[" + sess.User + "].")
	sess.User = ""
}<|endoftext|>


<commit_before>/*

agorn is a wrapper around gorename for use with Acme.
It renames the entity under the cursor.

Usage:
	agorn [name]

Example:
	'agorn Foo' renames the entity under the cursor with 'Foo'.

gorename must be installed:
	% go get code.google.com/p/go.tools/cmd/gorename
*/
package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"code.google.com/p/goplan9/plan9/acme"
)

type window struct {
	win    *acme.Win
	offset int
	name   string
}

func currentAcmeWin() (*acme.Win, error) {
	winid := os.Getenv("winid")
	if winid == "" {
		return nil, fmt.Errorf("$winid not set - not running inside acme?")
	}
	id, err := strconv.Atoi(winid)
	if err != nil {
		return nil, fmt.Errorf("invalid $winid %q", winid)
	}
	win, err := acme.Open(id, nil)
	if err != nil {
		return nil, fmt.Errorf("cannot open acme window: %v", err)
	}
	return win, nil
}

func currentWindow() (*window, error) {
	win, err := currentAcmeWin()
	if err != nil {
		return nil, err
	}
	_, _, err = win.ReadAddr()
	if err != nil {
		return nil, fmt.Errorf("cannot read address: %v", err)
	}
	err = win.Ctl("addr=dot")
	if err != nil {
		return nil, fmt.Errorf("cannot set addr=dot: %v", err)
	}
	q0, _, err := win.ReadAddr()
	if err != nil {
		return nil, fmt.Errorf("cannot read address: %v", err)
	}
	b, err := win.ReadAll("tag")
	if err != nil {
		return nil, fmt.Errorf("cannot read tag: %v", err)
	}
	tag := string(b)
	i := strings.Index(tag, " ")
	if i == -1 {
		return nil, fmt.Errorf("tag with no spaces")
	}
	body, err := readBody(win)
	if err != nil {
		return nil, fmt.Errorf("cannot read body: %v", err)
	}
	return &window{win: win, name: tag[0:i], offset: toByteOffset(body, q0)}, nil
}

// We would use win.ReadAll except for a bug in acme
// where it crashes when reading trying to read more
// than the negotiated 9P message size.
func readBody(win *acme.Win) ([]byte, error) {
	var body []byte
	buf := make([]byte, 8000)
	for {
		n, err := win.Read("body", buf)
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		body = append(body, buf[0:n]...)
	}
	return body, nil
}

func toByteOffset(b []byte, off int) int {
	r := 0
	for i := range string(b) {
		if r == off {
			return i
		}
		r++
	}
	return len(b)
}

func (w *window) showAddr(addr string) {
	w.win.Fprintf("addr", addr)
	w.win.Ctl("dot=addr")
	w.win.Ctl("show")
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "usage: agorn NAME\nReplaces the name under the cursor with NAME\n")
		return
	}
	to := os.Args[1]

	win, err := currentWindow()
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		return
	}

	err = exec.Command("gorename", "-offset", fmt.Sprintf("%s:#%d", win.name, win.offset), "-to", to).Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, err.Error())
		return
	}

	win.win.Ctl("get")
	win.showAddr(fmt.Sprintf("#%d", win.offset))
}
<commit_msg>Reduce code duplication.<commit_after>/*

agorn is a wrapper around gorename for use with Acme.
It renames the entity under the cursor.

Usage:
	agorn [name]

Example:
	'agorn Foo' renames the entity under the cursor with 'Foo'.

gorename must be installed:
	% go get code.google.com/p/go.tools/cmd/gorename
*/
package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"code.google.com/p/goplan9/plan9/acme"
)

type window struct {
	win    *acme.Win
	offset int
	name   string
}

func currentAcmeWin() (*acme.Win, error) {
	winid := os.Getenv("winid")
	if winid == "" {
		return nil, fmt.Errorf("$winid not set - not running inside acme?")
	}
	id, err := strconv.Atoi(winid)
	if err != nil {
		return nil, fmt.Errorf("invalid $winid %q", winid)
	}
	win, err := acme.Open(id, nil)
	if err != nil {
		return nil, fmt.Errorf("cannot open acme window: %v", err)
	}
	return win, nil
}

func currentWindow() (*window, error) {
	win, err := currentAcmeWin()
	if err != nil {
		return nil, err
	}
	_, _, err = win.ReadAddr()
	if err != nil {
		return nil, fmt.Errorf("cannot read address: %v", err)
	}
	err = win.Ctl("addr=dot")
	if err != nil {
		return nil, fmt.Errorf("cannot set addr=dot: %v", err)
	}
	q0, _, err := win.ReadAddr()
	if err != nil {
		return nil, fmt.Errorf("cannot read address: %v", err)
	}
	b, err := win.ReadAll("tag")
	if err != nil {
		return nil, fmt.Errorf("cannot read tag: %v", err)
	}
	tag := string(b)
	i := strings.Index(tag, " ")
	if i == -1 {
		return nil, fmt.Errorf("tag with no spaces")
	}
	body, err := readBody(win)
	if err != nil {
		return nil, fmt.Errorf("cannot read body: %v", err)
	}
	return &window{win: win, name: tag[0:i], offset: toByteOffset(body, q0)}, nil
}

// We would use win.ReadAll except for a bug in acme
// where it crashes when reading trying to read more
// than the negotiated 9P message size.
func readBody(win *acme.Win) ([]byte, error) {
	var body []byte
	buf := make([]byte, 8000)
	for {
		n, err := win.Read("body", buf)
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		body = append(body, buf[0:n]...)
	}
	return body, nil
}

func toByteOffset(b []byte, off int) int {
	r := 0
	for i := range string(b) {
		if r == off {
			return i
		}
		r++
	}
	return len(b)
}

func (w *window) showAddr(addr string) {
	w.win.Fprintf("addr", addr)
	w.win.Ctl("dot=addr")
	w.win.Ctl("show")
}

func fail(err error) {
	fmt.Fprintf(os.Stderr, "agorn: %v", err)
	os.Exit(1)
}

func main() {
	if len(os.Args) < 2 {
		fail(fmt.Errorf("usage: agorn NAME\nReplaces the name under the cursor with NAME\n"))
	}
	to := os.Args[1]

	win, err := currentWindow()
	if err != nil {
		fail(err)
	}

	err = exec.Command("gorename", "-offset", fmt.Sprintf("%s:#%d", win.name, win.offset), "-to", to).Run()
	if err != nil {
		fail(err)
	}

	win.win.Ctl("get")
	win.showAddr(fmt.Sprintf("#%d", win.offset))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"expvar"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"

	"cloud.google.com/go/logging"

	topk "github.com/dgryski/go-topk"
	"golang.org/x/net/publicsuffix"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
)

type originAllower struct {
	m        map[string]struct{}
	ns       *expvar.Map
	hostname string
	gclog    logClient

	mu                 *sync.RWMutex
	topKAllDomains     *topk.Stream
	topKOfflistDomains *topk.Stream
}

// FIXME flush on shutdown
type logClient interface {
	Log(logging.Entry)
	Flush()
}

func newOriginAllower(blockedDomains []string, hostname string, gclog logClient, ns *expvar.Map) *originAllower {
	mu := &sync.RWMutex{}
	topKAllDomains := topk.New(100)
	topKOfflistDomains := topk.New(100)
	lifetime := new(expvar.Map).Init()
	ns.Set("lifetime", lifetime)
	lifetime.Set("top_all_domains", expvar.Func(func() interface{} {
		mu.RLock()
		defer mu.RUnlock()
		return topKAllDomains.Keys()
	}))
	lifetime.Set("top_offlist_domains", expvar.Func(func() interface{} {
		mu.RLock()
		defer mu.RUnlock()
		return topKOfflistDomains.Keys()
	}))

	oa := &originAllower{
		m:                  make(map[string]struct{}),
		ns:                 ns,
		hostname:           hostname,
		gclog:              gclog,
		mu:                 mu,
		topKAllDomains:     topKAllDomains,
		topKOfflistDomains: topKOfflistDomains,
	}
	for _, d := range blockedDomains {
		oa.m[d] = struct{}{}
	}
	return oa
}

func (oa *originAllower) Allow(r *http.Request) (string, bool) {
	origin := r.Header.Get("Origin")
	referrer := r.Header.Get("Referer")

	apiKey := r.FormValue("key")
	userAgent := r.Header.Get("User-Agent")

	remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		log.Printf("error splitting %#v as host:port: %s", r.RemoteAddr, err)
		remoteIP = "<parseerror>"
	}
	entry := &apiLogEntry{
		DetectedDomain: "",
		Allowed:        false,
		APIKey:         apiKey,
		Headers: headers{
			Origin:    origin,
			Referrer:  referrer,
			UserAgent: userAgent,
		},
	}
	defer func() {
		go oa.countRequest(entry, r, remoteIP)
	}()

	if origin == "" && referrer == "" {
		entry.Allowed = true
		return "", true
	}
	if origin != "" {
		domain, ok := oa.checkDomain(origin)
		entry.DetectedDomain = domain
		entry.Allowed = ok
		if !ok {
			entry.RejectionReason = rejectionConfig
		}
		return domain, ok
	}
	if referrer != "" {
		domain, ok := oa.checkDomain(referrer)
		entry.DetectedDomain = domain
		entry.Allowed = ok
		if !ok {
			entry.RejectionReason = rejectionConfig
		}
		return domain, ok
	}

	return "", false
}

// checkDomain checks if the detected domain from the request headers and
// whether domain is allowed to make requests against howsmyssl's API.
func (oa *originAllower) checkDomain(d string) (string, bool) {
	domain, err := effectiveDomain(d)
	if err != nil {
		// TODO(jmhodges): replace this len check with false when we use top-k
		return "", len(oa.m) == 0
	}
	_, isBlocked := oa.m[domain]
	// TODO(jmhodges): remove this len check when we use top-k
	return domain, !isBlocked || len(oa.m) == 0
}

func (oa *originAllower) countRequest(entry *apiLogEntry, r *http.Request, remoteIP string) {
	oa.gclog.Log(logging.Entry{
		Payload:     entry,
		HTTPRequest: &logging.HTTPRequest{Request: r, RemoteIP: remoteIP},
		Labels: map[string]string{
			"server_hostname": oa.hostname,
			"app":             "howsmyssl",
		},
	})

	if entry.DetectedDomain == "" {
		return
	}

	oa.mu.Lock()
	defer oa.mu.Unlock()
	oa.topKAllDomains.Insert(entry.DetectedDomain, 1)
	if !entry.Allowed {
		oa.topKOfflistDomains.Insert(entry.DetectedDomain, 1)
	}
}

func effectiveDomain(str string) (string, error) {
	u, err := url.Parse(str)
	if err != nil {
		return "", err
	}
	host := u.Host
	if host == "" {
		return "", fmt.Errorf("unparsable domain string %#v", str)
	}
	i := strings.Index(host, ":")
	if i >= 0 {
		host = host[:i]
	}

	if host == "localhost" {
		return "localhost", nil
	}
	d, err := publicsuffix.EffectiveTLDPlusOne(host)
	if err != nil {
		return "", err
	}
	return d, nil
}

func loadOriginsConfig(fp string) *originsConfig {
	f, err := os.Open(fp)
	if err != nil {
		log.Fatalf("unable to open origins config file %#v: %s", fp, err)
	}
	defer f.Close()
	jc := &originsConfig{}
	err = json.NewDecoder(f).Decode(jc)
	if err != nil {
		log.Fatalf("unable to parse origins config file %#v: %s", fp, err)
	}
	for _, a := range jc.BlockedOrigins {
		if strings.HasPrefix(a, "http://") || strings.HasPrefix(a, "https://") {
			log.Fatalf("origins config file (%#v) should have only domains without the leading scheme. For example, %#v should not have the protocol scheme at its beginning.", fp, a)
		}
		if strings.Contains(a, "/") {
			log.Fatalf("origins config file (%#v) should have only domains without a path after it. For example, %#v should not have a trailing path.", fp, a)
		}
	}
	return jc
}

type originsConfig struct {
	// BlockedOrigins are domains that are not to be allowed as referrers to the
	// API. They should not have a scheme or path, but only the domain, as in
	// "example.com".
	BlockedOrigins []string `json:"blocked_origins"`
}

type rejectionReason string

const rejectionConfig = rejectionReason("config")

type apiLogEntry struct {
	DetectedDomain  string          `json:"detected_domain"`
	Allowed         bool            `json:"allowed"`
	APIKey          string          `json:"api_key"`
	RejectionReason rejectionReason `json:"rejection_reason"`
	Headers         headers         `json:"headers"`
}

type headers struct {
	Origin    string `json:"origin"`
	Referrer  string `json:"referrer"`
	UserAgent string `json:"user_agent"`
}

func loadGoogleServiceAccount(fp string) *googleConfig {
	bs, err := ioutil.ReadFile(fp)
	if err != nil {
		log.Fatalf("unable to read Google service account config %#v: %s", fp, err)
	}
	c := &googleConfig{}
	err = json.Unmarshal(bs, c)
	if err != nil {
		log.Fatalf("unable to parse project ID from Google service account config %#v: %s", fp, err)
	}
	if c.ProjectID == "" {
		log.Fatalf("blank project ID in Google service account config %#v: %s", fp, err)
	}
	jwtConf, err := google.JWTConfigFromJSON(bs, logging.WriteScope)
	if err != nil {
		log.Fatalf("unable to parse Google service account config %#v: %s", fp, err)
	}
	c.conf = jwtConf
	return c
}

type googleConfig struct {
	ProjectID string `json:"project_id"`

	conf *jwt.Config `json:"-"`
}

var _ logClient = nullLogClient{}

type nullLogClient struct{}

func (n nullLogClient) Log(e logging.Entry) {
}

func (n nullLogClient) Flush() {
}
<commit_msg>use 0.0.0.0 in Allower for busted IPs (#152)<commit_after>package main

import (
	"encoding/json"
	"expvar"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"

	"cloud.google.com/go/logging"

	topk "github.com/dgryski/go-topk"
	"golang.org/x/net/publicsuffix"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
)

type originAllower struct {
	m        map[string]struct{}
	ns       *expvar.Map
	hostname string
	gclog    logClient

	mu                 *sync.RWMutex
	topKAllDomains     *topk.Stream
	topKOfflistDomains *topk.Stream
}

// FIXME flush on shutdown
type logClient interface {
	Log(logging.Entry)
	Flush()
}

func newOriginAllower(blockedDomains []string, hostname string, gclog logClient, ns *expvar.Map) *originAllower {
	mu := &sync.RWMutex{}
	topKAllDomains := topk.New(100)
	topKOfflistDomains := topk.New(100)
	lifetime := new(expvar.Map).Init()
	ns.Set("lifetime", lifetime)
	lifetime.Set("top_all_domains", expvar.Func(func() interface{} {
		mu.RLock()
		defer mu.RUnlock()
		return topKAllDomains.Keys()
	}))
	lifetime.Set("top_offlist_domains", expvar.Func(func() interface{} {
		mu.RLock()
		defer mu.RUnlock()
		return topKOfflistDomains.Keys()
	}))

	oa := &originAllower{
		m:                  make(map[string]struct{}),
		ns:                 ns,
		hostname:           hostname,
		gclog:              gclog,
		mu:                 mu,
		topKAllDomains:     topKAllDomains,
		topKOfflistDomains: topKOfflistDomains,
	}
	for _, d := range blockedDomains {
		oa.m[d] = struct{}{}
	}
	return oa
}

func (oa *originAllower) Allow(r *http.Request) (string, bool) {
	origin := r.Header.Get("Origin")
	referrer := r.Header.Get("Referer")

	apiKey := r.FormValue("key")
	userAgent := r.Header.Get("User-Agent")

	remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		log.Printf("error splitting %#v as host:port: %s", r.RemoteAddr, err)
		remoteIP = "0.0.0.0"
	}
	entry := &apiLogEntry{
		DetectedDomain: "",
		Allowed:        false,
		APIKey:         apiKey,
		Headers: headers{
			Origin:    origin,
			Referrer:  referrer,
			UserAgent: userAgent,
		},
	}
	defer func() {
		go oa.countRequest(entry, r, remoteIP)
	}()

	if origin == "" && referrer == "" {
		entry.Allowed = true
		return "", true
	}
	if origin != "" {
		domain, ok := oa.checkDomain(origin)
		entry.DetectedDomain = domain
		entry.Allowed = ok
		if !ok {
			entry.RejectionReason = rejectionConfig
		}
		return domain, ok
	}
	if referrer != "" {
		domain, ok := oa.checkDomain(referrer)
		entry.DetectedDomain = domain
		entry.Allowed = ok
		if !ok {
			entry.RejectionReason = rejectionConfig
		}
		return domain, ok
	}

	return "", false
}

// checkDomain checks if the detected domain from the request headers and
// whether domain is allowed to make requests against howsmyssl's API.
func (oa *originAllower) checkDomain(d string) (string, bool) {
	domain, err := effectiveDomain(d)
	if err != nil {
		// TODO(jmhodges): replace this len check with false when we use top-k
		return "", len(oa.m) == 0
	}
	_, isBlocked := oa.m[domain]
	// TODO(jmhodges): remove this len check when we use top-k
	return domain, !isBlocked || len(oa.m) == 0
}

func (oa *originAllower) countRequest(entry *apiLogEntry, r *http.Request, remoteIP string) {
	oa.gclog.Log(logging.Entry{
		Payload:     entry,
		HTTPRequest: &logging.HTTPRequest{Request: r, RemoteIP: remoteIP},
		Labels: map[string]string{
			"server_hostname": oa.hostname,
			"app":             "howsmyssl",
		},
	})

	if entry.DetectedDomain == "" {
		return
	}

	oa.mu.Lock()
	defer oa.mu.Unlock()
	oa.topKAllDomains.Insert(entry.DetectedDomain, 1)
	if !entry.Allowed {
		oa.topKOfflistDomains.Insert(entry.DetectedDomain, 1)
	}
}

func effectiveDomain(str string) (string, error) {
	u, err := url.Parse(str)
	if err != nil {
		return "", err
	}
	host := u.Host
	if host == "" {
		return "", fmt.Errorf("unparsable domain string %#v", str)
	}
	i := strings.Index(host, ":")
	if i >= 0 {
		host = host[:i]
	}

	if host == "localhost" {
		return "localhost", nil
	}
	d, err := publicsuffix.EffectiveTLDPlusOne(host)
	if err != nil {
		return "", err
	}
	return d, nil
}

func loadOriginsConfig(fp string) *originsConfig {
	f, err := os.Open(fp)
	if err != nil {
		log.Fatalf("unable to open origins config file %#v: %s", fp, err)
	}
	defer f.Close()
	jc := &originsConfig{}
	err = json.NewDecoder(f).Decode(jc)
	if err != nil {
		log.Fatalf("unable to parse origins config file %#v: %s", fp, err)
	}
	for _, a := range jc.BlockedOrigins {
		if strings.HasPrefix(a, "http://") || strings.HasPrefix(a, "https://") {
			log.Fatalf("origins config file (%#v) should have only domains without the leading scheme. For example, %#v should not have the protocol scheme at its beginning.", fp, a)
		}
		if strings.Contains(a, "/") {
			log.Fatalf("origins config file (%#v) should have only domains without a path after it. For example, %#v should not have a trailing path.", fp, a)
		}
	}
	return jc
}

type originsConfig struct {
	// BlockedOrigins are domains that are not to be allowed as referrers to the
	// API. They should not have a scheme or path, but only the domain, as in
	// "example.com".
	BlockedOrigins []string `json:"blocked_origins"`
}

type rejectionReason string

const rejectionConfig = rejectionReason("config")

type apiLogEntry struct {
	DetectedDomain  string          `json:"detected_domain"`
	Allowed         bool            `json:"allowed"`
	APIKey          string          `json:"api_key"`
	RejectionReason rejectionReason `json:"rejection_reason"`
	Headers         headers         `json:"headers"`
}

type headers struct {
	Origin    string `json:"origin"`
	Referrer  string `json:"referrer"`
	UserAgent string `json:"user_agent"`
}

func loadGoogleServiceAccount(fp string) *googleConfig {
	bs, err := ioutil.ReadFile(fp)
	if err != nil {
		log.Fatalf("unable to read Google service account config %#v: %s", fp, err)
	}
	c := &googleConfig{}
	err = json.Unmarshal(bs, c)
	if err != nil {
		log.Fatalf("unable to parse project ID from Google service account config %#v: %s", fp, err)
	}
	if c.ProjectID == "" {
		log.Fatalf("blank project ID in Google service account config %#v: %s", fp, err)
	}
	jwtConf, err := google.JWTConfigFromJSON(bs, logging.WriteScope)
	if err != nil {
		log.Fatalf("unable to parse Google service account config %#v: %s", fp, err)
	}
	c.conf = jwtConf
	return c
}

type googleConfig struct {
	ProjectID string `json:"project_id"`

	conf *jwt.Config `json:"-"`
}

var _ logClient = nullLogClient{}

type nullLogClient struct{}

func (n nullLogClient) Log(e logging.Entry) {
}

func (n nullLogClient) Flush() {
}
<|endoftext|>


<commit_before>package client

import (
	"flag"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"how/page"
	"how/version"
	"os"
	"regexp"
	"strings"
)

// Gleam result links out of Google's search result page.
func extractPages(doc *goquery.Document, numberToExtract int) []page.Page {
	var links []page.Page

	doc.Find("#res a").Each(func(i int, s *goquery.Selection) {
		href, _ := s.Attr("href")
		matched, _ := regexp.MatchString("^(/url\\?q=)?http://(meta.)?stackoverflow.com", href)
		if matched {
			resultPage := page.Page{href, s.Text()}
			resultPage.NormalizeResultUrl()
			links = append(links, resultPage)
		}
	})

	return links[0:numberToExtract]
}

// Build the Google search URL.
func searchPage(query []string, https bool) page.Page {
	body := "google.com/search?q=site:stackoverflow.com"

	var pre string
	if https {
		pre = "https://encrypted."
	} else {
		pre = "http://"
	}

	return page.Page{
		fmt.Sprint(pre, body, "%20", strings.Join(query, "%20")),
		strings.Join(query, " "),
	}
}

// Extract the top answer out of the
// Stackoverflow page.
func printInstructions(links []page.Page) {
	numberOfResults := len(links)
	results := make(chan string, numberOfResults)

	for _, link := range links {
		go fetchFormattedResult(link, results)
	}

	for i := 0; i < numberOfResults; i++ {
		select {
		case msg := <-results:
			fmt.Println(msg)
		}
	}
}

// For every <a> in the selection, replace with a MD-style link.
// Stuffs the results into the supplied channel.
func fetchFormattedResult(l page.Page, results chan<- string) {
	doc, _ := l.Fetch()
	selection := doc.Find(".answer").First().Find(".post-text")
	text := selection.Text()

	selection.Find("a").Each(func(i int, s *goquery.Selection) {
		href, _ := s.Attr("href")
		linkText := s.Text()

		var mdPage = []string{
			"[",
			linkText,
			"](",
			href,
			")",
		}

		if strings.EqualFold(linkText, href) {
			mdPage = []string{
				"<", linkText, ">",
			}
		}

		text = strings.Replace(text, linkText, strings.Join(mdPage, ""), 1)
	})
	results <- linkHeading(l) + text
}

// Header for every link
func linkHeading(l page.Page) string {
	border := strings.Repeat("*", len(l.Url)+5)
	return fmt.Sprintf("%s\n%s\n%s\n\n", border, l.Url, border)
}

func Main() {
	https := flag.Bool("https", false, "Use https")
	numAnswers := flag.Int("answers", 1, "Number of answers to retrieve")
	onlyPages := flag.Bool("links", false, "Only display answer links, not the result text")
	showVersion := flag.Bool("version", false, "Display the version number and exit")
	update := flag.Bool("update", false, "Update to the latest version")

	flag.Parse()
	args := flag.Args()

	if *showVersion {
		fmt.Printf("%1.1f", version.Version)
		os.Exit(0)
	}

	if *update {
		if version.NewerVersionAvailable() {
			version.Update()
		} else {
			fmt.Println("You're on the latest version.")
		}
		os.Exit(0)
	}

	if len(args) == 0 {
		flag.PrintDefaults()
		fmt.Println("Supply a question")
		os.Exit(2)
	}

	link := searchPage(args, *https)
	page, e := link.Fetch()
	if e != nil {
		fmt.Println("Unable to fetch results due to connection problem.")
		os.Exit(-1)
	}
	links := extractPages(page, *numAnswers)

	if *onlyPages {
		for i, link := range links {
			fmt.Printf("%d. [%s](%s)\n", i+1, link.Title, link.Url)
		}
		os.Exit(0)
	}

	printInstructions(links)
}
<commit_msg>Fix several panic situations<commit_after>package client

import (
	"flag"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"how/page"
	"how/version"
	"os"
	"regexp"
	"strings"
)

// Gleam result links out of Google's search result page.
func extractPages(doc *goquery.Document, numberToExtract int) []page.Page {
	var links []page.Page

	doc.Find("#res a").Each(func(i int, s *goquery.Selection) {
		href, _ := s.Attr("href")

		if acceptResultUrl(href) {
			resultPage := page.Page{href, s.Text()}
			resultPage.NormalizeResultUrl()
			links = append(links, resultPage)
		}
	})

	numberOfLinks := len(links)

	if numberToExtract > numberOfLinks {
		numberToExtract = numberOfLinks - 1
	}

	if numberToExtract <= 0 {
		return links
	}

	return links[0:numberToExtract]
}

func acceptResultUrl(url string) bool {
	matched, _ := regexp.MatchString("^(/url\\?q=)?http://(meta.)?stackoverflow.com", url)
	return matched && !strings.Contains(url, "stackoverflow.com/questions/tagged/") && !strings.Contains(url, "stackoverflow.com/tags")
}

// Build the Google search URL.
func searchPage(query []string, https bool) page.Page {
	body := "google.com/search?q=site:stackoverflow.com"

	var pre string
	if https {
		pre = "https://encrypted."
	} else {
		pre = "http://"
	}

	return page.Page{
		fmt.Sprint(pre, body, "%20", strings.Join(query, "%20")),
		strings.Join(query, " "),
	}
}

// Extract the top answer out of the
// Stackoverflow page.
func printInstructions(links []page.Page) {
	numberOfResults := len(links)
	results := make(chan string, numberOfResults)

	for _, link := range links {
		go fetchFormattedResult(link, results)
	}

	for i := 0; i < numberOfResults; i++ {
		select {
		case msg := <-results:
			fmt.Println(msg)
		}
	}
}

// For every <a> in the selection, replace with a MD-style link.
// Stuffs the results into the supplied channel.
func fetchFormattedResult(l page.Page, results chan<- string) {
	doc, _ := l.Fetch()
	selection := doc.Find(".answer").First().Find(".post-text")
	text := selection.Text()

	selection.Find("a").Each(func(i int, s *goquery.Selection) {
		href, _ := s.Attr("href")
		linkText := s.Text()

		var mdPage = []string{
			"[",
			linkText,
			"](",
			href,
			")",
		}

		if strings.EqualFold(linkText, href) {
			mdPage = []string{
				"<", linkText, ">",
			}
		}

		text = strings.Replace(text, linkText, strings.Join(mdPage, ""), 1)
	})
	results <- linkHeading(l) + text
}

// Header for every link
func linkHeading(l page.Page) string {
	border := strings.Repeat("*", len(l.Url)+5)
	return fmt.Sprintf("%s\n%s\n%s\n\n", border, l.Url, border)
}

func Main() {
	https := flag.Bool("https", false, "Use https")
	numAnswers := flag.Int("answers", 1, "Number of answers to retrieve")
	onlyPages := flag.Bool("links", false, "Only display answer links, not the result text")
	showVersion := flag.Bool("version", false, "Display the version number and exit")
	update := flag.Bool("update", false, "Update to the latest version")

	flag.Parse()
	args := flag.Args()

	if *showVersion {
		fmt.Printf("%1.1f\n", version.Version)
		os.Exit(0)
	}

	if *update {
		if version.NewerVersionAvailable() {
			version.Update()
		} else {
			fmt.Println("You're on the latest version.")
		}
		os.Exit(0)
	}

	if len(args) == 0 {
		flag.PrintDefaults()
		fmt.Println("Supply a question")
		os.Exit(2)
	}

	link := searchPage(args, *https)
	page, e := link.Fetch()
	if e != nil {
		fmt.Println("Unable to fetch results due to connection problem.")
		os.Exit(-1)
	}
	links := extractPages(page, *numAnswers)

	if *onlyPages {
		for i, link := range links {
			fmt.Printf("%d. [%s](%s)\n", i+1, link.Title, link.Url)
		}
		os.Exit(0)
	}

	printInstructions(links)
}
<|endoftext|>


<commit_before>package instructions

import "jvmgo/rtda"

// Store into reference array
type aastore struct {NoOperandsInstruction}
func (self *aastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into byte or boolean array
type bastore struct {NoOperandsInstruction}
func (self *bastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into char array
type castore struct {NoOperandsInstruction}
func (self *castore) execute(thread *rtda.Thread) {
    // todo
}

// Store into double array
type dastore struct {NoOperandsInstruction}
func (self *dastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into float array
type fastore struct {NoOperandsInstruction}
func (self *fastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into int array
type iastore struct {NoOperandsInstruction}
func (self *iastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into long array
type lastore struct {NoOperandsInstruction}
func (self *lastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into short array
type sastore struct {NoOperandsInstruction}
func (self *sastore) execute(thread *rtda.Thread) {
    // todo
}
<commit_msg>bastore<commit_after>package instructions

import (
    . "jvmgo/any"
    "jvmgo/rtda"
)

// Store into reference array
type aastore struct {NoOperandsInstruction}
func (self *aastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into byte or boolean array
type bastore struct {NoOperandsInstruction}
func (self *bastore) execute(thread *rtda.Thread) {
    arrRef, index, val := popOperands(thread)
    byteArr := arrRef.Fields().([]int8)
    byteArr[index] = val.(int8)
}

func popOperands(thread *rtda.Thread) (*rtda.Obj, int, Any) {
    stack := thread.CurrentFrame().OperandStack()
    arrRef := stack.PopRef()
    index := int(stack.PopInt())
    val := stack.Pop()
    if arrRef == nil {
        // todo
        panic("NullPointerException")
    }
    return arrRef, index, val
}

// Store into char array
type castore struct {NoOperandsInstruction}
func (self *castore) execute(thread *rtda.Thread) {
    // todo
}

// Store into double array
type dastore struct {NoOperandsInstruction}
func (self *dastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into float array
type fastore struct {NoOperandsInstruction}
func (self *fastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into int array
type iastore struct {NoOperandsInstruction}
func (self *iastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into long array
type lastore struct {NoOperandsInstruction}
func (self *lastore) execute(thread *rtda.Thread) {
    // todo
}

// Store into short array
type sastore struct {NoOperandsInstruction}
func (self *sastore) execute(thread *rtda.Thread) {
    // todo
}
<|endoftext|>


<commit_before>package aloja

import (
	"net/http"
	"time"

	"github.com/golang/glog"
	"github.com/jllopis/aloja/mw"
	"github.com/julienschmidt/httprouter"
)

// Aloja has the methods to abstract the work away. It provides a default Subrouter to
// carry on requests to '/'.
// You can coall NewSubrouter method on it to group routes and apply different middlewares to them
type Aloja struct {
	*Subrouter
	router           *httprouter.Router
	globalMiddleware *mw.Stack
	cert, key        string
	host             string
	port             string
}

// New creates a new Aloja with options. Accepted are:
// - Host
// - Port
// - SSLConf
// It exposes a global middleware that is called on every request,
// independently of the sobrouter configured if any
func New(options ...func(s *Aloja) *Aloja) *Aloja {
	srv := &Aloja{
		router:           httprouter.New(),
		globalMiddleware: mw.New(),
		host:             "",
		port:             "8888",
	}
	if options != nil {
		for _, option := range options {
			option(srv)
		}
	}
	srv.Subrouter = &Subrouter{
		srv.router,
		mw.New(),
		"/",
	}

	// add default service to give server time
	srv.HandleFunc("GET", "/time", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		t, _ := time.Now().UTC().MarshalJSON()
		w.Write(t)
	})
	return srv
}

// Host set the hostname for the running server
func (s *Aloja) Host(h string) *Aloja {
	s.host = h
	return s
}

// Port set the port for the running server
func (s *Aloja) Port(p string) *Aloja {
	s.port = p
	return s
}

// SSLConf pass in the cert and key for StartTLS
func (s *Aloja) SSLConf(cert, key string) *Aloja {
	if cert != "" && key != "" {
		s.cert = cert
		s.key = key
	}
	return s
}

// Run is a convenience function to start an http server.
// If cert and key were provided, ONLY https will be started!
// If they aren't, http will boot up. NOT RECOMMENDED!
func (s *Aloja) Run() {
	if s.cert != "" && s.key != "" {
		// StartTLS
		glog.Infof("Aloja started on %s:%s", s.host, s.port)
		glog.Fatalf("(ERR) main: Cannot start https server: %s", http.ListenAndServeTLS(s.host+":"+s.port, s.cert, s.key, s.globalMiddleware.Then(s.router)))
	} else {
		// Non TLS available!!!
		glog.Infof("Aloja started on %s:%s", s.host, s.port)
		glog.Warningf("SSL disabled!! Please, provide a certificate to be secured!!")
		http.ListenAndServe(s.host+":"+s.port, s.globalMiddleware.Then(s.router))
	}
}

// Add stacks a new middleware. It will be the last called.
// It accepts a list of middlewares and ordere is preserved left to right.
func (s *Aloja) AddGlobal(m ...mw.Middleware) {
	s.globalMiddleware.Add(m...)
}
<commit_msg>added LoadTemplates function<commit_after>package aloja

import (
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/golang/glog"
	"github.com/jllopis/aloja/mw"
	"github.com/julienschmidt/httprouter"
)

// Aloja has the methods to abstract the work away. It provides a default Subrouter to
// carry on requests to '/'.
// You can coall NewSubrouter method on it to group routes and apply different middlewares to them
type Aloja struct {
	*Subrouter
	router           *httprouter.Router
	globalMiddleware *mw.Stack
	cert, key        string
	host             string
	port             string
}

var (
	templates *template.Template
)

// New creates a new Aloja with options. Accepted are:
// - Host
// - Port
// - SSLConf
// It exposes a global middleware that is called on every request,
// independently of the sobrouter configured if any
func New(options ...func(s *Aloja) *Aloja) *Aloja {
	srv := &Aloja{
		router:           httprouter.New(),
		globalMiddleware: mw.New(),
		host:             "",
		port:             "8888",
	}
	if options != nil {
		for _, option := range options {
			option(srv)
		}
	}
	srv.Subrouter = &Subrouter{
		srv.router,
		mw.New(),
		"/",
	}

	// add default service to give server time
	srv.HandleFunc("GET", "/time", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		t, _ := time.Now().UTC().MarshalJSON()
		w.Write(t)
	})
	return srv
}

// Host set the hostname for the running server
func (s *Aloja) Host(h string) *Aloja {
	s.host = h
	return s
}

// Port set the port for the running server
func (s *Aloja) Port(p string) *Aloja {
	s.port = p
	return s
}

// SSLConf pass in the cert and key for StartTLS
func (s *Aloja) SSLConf(cert, key string) *Aloja {
	if cert != "" && key != "" {
		s.cert = cert
		s.key = key
	}
	return s
}

// Run is a convenience function to start an http server.
// If cert and key were provided, ONLY https will be started!
// If they aren't, http will boot up. NOT RECOMMENDED!
func (s *Aloja) Run() {
	if s.cert != "" && s.key != "" {
		// StartTLS
		glog.Infof("Aloja started on %s:%s", s.host, s.port)
		glog.Fatalf("(ERR) main: Cannot start https server: %s", http.ListenAndServeTLS(s.host+":"+s.port, s.cert, s.key, s.globalMiddleware.Then(s.router)))
	} else {
		// Non TLS available!!!
		glog.Infof("Aloja started on %s:%s", s.host, s.port)
		glog.Warningf("SSL disabled!! Please, provide a certificate to be secured!!")
		http.ListenAndServe(s.host+":"+s.port, s.globalMiddleware.Then(s.router))
	}
}

// Add stacks a new middleware. It will be the last called.
// It accepts a list of middlewares and ordere is preserved left to right.
func (s *Aloja) AddGlobal(m ...mw.Middleware) {
	s.globalMiddleware.Add(m...)
}

func (s *Aloja) LoadTemplates(tdir string, templateDelims []string) (*template.Template, error) {
	// So sorry. Can't remember where I found this code. I'll give credit when find author.
	// initialize the templates,
	// couldn't have used http://golang.org/pkg/html/template/#ParseGlob
	// since we have custom delimiters.
	basePath := tdir
	err := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// don't process folders themselves
		if info.IsDir() {
			return nil
		}
		templateName := path[len(basePath):]
		if templates == nil {
			templates = template.New(templateName)
			templates.Delims(templateDelims[0], templateDelims[1])
			_, err = templates.ParseFiles(path)
		} else {
			_, err = templates.New(templateName).ParseFiles(path)
		}
		glog.Infof("Processed template %s\n", templateName)
		return err
	})
	if err != nil {
		return nil, err
	}
	return templates, nil
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/flynn/flynn/host/volume"
	"github.com/flynn/flynn/host/volume/manager"
	"github.com/flynn/flynn/host/volume/zfs"
	"github.com/flynn/flynn/pkg/shutdown"
	"github.com/flynn/go-docopt"
	"github.com/inconshreveable/log15"
)

func init() {
	Register("destroy-volumes", runVolumeDestroy, `
usage: flynn-host destroy-volumes [options]

options:
  --volpath=PATH         directory to create volumes in [default: /var/lib/flynn/volumes]
  --include-data         actually destroy data in backends *this is dangerous* [default: false]
  --keep-system-images   don't destroy system images

Destroys all data volumes on this host.  This is a dangerous operation: data
may be permanently discarded.

If the '--include-data' flag is given, the volume database will be loaded, and
all data in the referenced systems will be removed (i.e., with a zfs backend,
the datasets will be destroyed).

If '--include-data' is not specified (or the volume database cannot be loaded),
it will simply be removed.  In this case, data will remain behind in the backend
storage engines (and eventually require manual cleanup), but the flynn-host
daemon's next launch will still be like a fresh launch.

If '--keep-system-images' is set, system image volumes are not destroyed and
the volume database is not removed.

Major features of backends will not be adjusted (i.e., zfs datasets will be
destroyed, but zpools will not be touched).`)
}

func runVolumeDestroy(args *docopt.Args) error {
	if os.Getuid() != 0 {
		fmt.Println("this command requires root!\ntry again with `sudo flynn-host destroy-volumes`.")
		shutdown.ExitWithCode(1)
	}

	volPath := args.String["--volpath"]
	includeData := args.Bool["--include-data"]
	keepSystemImages := args.Bool["--keep-system-images"]

	volumeDBPath := filepath.Join(volPath, "volumes.bolt")

	// if there is no state db, nothing to do
	if _, err := os.Stat(volumeDBPath); err != nil && os.IsNotExist(err) {
		fmt.Printf("no volume state db exists at %q; already clean.\n", volumeDBPath)
		shutdown.Exit()
	}

	// open state db.  we're maybe using it; and regardless want to flock before removing it
	vman, vmanErr := loadVolumeState(volumeDBPath)

	// if '--include-data' specified and vman loaded, destroy volumes
	allVolumesDestroyed := true
	if vmanErr != nil {
		fmt.Printf("%s\n", vmanErr)
	} else if includeData == false {
		fmt.Println("'--include-data' not specified; leaving backend data storage intact.")
	} else {
		if err := destroyVolumes(vman, keepSystemImages); err != nil {
			fmt.Printf("%s\n", err)
			allVolumesDestroyed = false
		}
	}

	if !keepSystemImages {
		// remove db file
		if err := os.Remove(volumeDBPath); err != nil {
			fmt.Printf("could not remove volume state db file %q: %s.\n", volumeDBPath, err)
			shutdown.ExitWithCode(5)
		}
		fmt.Printf("state db file %q removed.\n", volumeDBPath)
	}

	// exit code depends on if all volumes were destroyed successfully or not
	if includeData && !allVolumesDestroyed {
		shutdown.ExitWithCode(6)
	}
	shutdown.Exit()
	return nil
}

func loadVolumeState(volumeDBPath string) (*volumemanager.Manager, error) {
	// attempt to restore manager from state db
	// no need to defer closing the db; we're about to unlink it and the fd can drop on exit
	fmt.Println("opening volume state db...")
	vman := volumemanager.New(
		volumeDBPath,
		log15.New(),
		func() (volume.Provider, error) {
			return nil, nil
		},
	)
	if err := vman.OpenDB(); err != nil {
		if strings.HasSuffix(err.Error(), "timeout") { //bolt.ErrTimeout
			fmt.Println("volume state db is locked by another process; aborting.")
			shutdown.ExitWithCode(4)
		}
		return nil, fmt.Errorf("warning: the previous volume database could not be loaded; any data in backends may need manual removal\n  (error was: %s)", err)
	}
	fmt.Println("volume state db opened.")
	return vman, nil
}

func destroyVolumes(vman *volumemanager.Manager, keepSystemImages bool) error {
	someVolumesNotDestroyed := false
	var secondPass []string
	for id, vol := range vman.Volumes() {
		if keepSystemImages && vol.Info().Meta["flynn.system-image"] == "true" {
			continue
		}
		fmt.Printf("removing volume id=%q... ", id)
		if err := vman.DestroyVolume(id); err == nil {
			fmt.Println("success")
		} else if zfs.IsDatasetHasChildrenError(err) {
			fmt.Println("has children, coming back to it later")
			secondPass = append(secondPass, id)
		} else {
			fmt.Printf("error: %s\n", err)
			someVolumesNotDestroyed = true
		}
	}
	for id, vol := range vman.Volumes() {
		if keepSystemImages && vol.Info().Meta["flynn.system-image"] == "true" {
			continue
		}
		fmt.Printf("removing volume id=%q... ", id)
		if err := vman.DestroyVolume(id); err == nil {
			fmt.Println("success")
		} else {
			fmt.Printf("error: %s\n", err)
			someVolumesNotDestroyed = true
		}
	}

	if someVolumesNotDestroyed {
		return fmt.Errorf("some volumes were not destroyed successfully")
	}
	return nil
}
<commit_msg>host/cli: Remove unused secondPass from destroy-volumes<commit_after>package cli

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/flynn/flynn/host/volume"
	"github.com/flynn/flynn/host/volume/manager"
	"github.com/flynn/flynn/host/volume/zfs"
	"github.com/flynn/flynn/pkg/shutdown"
	"github.com/flynn/go-docopt"
	"github.com/inconshreveable/log15"
)

func init() {
	Register("destroy-volumes", runVolumeDestroy, `
usage: flynn-host destroy-volumes [options]

options:
  --volpath=PATH         directory to create volumes in [default: /var/lib/flynn/volumes]
  --include-data         actually destroy data in backends *this is dangerous* [default: false]
  --keep-system-images   don't destroy system images

Destroys all data volumes on this host.  This is a dangerous operation: data
may be permanently discarded.

If the '--include-data' flag is given, the volume database will be loaded, and
all data in the referenced systems will be removed (i.e., with a zfs backend,
the datasets will be destroyed).

If '--include-data' is not specified (or the volume database cannot be loaded),
it will simply be removed.  In this case, data will remain behind in the backend
storage engines (and eventually require manual cleanup), but the flynn-host
daemon's next launch will still be like a fresh launch.

If '--keep-system-images' is set, system image volumes are not destroyed and
the volume database is not removed.

Major features of backends will not be adjusted (i.e., zfs datasets will be
destroyed, but zpools will not be touched).`)
}

func runVolumeDestroy(args *docopt.Args) error {
	if os.Getuid() != 0 {
		fmt.Println("this command requires root!\ntry again with `sudo flynn-host destroy-volumes`.")
		shutdown.ExitWithCode(1)
	}

	volPath := args.String["--volpath"]
	includeData := args.Bool["--include-data"]
	keepSystemImages := args.Bool["--keep-system-images"]

	volumeDBPath := filepath.Join(volPath, "volumes.bolt")

	// if there is no state db, nothing to do
	if _, err := os.Stat(volumeDBPath); err != nil && os.IsNotExist(err) {
		fmt.Printf("no volume state db exists at %q; already clean.\n", volumeDBPath)
		shutdown.Exit()
	}

	// open state db.  we're maybe using it; and regardless want to flock before removing it
	vman, vmanErr := loadVolumeState(volumeDBPath)

	// if '--include-data' specified and vman loaded, destroy volumes
	allVolumesDestroyed := true
	if vmanErr != nil {
		fmt.Printf("%s\n", vmanErr)
	} else if includeData == false {
		fmt.Println("'--include-data' not specified; leaving backend data storage intact.")
	} else {
		if err := destroyVolumes(vman, keepSystemImages); err != nil {
			fmt.Printf("%s\n", err)
			allVolumesDestroyed = false
		}
	}

	if !keepSystemImages {
		// remove db file
		if err := os.Remove(volumeDBPath); err != nil {
			fmt.Printf("could not remove volume state db file %q: %s.\n", volumeDBPath, err)
			shutdown.ExitWithCode(5)
		}
		fmt.Printf("state db file %q removed.\n", volumeDBPath)
	}

	// exit code depends on if all volumes were destroyed successfully or not
	if includeData && !allVolumesDestroyed {
		shutdown.ExitWithCode(6)
	}
	shutdown.Exit()
	return nil
}

func loadVolumeState(volumeDBPath string) (*volumemanager.Manager, error) {
	// attempt to restore manager from state db
	// no need to defer closing the db; we're about to unlink it and the fd can drop on exit
	fmt.Println("opening volume state db...")
	vman := volumemanager.New(
		volumeDBPath,
		log15.New(),
		func() (volume.Provider, error) {
			return nil, nil
		},
	)
	if err := vman.OpenDB(); err != nil {
		if strings.HasSuffix(err.Error(), "timeout") { //bolt.ErrTimeout
			fmt.Println("volume state db is locked by another process; aborting.")
			shutdown.ExitWithCode(4)
		}
		return nil, fmt.Errorf("warning: the previous volume database could not be loaded; any data in backends may need manual removal\n  (error was: %s)", err)
	}
	fmt.Println("volume state db opened.")
	return vman, nil
}

func destroyVolumes(vman *volumemanager.Manager, keepSystemImages bool) error {
	someVolumesNotDestroyed := false
	for id, vol := range vman.Volumes() {
		if keepSystemImages && vol.Info().Meta["flynn.system-image"] == "true" {
			continue
		}
		fmt.Printf("removing volume id=%q... ", id)
		if err := vman.DestroyVolume(id); err == nil {
			fmt.Println("success")
		} else if zfs.IsDatasetHasChildrenError(err) {
			fmt.Println("has children, coming back to it later")
		} else {
			fmt.Printf("error: %s\n", err)
			someVolumesNotDestroyed = true
		}
	}
	for id, vol := range vman.Volumes() {
		if keepSystemImages && vol.Info().Meta["flynn.system-image"] == "true" {
			continue
		}
		fmt.Printf("removing volume id=%q... ", id)
		if err := vman.DestroyVolume(id); err == nil {
			fmt.Println("success")
		} else {
			fmt.Printf("error: %s\n", err)
			someVolumesNotDestroyed = true
		}
	}

	if someVolumesNotDestroyed {
		return fmt.Errorf("some volumes were not destroyed successfully")
	}
	return nil
}
<|endoftext|>


<commit_before>// Package flagconf combines the standard library's flag package
// with Andrew Gallant's excellent TOML parsing library:
// https://github.com/BurntSushi/toml.
//
// This package sets program options from a TOML configuration file
// while allowing the settings to be overridden with command-line flags as well.
package flagconf

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/BurntSushi/toml"
)

// ParseStrings reads a TOML configuration file at path as well as
// command-line arguments in args  and sets matching options in config,
// which must be a non-nil pointer to a struct.
//
// ParseStrings is similar to Parse except that it provides the caller
// with more fine-grained control.
//
// The argument array is passed into ParseStrings the args parameter;
// note that this is treated like os.Args in that args[0] is interpreted
// as the executable name and args[1:] are flags.
//
// The allowNoConfig parameter controls whether ParseStrings returns an error
// if no file is found at path.
func ParseStrings(args []string, path string, config interface{}, allowNoConfigFile bool) error {
	if len(args) < 1 {
		return fmt.Errorf("flagconf: ParseStrings called with empty args")
	}
	v := reflect.ValueOf(config)
	if v.Kind() != reflect.Ptr || reflect.Indirect(v).Kind() != reflect.Struct {
		return fmt.Errorf("flagconf: config must be a pointer to a struct")
	}

	flagset := flag.NewFlagSet(args[0], flag.ContinueOnError)
	flagset.Usage = func() {}
	// Create flags
	if err := registerFlags(flagset, reflect.Indirect(v), "", ""); err != nil {
		return err
	}

	// Load TOML
	_, err := toml.DecodeFile(path, config)
	if err != nil {
		if !(os.IsNotExist(err) && allowNoConfigFile) {
			return err
		}
	}

	// Override any settings with configured flags
	if err = flagset.Parse(args[1:]); err != nil {
		// In case flag parsing fails, return a custom error containing usage
		// info if user wants to print it.
		buf := &bytes.Buffer{}
		flagset.SetOutput(buf)
		flagset.PrintDefaults()
		err = FlagError{Err: err, Usage: buf.String()}
	}
	return err
}

// FlagError combines error received from flag parsing with default usage info.
type FlagError struct {
	Err   error
	Usage string
}

func (e FlagError) Error() string {
	return e.Err.Error()
}

/*
Parse reads a TOML configuration file at path as well as user-supplied options
from os.Args and sets matching options in config, which must be a non-nil pointer to a struct.

Typical usage is that the user represents configuration options with a struct type
and then populates a value of that type with the default configuration values.

Then the user calls flagconf.Parse, passing in the path to the configuration file
and a pointer to the configuration value. This function will read settings
from the TOML file and then read the user-supplied arguments from os.Args.

Example

Here is a small example:

    import (
      "github.com/cespare/flagconf"
    )

    type Config struct {
      MaxProcs int    `desc:"maximum OS threads"`
      Addr     string `desc:"listen address (with port)"`
    }

    func main() {
      // Set the defaults
      config := Config{
        MaxProcs: 4,
      }
      flagconf.Parse("config.toml", &config)
    }

Now if the TOML looks like this:

    maxprocs = 8
    addr = "localhost:7755"

and the user runs the program with

    ./prog -addr ":8888"

then conf will be:

    MaxProcs: 8
    Addr: ":8888"

(That is, TOML settings override the defaults and flags given override those.)

Descriptions for the flags are taken from the "desc" struct tag.
A default description is created based on the field type if a tag is not provided.

TOML matches are attempted for every exported field in the configuration struct.
Flag names are constructed for every exported field. Unexported fields,
as well as exported fields tagged with `flag:"-"`, are ignored by flagconf.
(If a field is ignored by using this tag, it is typically best to also use
`toml:"-"` so that the field is not picked up by the TOML parser.)

Parse returns an error if no file can be found at path.

Types

The basic types flagconf supports are those which are directly supported by both
package flag and TOML:

    bool
    string
    int
    int64
    uint
    uint64
    float64

Flagconf also supports any type implementing flag.Value, as long as TOML also supports it.

Finally, flagconf supports nesting by recursively inspecting structs
and creating them as necessary when the config value contains a nil struct pointer.
In TOML, a struct corresponds to a nested section; in flags the name will be dot-separated:

    type Conf {
      S *struct {
        N int
      }
    }

    // corresponds to this TOML
    [s]
    n = 3

    // and this flag
    -s.n=3

Naming

Matching names from TOML values to struct field names is much like encoding/json:
exact matches are preferred and then case-insensitive matching will be accepted.
(TOML names are typically lowercase, but the struct fields must be exported.)
The struct tag "toml" can be used to set a different name.

The flag names are constructed by lowercasing the struct field name.
The "flag" struct tag controls the flag name.

    type Conf struct {
      Foo string `toml:"bar" flag:"baz"`
    }
*/
func Parse(path string, config interface{}) error {
	return ParseStrings(os.Args, path, config, false)
}

// MustParse is like Parse except if parsing fails it prints the error and
// exits the program.
func MustParse(path string, config interface{}) {
	if err := ParseStrings(os.Args, path, config, false); err != nil {
		if ferr, ok := err.(FlagError); ok {
			fmt.Fprintln(os.Stderr, ferr.Usage)
		} else {
			fmt.Fprintln(os.Stderr, err)
		}
		os.Exit(2)
	}
}

func joinNS(ns, name string) string {
	if ns == "" {
		return name
	}
	return ns + "." + name
}

func registerFlags(flagset *flag.FlagSet, v reflect.Value, namespace, description string) error {
	if v.Kind() == reflect.Struct {
		for i := 0; i < v.NumField(); i++ {
			field := v.Field(i)
			if !field.CanSet() {
				continue
			}
			typ := v.Type().Field(i)
			name := strings.ToLower(typ.Name)
			if tag := typ.Tag.Get("flag"); tag != "" {
				if tag == "-" {
					continue
				}
				name = tag
			}
			desc := typ.Tag.Get("desc")
			newNS := joinNS(namespace, name)
			// Create uninitialized pointers to structs
			if field.Kind() == reflect.Ptr && field.Type().Elem().Kind() == reflect.Struct {
				if field.IsNil() && field.IsValid() && field.CanSet() {
					newField := reflect.New(field.Type().Elem())
					field.Set(newField)
				}
				field = field.Elem()
			}
			if err := registerFlags(flagset, field, newNS, desc); err != nil {
				return err
			}
		}
		return nil
	}

	name := reflect.ValueOf(namespace)
	usage := reflect.ValueOf(description)
	if description == "" {
		usage = reflect.ValueOf(fmt.Sprintf("(%s flag, no description given)", v.Type()))
	}

	var flagFunc reflect.Value

	switch v.Type() {
	case reflect.TypeOf(false):
		flagFunc = reflect.ValueOf(flagset.BoolVar)
	case reflect.TypeOf(float64(0)):
		flagFunc = reflect.ValueOf(flagset.Float64Var)
	case reflect.TypeOf(int(0)):
		flagFunc = reflect.ValueOf(flagset.IntVar)
	case reflect.TypeOf(int64(0)):
		flagFunc = reflect.ValueOf(flagset.Int64Var)
	case reflect.TypeOf(""):
		flagFunc = reflect.ValueOf(flagset.StringVar)
	case reflect.TypeOf(uint(0)):
		flagFunc = reflect.ValueOf(flagset.UintVar)
	case reflect.TypeOf(uint64(0)):
		flagFunc = reflect.ValueOf(flagset.Uint64Var)
	default:
		// reflect.Type of flag.Value
		fvt := reflect.TypeOf((*flag.Value)(nil)).Elem()
		if v.Type().Implements(fvt) {
			flagset.Var(v.Interface().(flag.Value), namespace, usage.String())
			return nil
		}
		// If value is addressable, its pointer may implement flag.Value
		if v.CanAddr() && v.Addr().Type().Implements(fvt) {
			flagset.Var(v.Addr().Interface().(flag.Value), namespace, usage.String())
			return nil
		}

		return fmt.Errorf("flagconf: unhandled type: %s", v.Type())
	}

	p := v.Addr()
	args := []reflect.Value{p, name, v, usage}
	flagFunc.Call(args)
	return nil
}

// Strings is a convenience wrapper around a string slice that implements
// flag.Value and handles the value as comma-separated list.
//
// For example flag -peers=127.0.0.1,127.0.0.2 will result in a slice
// {"127.0.0.1", "127.0.0.2"}.
type Strings []string

func (ss Strings) String() string {
	return strings.Join(ss, ",")
}

func (ss *Strings) Set(s string) error {
	*ss = Strings(strings.Split(s, ","))
	return nil
}

// Ints is a convenience wrapper around an int slice that implements flag.Value
// and handles the value as comma-separated list.
//
// For example flag -cpu=1,2,4 will result in a slice {1, 2, 4}.
type Ints []int

func (is Ints) String() string {
	ss := make([]string, 0, len(is))
	for _, i := range is {
		ss = append(ss, strconv.Itoa(i))
	}
	return strings.Join(ss, ",")
}

func (is *Ints) Set(ss string) error {
	for _, s := range strings.Split(ss, ",") {
		i, err := strconv.Atoi(s)
		if err != nil {
			return err
		}
		*is = append(*is, i)
	}
	return nil
}
<commit_msg>Prevent flagset.ParseStrings from printing error<commit_after>// Package flagconf combines the standard library's flag package
// with Andrew Gallant's excellent TOML parsing library:
// https://github.com/BurntSushi/toml.
//
// This package sets program options from a TOML configuration file
// while allowing the settings to be overridden with command-line flags as well.
package flagconf

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/BurntSushi/toml"
)

// ParseStrings reads a TOML configuration file at path as well as
// command-line arguments in args  and sets matching options in config,
// which must be a non-nil pointer to a struct.
//
// ParseStrings is similar to Parse except that it provides the caller
// with more fine-grained control.
//
// The argument array is passed into ParseStrings the args parameter;
// note that this is treated like os.Args in that args[0] is interpreted
// as the executable name and args[1:] are flags.
//
// The allowNoConfig parameter controls whether ParseStrings returns an error
// if no file is found at path.
func ParseStrings(args []string, path string, config interface{}, allowNoConfigFile bool) error {
	if len(args) < 1 {
		return fmt.Errorf("flagconf: ParseStrings called with empty args")
	}
	v := reflect.ValueOf(config)
	if v.Kind() != reflect.Ptr || reflect.Indirect(v).Kind() != reflect.Struct {
		return fmt.Errorf("flagconf: config must be a pointer to a struct")
	}

	flagset := flag.NewFlagSet(args[0], flag.ContinueOnError)
	// Create flags
	if err := registerFlags(flagset, reflect.Indirect(v), "", ""); err != nil {
		return err
	}

	// Load TOML
	_, err := toml.DecodeFile(path, config)
	if err != nil {
		if !(os.IsNotExist(err) && allowNoConfigFile) {
			return err
		}
	}

	// Prevent flagset.Parse from printing error and usage to stderr if parsing
	// fails
	flagset.Usage = func() {}
	buf := &bytes.Buffer{}
	flagset.SetOutput(buf)

	// Override any settings with configured flags
	if err = flagset.Parse(args[1:]); err != nil {
		// In case flag parsing fails, return a custom error containing usage
		// info if user wants to print it.
		flagset.PrintDefaults()
		err = FlagError{Err: err, Usage: buf.String()}
	}
	return err
}

// FlagError combines error received from flag parsing with default usage info.
type FlagError struct {
	Err   error
	Usage string
}

func (e FlagError) Error() string {
	return e.Err.Error()
}

/*
Parse reads a TOML configuration file at path as well as user-supplied options
from os.Args and sets matching options in config, which must be a non-nil pointer to a struct.

Typical usage is that the user represents configuration options with a struct type
and then populates a value of that type with the default configuration values.

Then the user calls flagconf.Parse, passing in the path to the configuration file
and a pointer to the configuration value. This function will read settings
from the TOML file and then read the user-supplied arguments from os.Args.

Example

Here is a small example:

    import (
      "github.com/cespare/flagconf"
    )

    type Config struct {
      MaxProcs int    `desc:"maximum OS threads"`
      Addr     string `desc:"listen address (with port)"`
    }

    func main() {
      // Set the defaults
      config := Config{
        MaxProcs: 4,
      }
      flagconf.Parse("config.toml", &config)
    }

Now if the TOML looks like this:

    maxprocs = 8
    addr = "localhost:7755"

and the user runs the program with

    ./prog -addr ":8888"

then conf will be:

    MaxProcs: 8
    Addr: ":8888"

(That is, TOML settings override the defaults and flags given override those.)

Descriptions for the flags are taken from the "desc" struct tag.
A default description is created based on the field type if a tag is not provided.

TOML matches are attempted for every exported field in the configuration struct.
Flag names are constructed for every exported field. Unexported fields,
as well as exported fields tagged with `flag:"-"`, are ignored by flagconf.
(If a field is ignored by using this tag, it is typically best to also use
`toml:"-"` so that the field is not picked up by the TOML parser.)

Parse returns an error if no file can be found at path.

Types

The basic types flagconf supports are those which are directly supported by both
package flag and TOML:

    bool
    string
    int
    int64
    uint
    uint64
    float64

Flagconf also supports any type implementing flag.Value, as long as TOML also supports it.

Finally, flagconf supports nesting by recursively inspecting structs
and creating them as necessary when the config value contains a nil struct pointer.
In TOML, a struct corresponds to a nested section; in flags the name will be dot-separated:

    type Conf {
      S *struct {
        N int
      }
    }

    // corresponds to this TOML
    [s]
    n = 3

    // and this flag
    -s.n=3

Naming

Matching names from TOML values to struct field names is much like encoding/json:
exact matches are preferred and then case-insensitive matching will be accepted.
(TOML names are typically lowercase, but the struct fields must be exported.)
The struct tag "toml" can be used to set a different name.

The flag names are constructed by lowercasing the struct field name.
The "flag" struct tag controls the flag name.

    type Conf struct {
      Foo string `toml:"bar" flag:"baz"`
    }
*/
func Parse(path string, config interface{}) error {
	return ParseStrings(os.Args, path, config, false)
}

// MustParse is like Parse except if parsing fails it prints the error and
// exits the program.
func MustParse(path string, config interface{}) {
	if err := ParseStrings(os.Args, path, config, false); err != nil {
		if ferr, ok := err.(FlagError); ok {
			fmt.Fprintln(os.Stderr, ferr.Usage)
		} else {
			fmt.Fprintln(os.Stderr, err)
		}
		os.Exit(2)
	}
}

func joinNS(ns, name string) string {
	if ns == "" {
		return name
	}
	return ns + "." + name
}

func registerFlags(flagset *flag.FlagSet, v reflect.Value, namespace, description string) error {
	if v.Kind() == reflect.Struct {
		for i := 0; i < v.NumField(); i++ {
			field := v.Field(i)
			if !field.CanSet() {
				continue
			}
			typ := v.Type().Field(i)
			name := strings.ToLower(typ.Name)
			if tag := typ.Tag.Get("flag"); tag != "" {
				if tag == "-" {
					continue
				}
				name = tag
			}
			desc := typ.Tag.Get("desc")
			newNS := joinNS(namespace, name)
			// Create uninitialized pointers to structs
			if field.Kind() == reflect.Ptr && field.Type().Elem().Kind() == reflect.Struct {
				if field.IsNil() && field.IsValid() && field.CanSet() {
					newField := reflect.New(field.Type().Elem())
					field.Set(newField)
				}
				field = field.Elem()
			}
			if err := registerFlags(flagset, field, newNS, desc); err != nil {
				return err
			}
		}
		return nil
	}

	name := reflect.ValueOf(namespace)
	usage := reflect.ValueOf(description)
	if description == "" {
		usage = reflect.ValueOf(fmt.Sprintf("(%s flag, no description given)", v.Type()))
	}

	var flagFunc reflect.Value

	switch v.Type() {
	case reflect.TypeOf(false):
		flagFunc = reflect.ValueOf(flagset.BoolVar)
	case reflect.TypeOf(float64(0)):
		flagFunc = reflect.ValueOf(flagset.Float64Var)
	case reflect.TypeOf(int(0)):
		flagFunc = reflect.ValueOf(flagset.IntVar)
	case reflect.TypeOf(int64(0)):
		flagFunc = reflect.ValueOf(flagset.Int64Var)
	case reflect.TypeOf(""):
		flagFunc = reflect.ValueOf(flagset.StringVar)
	case reflect.TypeOf(uint(0)):
		flagFunc = reflect.ValueOf(flagset.UintVar)
	case reflect.TypeOf(uint64(0)):
		flagFunc = reflect.ValueOf(flagset.Uint64Var)
	default:
		// reflect.Type of flag.Value
		fvt := reflect.TypeOf((*flag.Value)(nil)).Elem()
		if v.Type().Implements(fvt) {
			flagset.Var(v.Interface().(flag.Value), namespace, usage.String())
			return nil
		}
		// If value is addressable, its pointer may implement flag.Value
		if v.CanAddr() && v.Addr().Type().Implements(fvt) {
			flagset.Var(v.Addr().Interface().(flag.Value), namespace, usage.String())
			return nil
		}

		return fmt.Errorf("flagconf: unhandled type: %s", v.Type())
	}

	p := v.Addr()
	args := []reflect.Value{p, name, v, usage}
	flagFunc.Call(args)
	return nil
}

// Strings is a convenience wrapper around a string slice that implements
// flag.Value and handles the value as comma-separated list.
//
// For example flag -peers=127.0.0.1,127.0.0.2 will result in a slice
// {"127.0.0.1", "127.0.0.2"}.
type Strings []string

func (ss Strings) String() string {
	return strings.Join(ss, ",")
}

func (ss *Strings) Set(s string) error {
	*ss = Strings(strings.Split(s, ","))
	return nil
}

// Ints is a convenience wrapper around an int slice that implements flag.Value
// and handles the value as comma-separated list.
//
// For example flag -cpu=1,2,4 will result in a slice {1, 2, 4}.
type Ints []int

func (is Ints) String() string {
	ss := make([]string, 0, len(is))
	for _, i := range is {
		ss = append(ss, strconv.Itoa(i))
	}
	return strings.Join(ss, ",")
}

func (is *Ints) Set(ss string) error {
	for _, s := range strings.Split(ss, ",") {
		i, err := strconv.Atoi(s)
		if err != nil {
			return err
		}
		*is = append(*is, i)
	}
	return nil
}
<|endoftext|>


<commit_before>package gom

import (
	"fmt"
	"reflect"
	"strings"
	"time"
)

func IsEmpty(v interface{}) bool {
	times := time.Time{}
	if times == v {
		return true
	}
	if v == "" {
		return true
	}
	if v == 0 {
		return true
	}
	if v == 0.0 {
		return true
	}
	if v == nil {
		return true
	}
	return false
}
func getType(v interface{}) (reflect.Type, bool, bool) {
	tt := reflect.TypeOf(v)
	isPtr := false
	islice := false
	if tt.Kind() == reflect.Ptr {
		tt = tt.Elem()
		isPtr = true
	}
	if tt.Kind() == reflect.Slice || tt.Kind() == reflect.Array {
		tt = tt.Elem()
		islice = true
	}
	if debug {
		fmt.Println("Test getType, result:", tt, isPtr, islice)
	}
	return tt, isPtr, islice
}
func getTableModels(vs ...interface{}) []TableModel {
	tablemodels := []TableModel{}
	for _, v := range vs {
		tablemodels = append(tablemodels, getTableModel(v))
	}
	return tablemodels
}
func GetTableModel(v interface{}, names ...string) TableModel {
	tm := getTableModel(v)
	var cc []Column
	for _, ct := range tm.Columns {
		for _, name := range names {
			if ct.ColumnName == name {
				cc = append(cc, ct)
			}
		}
	}
	tm.Columns = cc
	return tm

}
func CreateSingleValueTableModel(v interface{}, table string, field string) TableModel {
	tt, _, _ := getType(v)
	vals := reflect.New(tt).Elem()
	columns := []Column{{ColumnName: field, ColumnType: tt, IsPrimary: false, Auto: false}}
	return TableModel{Columns: columns, TableName: table, ModelType: tt, ModelValue: vals}
}
func getTableModel(v interface{}) TableModel {
	if v != nil && reflect.TypeOf(v).Kind() != reflect.Interface {
		tt, isPtr, _ := getType(v)
		vals := reflect.ValueOf(v)
		if isPtr {
			vals = vals.Elem()
		}
		if tt.NumField() > 0 && tt.NumMethod() > 0 {
			nameMethod := vals.MethodByName("TableName")
			if debug {
				fmt.Println(nameMethod)
			}
			tableName := nameMethod.Call(nil)[0].String()
			columns, primary := getColumns(vals)
			ccs := []Column{primary}
			ccs = append(ccs, columns...)
			return TableModel{ModelType: tt, ModelValue: vals, Columns: ccs, TableName: tableName, Primary: primary}
		} else {
			return TableModel{}
		}
	} else {
		return TableModel{}
	}
}
func getColumns(v reflect.Value) ([]Column, Column) {
	var columns []Column
	var primary Column
	results := reflect.Indirect(reflect.ValueOf(&columns))
	oo := v.Type()
	i := 0
	for ; i < oo.NumField(); i++ {
		field := oo.Field(i)
		col, tps := getColumnFromField(field)
		if tps != -1 {
			if tps == 1 || tps == 2 {
				primary = col
			} else {
				n := reflect.Indirect(reflect.ValueOf(&col))
				if results.Kind() == reflect.Ptr {
					results.Set(reflect.Append(results, n.Addr()))
				} else {
					results.Set(reflect.Append(results, n))
				}
			}

		}
	}
	if debug {
		fmt.Println("columns is:", columns)
	}
	return columns, primary
}
func getColumnFromField(filed reflect.StructField) (Column, int) {
	tag, tps := getTagFromField(filed)
	if debug {
		fmt.Println("Tag is:", tag, "type is:", tps)
	}
	if tps != -1 {
		return Column{ColumnType: filed.Type, ColumnName: tag, FieldName: filed.Name, Auto: tps == 2, IsPrimary: tps == 1 || tps == 2}, tps
	} else {
		return Column{}, -1
	}

}
func getTagFromField(field reflect.StructField) (string, int) {
	tag, hasTag := field.Tag.Lookup("gom")
	if hasTag {
		if strings.EqualFold(tag, "-") || len(tag) == 0 {
			return "", -1
		} else if len(tag) == 1 {
			tps := 0
			if strings.EqualFold(tag, "@") {
				tps = 2
			}
			if strings.EqualFold(tag, "!") {
				tps = 1
			}
			return strings.ToLower(field.Name), tps
		} else {
			if strings.Contains(tag, ",") {
				tags := strings.Split(tag, ",")
				if len(tags) == 2 {
					if strings.EqualFold(tags[0], "!") || strings.EqualFold(tags[0], "primary") {
						return tags[1], 1
					} else if strings.EqualFold(tags[0], "@") || strings.EqualFold(tags[0], "auto") {
						return tags[1], 2
					} else if strings.EqualFold(tags[0], "#") || strings.EqualFold(tags[0], "column") {
						return tags[1], 0
					} else {
						return "", -1
					}
				} else {
					return "", -1
				}
			} else {
				return tag, 0
			}
		}
	} else {
		return "", -1
	}
}
func getValueOfTableRow(model TableModel, row RowChooser) reflect.Value {
	maps := getBytesMap(model, row)
	ccs := model.Columns
	vv := reflect.New(model.ModelType)
	isStruct := model.ModelType.Kind() == reflect.Struct && model.ModelType != reflect.TypeOf(time.Time{})
	for _, c := range ccs {
		var dds interface{}
		dbytes := maps[c.ColumnName]
		data := string(dbytes)
		switch c.ColumnType.Kind() {
		case reflect.Uint:
			dds, _ = UIntfromString(data)
		case reflect.Uint16:
			dds, _ = UInt16fromString(data)
		case reflect.Uint32:
			dds, _ = UInt32fromString(data)
		case reflect.Uint64:
			dds, _ = UInt64fromString(data)
		case reflect.Int:
			dds, _ = IntfromString(data)
		case reflect.Int8:
			dds, _ = Int8fromString(data)
		case reflect.Int16:
			dds, _ = Int16fromString(data)
		case reflect.Int32:
			dds, _ = Int32fromString(data)
		case reflect.Int64:
			dds, _ = Int64fromString(data)
		case reflect.Float32:
			dds, _ = Float32fromString(data)
		case reflect.Float64:
			dds, _ = Float64fromString(data)
		case reflect.String:
			dds = data
		case reflect.TypeOf([]byte{}).Kind():
			dds = dbytes
		case reflect.TypeOf(time.Time{}).Kind():
			dds, _ = TimeFromString(data)
		default:
			dds = data
		}
		if isStruct {
			vv.Elem().FieldByName(c.FieldName).Set(reflect.ValueOf(dds))
		} else {
			vv.Elem().Set(reflect.ValueOf(dds))
		}
	}
	return vv
}
func getBytesMap(model TableModel, row RowChooser) map[string][]byte {

	data := make([][]byte, len(model.Columns))
	dest := make([]interface{}, len(model.Columns)) // A temporary interface{} slice
	for i, _ := range data {
		dest[i] = &data[i] // Put pointers to each string in the interface slice
	}
	err := row.Scan(dest...)
	if err != nil {
		return map[string][]byte{}
	}
	result := make(map[string][]byte, len(model.Columns))
	ccs := model.Columns
	for i, dd := range ccs {
		result[dd.ColumnName] = data[i]
	}
	return result

}
<commit_msg>修复sql执行时参数不足的bug data...<commit_after>package gom

import (
	"fmt"
	"reflect"
	"strings"
	"time"
)

func IsEmpty(v interface{}) bool {
	times := time.Time{}
	if times == v {
		return true
	}
	if v == "" {
		return true
	}
	if v == 0 {
		return true
	}
	if v == 0.0 {
		return true
	}
	if v == nil {
		return true
	}
	return false
}
func getType(v interface{}) (reflect.Type, bool, bool) {
	tt := reflect.TypeOf(v)
	isPtr := false
	islice := false
	if tt.Kind() == reflect.Ptr {
		tt = tt.Elem()
		isPtr = true
	}
	if tt.Kind() == reflect.Slice || tt.Kind() == reflect.Array {
		tt = tt.Elem()
		islice = true
	}
	if debug {
		fmt.Println("Test getType, result:", tt, isPtr, islice)
	}
	return tt, isPtr, islice
}
func getTableModels(vs ...interface{}) []TableModel {
	tablemodels := []TableModel{}
	for _, v := range vs {
		tablemodels = append(tablemodels, getTableModel(v))
	}
	return tablemodels
}
func GetTableModel(v interface{}, names ...string) TableModel {
	tm := getTableModel(v)
	var cc []Column
	for _, ct := range tm.Columns {
		for _, name := range names {
			if ct.ColumnName == name {
				cc = append(cc, ct)
			}
		}
	}
	tm.Columns = cc
	return tm

}
func CreateSingleValueTableModel(v interface{}, table string, field string) TableModel {
	tt, _, _ := getType(v)
	vals := reflect.New(tt).Elem()
	columns := []Column{{ColumnName: field, ColumnType: tt, IsPrimary: false, Auto: false}}
	return TableModel{Columns: columns, TableName: table, ModelType: tt, ModelValue: vals}
}
func getTableModel(v interface{}) TableModel {
	if v != nil && reflect.TypeOf(v).Kind() != reflect.Interface {
		tt, isPtr, isSlice := getType(v)
		vals := reflect.ValueOf(v)
		if isPtr {
			vals = vals.Elem()
		}
		if debug {
			fmt.Println("model info:", tt, isPtr, isSlice, vals)
		}
		if tt.NumField() > 0 && tt.NumMethod() > 0 {
			nameMethod := vals.MethodByName("TableName")
			if debug {
				fmt.Println(nameMethod)
			}
			tableName := nameMethod.Call(nil)[0].String()
			columns, primary := getColumns(vals)
			ccs := []Column{primary}
			ccs = append(ccs, columns...)
			return TableModel{ModelType: tt, ModelValue: vals, Columns: ccs, TableName: tableName, Primary: primary}
		} else {
			return TableModel{}
		}
	} else {
		return TableModel{}
	}
}
func getColumns(v reflect.Value) ([]Column, Column) {
	var columns []Column
	var primary Column
	results := reflect.Indirect(reflect.ValueOf(&columns))
	oo := v.Type()
	i := 0
	for ; i < oo.NumField(); i++ {
		field := oo.Field(i)
		col, tps := getColumnFromField(field)
		if tps != -1 {
			if tps == 1 || tps == 2 {
				primary = col
			} else {
				n := reflect.Indirect(reflect.ValueOf(&col))
				if results.Kind() == reflect.Ptr {
					results.Set(reflect.Append(results, n.Addr()))
				} else {
					results.Set(reflect.Append(results, n))
				}
			}

		}
	}
	if debug {
		fmt.Println("columns is:", columns)
	}
	return columns, primary
}
func getColumnFromField(filed reflect.StructField) (Column, int) {
	tag, tps := getTagFromField(filed)
	if debug {
		fmt.Println("Tag is:", tag, "type is:", tps)
	}
	if tps != -1 {
		return Column{ColumnType: filed.Type, ColumnName: tag, FieldName: filed.Name, Auto: tps == 2, IsPrimary: tps == 1 || tps == 2}, tps
	} else {
		return Column{}, -1
	}

}
func getTagFromField(field reflect.StructField) (string, int) {
	tag, hasTag := field.Tag.Lookup("gom")
	if hasTag {
		if strings.EqualFold(tag, "-") || len(tag) == 0 {
			return "", -1
		} else if len(tag) == 1 {
			tps := 0
			if strings.EqualFold(tag, "@") {
				tps = 2
			}
			if strings.EqualFold(tag, "!") {
				tps = 1
			}
			return strings.ToLower(field.Name), tps
		} else {
			if strings.Contains(tag, ",") {
				tags := strings.Split(tag, ",")
				if len(tags) == 2 {
					if strings.EqualFold(tags[0], "!") || strings.EqualFold(tags[0], "primary") {
						return tags[1], 1
					} else if strings.EqualFold(tags[0], "@") || strings.EqualFold(tags[0], "auto") {
						return tags[1], 2
					} else if strings.EqualFold(tags[0], "#") || strings.EqualFold(tags[0], "column") {
						return tags[1], 0
					} else {
						return "", -1
					}
				} else {
					return "", -1
				}
			} else {
				return tag, 0
			}
		}
	} else {
		return "", -1
	}
}
func getValueOfTableRow(model TableModel, row RowChooser) reflect.Value {
	maps := getBytesMap(model, row)
	ccs := model.Columns
	vv := reflect.New(model.ModelType)
	isStruct := model.ModelType.Kind() == reflect.Struct && model.ModelType != reflect.TypeOf(time.Time{})
	for _, c := range ccs {
		var dds interface{}
		dbytes := maps[c.ColumnName]
		data := string(dbytes)
		switch c.ColumnType.Kind() {
		case reflect.Uint:
			dds, _ = UIntfromString(data)
		case reflect.Uint16:
			dds, _ = UInt16fromString(data)
		case reflect.Uint32:
			dds, _ = UInt32fromString(data)
		case reflect.Uint64:
			dds, _ = UInt64fromString(data)
		case reflect.Int:
			dds, _ = IntfromString(data)
		case reflect.Int8:
			dds, _ = Int8fromString(data)
		case reflect.Int16:
			dds, _ = Int16fromString(data)
		case reflect.Int32:
			dds, _ = Int32fromString(data)
		case reflect.Int64:
			dds, _ = Int64fromString(data)
		case reflect.Float32:
			dds, _ = Float32fromString(data)
		case reflect.Float64:
			dds, _ = Float64fromString(data)
		case reflect.String:
			dds = data
		case reflect.TypeOf([]byte{}).Kind():
			dds = dbytes
		case reflect.TypeOf(time.Time{}).Kind():
			dds, _ = TimeFromString(data)
		default:
			dds = data
		}
		if isStruct {
			vv.Elem().FieldByName(c.FieldName).Set(reflect.ValueOf(dds))
		} else {
			vv.Elem().Set(reflect.ValueOf(dds))
		}
	}
	return vv
}
func getBytesMap(model TableModel, row RowChooser) map[string][]byte {

	data := make([][]byte, len(model.Columns))
	dest := make([]interface{}, len(model.Columns)) // A temporary interface{} slice
	for i, _ := range data {
		dest[i] = &data[i] // Put pointers to each string in the interface slice
	}
	err := row.Scan(dest...)
	if err != nil {
		return map[string][]byte{}
	}
	result := make(map[string][]byte, len(model.Columns))
	ccs := model.Columns
	for i, dd := range ccs {
		result[dd.ColumnName] = data[i]
	}
	return result

}
<|endoftext|>