<commit_before>package permissions

import (
	"crypto/sha256"
	"errors"
	"io"
	"log"
	"math/rand"
	"net/http"
	"time"

	"github.com/xyproto/simpleredis"
)

const (
	minConfirmationCodeLength = 20   // minimum length of the confirmation code
	saltword                  = "hi" // used together with username and password when hashing
	defaultRedisServer        = ":6379"
)

type UserState struct {
	// see: http://redis.io/topics/data-types
	users        *simpleredis.HashMap        // Hash map of users, with several different fields per user ("loggedin", "confirmed", "email" etc)
	usernames    *simpleredis.Set            // A list of all usernames, for easy enumeration
	unconfirmed  *simpleredis.Set            // A list of unconfirmed usernames, for easy enumeration
	pool         *simpleredis.ConnectionPool // A connection pool for Redis
	dbindex      int                         // Redis database index
	cookieSecret string                      // Secret for storing secure cookies
	cookieTime   int64                       // How long a cookie should last, in seconds
}

// Simple way to manage user sessions, uses a pseudorandom (not random) cookie secret
func NewUserStateSimple() *UserState {
	// db index 0, initialize random generator after generating the cookie secret
	return NewUserState(0, true, defaultRedisServer)
}

// Also creates a new ConnectionPool.
// randomseed is normally true, for seeding the random number generator after generating the cookie secret
// redisHostPort can be blank, for using the local Redis instance
func NewUserState(dbindex int, randomseed bool, redisHostPort string) *UserState {
	var pool *simpleredis.ConnectionPool

	// Connnect to the default redis server if redisHostPort is empty
	if redisHostPort == "" {
		redisHostPort = defaultRedisServer
	}

	// Test connection
	if err := simpleredis.TestConnectionHost(redisHostPort); err != nil {
		log.Fatalln(err.Error())
	}
	// Aquire connection pool
	pool = simpleredis.NewConnectionPoolHost(redisHostPort)

	state := new(UserState)

	state.users = simpleredis.NewHashMap(pool, "users")
	state.users.SelectDatabase(dbindex)

	state.usernames = simpleredis.NewSet(pool, "usernames")
	state.usernames.SelectDatabase(dbindex)

	state.unconfirmed = simpleredis.NewSet(pool, "unconfirmed")
	state.unconfirmed.SelectDatabase(dbindex)

	state.pool = pool

	state.dbindex = dbindex

	// For the secure cookies
	// This must happen before the random seeding, or
	// else people will have to log in again after every server restart
	state.cookieSecret = RandomCookieFriendlyString(30)

	// Seed the random number generator
	if randomseed {
		rand.Seed(time.Now().UnixNano())
	}

	// Cookies lasts for 24 hours by default. Specified in seconds.
	state.cookieTime = defaultCookieTime

	return state
}

func (state *UserState) GetDatabaseIndex() int {
	return state.dbindex
}

func (state *UserState) GetPool() *simpleredis.ConnectionPool {
	return state.pool
}

func (state *UserState) Close() {
	state.pool.Close()
}

// Checks if the current user is logged in as a user right now
func (state *UserState) UserRights(req *http.Request) bool {
	username, err := state.GetUsernameCookie(req)
	if err != nil {
		return false
	}
	return state.IsLoggedIn(username)
}

func (state *UserState) HasUser(username string) bool {
	val, err := state.usernames.Has(username)
	if err != nil {
		// This happened at concurrent connections before introducing the connection pool
		panic("ERROR: Lost connection to Redis?")
	}
	return val
}

// Return the boolean value for a given username and fieldname.
// If the user or field is missing, false will be returned.
// Useful for states where it makes sense that the returned value is not true
// unless everything is in order.
func (state *UserState) GetBooleanField(username, fieldname string) bool {
	hasUser := state.HasUser(username)
	if !hasUser {
		return false
	}
	value, err := state.users.Get(username, fieldname)
	if err != nil {
		return false
	}
	return value == "true"
}

// Attempt to store a boolean value for the given username and fieldname.
func (state *UserState) SetBooleanField(username, fieldname string, val bool) {
	strval := "false"
	if val {
		strval = "true"
	}
	state.users.Set(username, fieldname, strval)
}

func (state *UserState) IsConfirmed(username string) bool {
	return state.GetBooleanField(username, "confirmed")
}

// Checks if the given username is logged in or not
func (state *UserState) IsLoggedIn(username string) bool {
	if !state.HasUser(username) {
		return false
	}
	status, err := state.users.Get(username, "loggedin")
	if err != nil {
		// Returns "no" if the status can not be retrieved
		return false
	}
	return status == "true"
}

// Checks if the current user is logged in as Administrator right now
func (state *UserState) AdminRights(req *http.Request) bool {
	username, err := state.GetUsernameCookie(req)
	if err != nil {
		return false
	}
	return state.IsLoggedIn(username) && state.IsAdmin(username)
}

// Checks if the given username is an administrator
func (state *UserState) IsAdmin(username string) bool {
	if !state.HasUser(username) {
		return false
	}
	status, err := state.users.Get(username, "admin")
	if err != nil {
		return false
	}
	return status == "true"
}

// Gets the username that is stored in a cookie in the browser, if available
func (state *UserState) GetUsernameCookie(req *http.Request) (string, error) {
	username, ok := GetSecureCookie(req, "user", state.cookieSecret)
	if ok && (username != "") {
		return username, nil
	}
	return "", errors.New("Could not retrieve the username from browser cookie")
}

func (state *UserState) SetUsernameCookie(w http.ResponseWriter, username string) error {
	if username == "" {
		return errors.New("Can't set cookie for empty username")
	}
	if !state.HasUser(username) {
		return errors.New("Can't store cookie for non-existsing user")
	}
	// Create a cookie that lasts for a while ("timeout" seconds),
	// this is the equivivalent of a session for a given username.
	SetSecureCookiePath(w, "user", username, state.cookieTime, "/", state.cookieSecret)
	return nil
}

func (state *UserState) GetAllUsernames() ([]string, error) {
	return state.usernames.GetAll()
}

func (state *UserState) GetEmail(username string) (string, error) {
	return state.users.Get(username, "email")
}

func (state *UserState) GetPasswordHash(username string) (string, error) {
	return state.users.Get(username, "password")
}

func (state *UserState) GetAllUnconfirmedUsernames() ([]string, error) {
	return state.unconfirmed.GetAll()
}

// Get the confirmation code for a specific user
func (state *UserState) GetConfirmationCode(username string) (string, error) {
	return state.users.Get(username, "confirmationCode")
}

func (state *UserState) GetUsers() *simpleredis.HashMap {
	return state.users
}

// Add a user that has registered but not confirmed
func (state *UserState) AddUnconfirmed(username, confirmationCode string) {
	state.unconfirmed.Add(username)
	state.users.Set(username, "confirmationCode", confirmationCode)
}

// Remove a user that has registered but not confirmed
func (state *UserState) RemoveUnconfirmed(username string) {
	state.unconfirmed.Del(username)
	state.users.DelKey(username, "confirmationCode")
}

// Mark a user as confirmed
func (state *UserState) MarkConfirmed(username string) {
	state.users.Set(username, "confirmed", "true")
}

// Remove user and login status
func (state *UserState) RemoveUser(username string) {
	state.usernames.Del(username)
	// Remove additional data as well
	state.users.DelKey(username, "loggedin")
}

// Mark user as an administrator
func (state *UserState) SetAdminStatus(username string) {
	state.users.Set(username, "admin", "true")
}

// Mark user as a regular user
func (state *UserState) RemoveAdminStatus(username string) {
	state.users.Set(username, "admin", "false")
}

// Creates a user from the username and password hash, does not check for rights
func (state *UserState) addUserUnchecked(username, passwordHash, email string) {
	// Add the user
	state.usernames.Add(username)

	// Add password and email
	state.users.Set(username, "password", passwordHash)
	state.users.Set(username, "email", email)

	// Addditional fields
	additionalfields := []string{"loggedin", "confirmed", "admin"}
	for _, fieldname := range additionalfields {
		state.users.Set(username, fieldname, "false")
	}
}

// Creates a user and hashes the password, does not check for rights.
// The given data must be valid.
func (state *UserState) AddUser(username, password, email string) {
	passwordHash := state.HashPassword(username, password)
	state.addUserUnchecked(username, passwordHash, email)
}

// Mark the user as logged in
func (state *UserState) SetLoggedIn(username string) {
	state.users.Set(username, "loggedin", "true")
}

// Mark the user as logged out
func (state *UserState) SetLoggedOut(username string) {
	state.users.Set(username, "loggedin", "false")
}

// Convenience function for logging the user in and storing the username in a cookie
func (state *UserState) Login(w http.ResponseWriter, username string) {
	state.SetLoggedIn(username)
	state.SetUsernameCookie(w, username)
}

// Convenience function for logging the user out
func (state *UserState) Logout(username string) {
	state.SetLoggedOut(username)
}

// Convenience function that will return a username or an empty string
func (state *UserState) GetUsername(req *http.Request) string {
	username, err := state.GetUsernameCookie(req)
	if err != nil {
		return ""
	}
	return username
}

// Get how long a login cookie should last, in seconds
func (state *UserState) GetCookieTimeout(username string) int64 {
	return state.cookieTime
}

// Set how long a login cookie should last, in seconds
func (state *UserState) SetCookieTimeout(cookieTime int64) {
	state.cookieTime = cookieTime
}

// Set how long a loogin cookie should last

// New password hashing function, with the username as part of the salt
func (state *UserState) HashPassword(username, password string) string {
	hasher := sha256.New()
	// Use the cookie secret as additional salt
	io.WriteString(hasher, password+state.cookieSecret+username)
	return string(hasher.Sum(nil))
}

// Check if a password is correct. username is used as part of the hash.
func (state *UserState) CorrectPassword(username, password string) bool {
	passwordHash, err := state.GetPasswordHash(username)
	if err != nil {
		return false
	}
	return passwordHash == state.HashPassword(username, password)
}

// Goes through all the confirmationCodes of all the unconfirmed users
// and checks if this confirmationCode already is in use
func (state *UserState) AlreadyHasConfirmationCode(confirmationCode string) bool {
	unconfirmedUsernames, err := state.GetAllUnconfirmedUsernames()
	if err != nil {
		return false
	}
	for _, aUsername := range unconfirmedUsernames {
		aConfirmationCode, err := state.GetConfirmationCode(aUsername)
		if err != nil {
			// If the confirmation code can not be found, that's okay too
			return false
		}
		if confirmationCode == aConfirmationCode {
			// Found it
			return true
		}
	}
	return false
}

func (state *UserState) FindUserByConfirmationCode(confirmationcode string) (string, error) {
	unconfirmedUsernames, err := state.GetAllUnconfirmedUsernames()
	if err != nil {
		return "", errors.New("All existing users are already confirmed.")
	}

	// Find the username by looking up the confirmationcode on unconfirmed users
	username := ""
	for _, aUsername := range unconfirmedUsernames {
		aConfirmationCode, err := state.GetConfirmationCode(aUsername)
		if err != nil {
			// If the confirmation code can not be found, just skip this one
			continue
		}
		if confirmationcode == aConfirmationCode {
			// Found the right user
			username = aUsername
			break
		}
	}

	// Check that the user is there
	if username == "" {
		return username, errors.New("The confirmation code is no longer valid.")
	}
	hasUser := state.HasUser(username)
	if !hasUser {
		return username, errors.New("The user that is to be confirmed no longer exists.")
	}

	return username, nil
}

// Both remove the username from the list of unconfirmed users and mark the user as confirmed
func (state *UserState) Confirm(username string) {
	// Remove from the list of unconfirmed usernames
	state.RemoveUnconfirmed(username)

	// Mark user as confirmed
	state.MarkConfirmed(username)
}

// Take a confirmation code and mark the corresponding unconfirmed user as confirmed
func (state *UserState) ConfirmUserByConfirmationCode(confirmationcode string) error {
	if username, err := state.FindUserByConfirmationCode(confirmationcode); err != nil {
		return err
	} else {
		state.Confirm(username)
	}
	return nil
}

func (state *UserState) GenerateUniqueConfirmationCode() (string, error) {
	// The confirmation code must be a minimum of 8 letters long
	length := minConfirmationCodeLength
	confirmationCode := RandomHumanFriendlyString(length)
	for state.AlreadyHasConfirmationCode(confirmationCode) {
		// Increase the length of the confirmationCode random string every time there is a collision
		length++
		confirmationCode = RandomHumanFriendlyString(length)
		if length > 100 {
			// This should never happen
			return confirmationCode, errors.New("ERROR: Too many generated confirmation codes are not unique, something is wrong")
		}
	}
	return confirmationCode, nil
}

// Check that the given username and password are different.
// Also check if the chosen letters are a-å, A-Å, 0-9 and/or _.
func ValidUsernamePassword(username, password string) error {
	const allowed_letters = "abcdefghijklmnopqrstuvwxyzæøåABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ_0123456789"
NEXT:
	for _, letter := range username {
		for _, allowedLetter := range allowed_letters {
			if letter == allowedLetter {
				continue NEXT // check the next letter in the username
			}
		}
		return errors.New("Only a-å, A-Å, 0-9 and _ are allowed in usernames.")
	}
	if username == password {
		return errors.New("Username and password must be different, try another password.")
	}
	return nil
}
<commit_msg>Made the length of the user confirmation string customizable<commit_after>package permissions

import (
	"crypto/sha256"
	"errors"
	"io"
	"log"
	"math/rand"
	"net/http"
	"time"

	"github.com/xyproto/simpleredis"
)

const (
	defaultRedisServer = ":6379"
)

var (
	minConfirmationCodeLength = 20 // minimum length of the confirmation code
)

type UserState struct {
	// see: http://redis.io/topics/data-types
	users        *simpleredis.HashMap        // Hash map of users, with several different fields per user ("loggedin", "confirmed", "email" etc)
	usernames    *simpleredis.Set            // A list of all usernames, for easy enumeration
	unconfirmed  *simpleredis.Set            // A list of unconfirmed usernames, for easy enumeration
	pool         *simpleredis.ConnectionPool // A connection pool for Redis
	dbindex      int                         // Redis database index
	cookieSecret string                      // Secret for storing secure cookies
	cookieTime   int64                       // How long a cookie should last, in seconds
}

// Simple way to manage user sessions, uses a pseudorandom (not random) cookie secret
func NewUserStateSimple() *UserState {
	// db index 0, initialize random generator after generating the cookie secret
	return NewUserState(0, true, defaultRedisServer)
}

// Also creates a new ConnectionPool.
// randomseed is normally true, for seeding the random number generator after generating the cookie secret
// redisHostPort can be blank, for using the local Redis instance
func NewUserState(dbindex int, randomseed bool, redisHostPort string) *UserState {
	var pool *simpleredis.ConnectionPool

	// Connnect to the default redis server if redisHostPort is empty
	if redisHostPort == "" {
		redisHostPort = defaultRedisServer
	}

	// Test connection
	if err := simpleredis.TestConnectionHost(redisHostPort); err != nil {
		log.Fatalln(err.Error())
	}
	// Aquire connection pool
	pool = simpleredis.NewConnectionPoolHost(redisHostPort)

	state := new(UserState)

	state.users = simpleredis.NewHashMap(pool, "users")
	state.users.SelectDatabase(dbindex)

	state.usernames = simpleredis.NewSet(pool, "usernames")
	state.usernames.SelectDatabase(dbindex)

	state.unconfirmed = simpleredis.NewSet(pool, "unconfirmed")
	state.unconfirmed.SelectDatabase(dbindex)

	state.pool = pool

	state.dbindex = dbindex

	// For the secure cookies
	// This must happen before the random seeding, or
	// else people will have to log in again after every server restart
	state.cookieSecret = RandomCookieFriendlyString(30)

	// Seed the random number generator
	if randomseed {
		rand.Seed(time.Now().UnixNano())
	}

	// Cookies lasts for 24 hours by default. Specified in seconds.
	state.cookieTime = defaultCookieTime

	return state
}

func (state *UserState) GetDatabaseIndex() int {
	return state.dbindex
}

func (state *UserState) GetPool() *simpleredis.ConnectionPool {
	return state.pool
}

func (state *UserState) Close() {
	state.pool.Close()
}

// Checks if the current user is logged in as a user right now
func (state *UserState) UserRights(req *http.Request) bool {
	username, err := state.GetUsernameCookie(req)
	if err != nil {
		return false
	}
	return state.IsLoggedIn(username)
}

func (state *UserState) HasUser(username string) bool {
	val, err := state.usernames.Has(username)
	if err != nil {
		// This happened at concurrent connections before introducing the connection pool
		panic("ERROR: Lost connection to Redis?")
	}
	return val
}

// Return the boolean value for a given username and fieldname.
// If the user or field is missing, false will be returned.
// Useful for states where it makes sense that the returned value is not true
// unless everything is in order.
func (state *UserState) GetBooleanField(username, fieldname string) bool {
	hasUser := state.HasUser(username)
	if !hasUser {
		return false
	}
	value, err := state.users.Get(username, fieldname)
	if err != nil {
		return false
	}
	return value == "true"
}

// Attempt to store a boolean value for the given username and fieldname.
func (state *UserState) SetBooleanField(username, fieldname string, val bool) {
	strval := "false"
	if val {
		strval = "true"
	}
	state.users.Set(username, fieldname, strval)
}

func (state *UserState) IsConfirmed(username string) bool {
	return state.GetBooleanField(username, "confirmed")
}

// Checks if the given username is logged in or not
func (state *UserState) IsLoggedIn(username string) bool {
	if !state.HasUser(username) {
		return false
	}
	status, err := state.users.Get(username, "loggedin")
	if err != nil {
		// Returns "no" if the status can not be retrieved
		return false
	}
	return status == "true"
}

// Checks if the current user is logged in as Administrator right now
func (state *UserState) AdminRights(req *http.Request) bool {
	username, err := state.GetUsernameCookie(req)
	if err != nil {
		return false
	}
	return state.IsLoggedIn(username) && state.IsAdmin(username)
}

// Checks if the given username is an administrator
func (state *UserState) IsAdmin(username string) bool {
	if !state.HasUser(username) {
		return false
	}
	status, err := state.users.Get(username, "admin")
	if err != nil {
		return false
	}
	return status == "true"
}

// Gets the username that is stored in a cookie in the browser, if available
func (state *UserState) GetUsernameCookie(req *http.Request) (string, error) {
	username, ok := GetSecureCookie(req, "user", state.cookieSecret)
	if ok && (username != "") {
		return username, nil
	}
	return "", errors.New("Could not retrieve the username from browser cookie")
}

func (state *UserState) SetUsernameCookie(w http.ResponseWriter, username string) error {
	if username == "" {
		return errors.New("Can't set cookie for empty username")
	}
	if !state.HasUser(username) {
		return errors.New("Can't store cookie for non-existsing user")
	}
	// Create a cookie that lasts for a while ("timeout" seconds),
	// this is the equivivalent of a session for a given username.
	SetSecureCookiePath(w, "user", username, state.cookieTime, "/", state.cookieSecret)
	return nil
}

func (state *UserState) GetAllUsernames() ([]string, error) {
	return state.usernames.GetAll()
}

func (state *UserState) GetEmail(username string) (string, error) {
	return state.users.Get(username, "email")
}

func (state *UserState) GetPasswordHash(username string) (string, error) {
	return state.users.Get(username, "password")
}

func (state *UserState) GetAllUnconfirmedUsernames() ([]string, error) {
	return state.unconfirmed.GetAll()
}

// Get the confirmation code for a specific user
func (state *UserState) GetConfirmationCode(username string) (string, error) {
	return state.users.Get(username, "confirmationCode")
}

func (state *UserState) GetUsers() *simpleredis.HashMap {
	return state.users
}

// Add a user that has registered but not confirmed
func (state *UserState) AddUnconfirmed(username, confirmationCode string) {
	state.unconfirmed.Add(username)
	state.users.Set(username, "confirmationCode", confirmationCode)
}

// Remove a user that has registered but not confirmed
func (state *UserState) RemoveUnconfirmed(username string) {
	state.unconfirmed.Del(username)
	state.users.DelKey(username, "confirmationCode")
}

// Mark a user as confirmed
func (state *UserState) MarkConfirmed(username string) {
	state.users.Set(username, "confirmed", "true")
}

// Remove user and login status
func (state *UserState) RemoveUser(username string) {
	state.usernames.Del(username)
	// Remove additional data as well
	state.users.DelKey(username, "loggedin")
}

// Mark user as an administrator
func (state *UserState) SetAdminStatus(username string) {
	state.users.Set(username, "admin", "true")
}

// Mark user as a regular user
func (state *UserState) RemoveAdminStatus(username string) {
	state.users.Set(username, "admin", "false")
}

// Creates a user from the username and password hash, does not check for rights
func (state *UserState) addUserUnchecked(username, passwordHash, email string) {
	// Add the user
	state.usernames.Add(username)

	// Add password and email
	state.users.Set(username, "password", passwordHash)
	state.users.Set(username, "email", email)

	// Addditional fields
	additionalfields := []string{"loggedin", "confirmed", "admin"}
	for _, fieldname := range additionalfields {
		state.users.Set(username, fieldname, "false")
	}
}

// Creates a user and hashes the password, does not check for rights.
// The given data must be valid.
func (state *UserState) AddUser(username, password, email string) {
	passwordHash := state.HashPassword(username, password)
	state.addUserUnchecked(username, passwordHash, email)
}

// Mark the user as logged in
func (state *UserState) SetLoggedIn(username string) {
	state.users.Set(username, "loggedin", "true")
}

// Mark the user as logged out
func (state *UserState) SetLoggedOut(username string) {
	state.users.Set(username, "loggedin", "false")
}

// Convenience function for logging the user in and storing the username in a cookie
func (state *UserState) Login(w http.ResponseWriter, username string) {
	state.SetLoggedIn(username)
	state.SetUsernameCookie(w, username)
}

// Convenience function for logging the user out
func (state *UserState) Logout(username string) {
	state.SetLoggedOut(username)
}

// Convenience function that will return a username or an empty string
func (state *UserState) GetUsername(req *http.Request) string {
	username, err := state.GetUsernameCookie(req)
	if err != nil {
		return ""
	}
	return username
}

// Get how long a login cookie should last, in seconds
func (state *UserState) GetCookieTimeout(username string) int64 {
	return state.cookieTime
}

// Set how long a login cookie should last, in seconds
func (state *UserState) SetCookieTimeout(cookieTime int64) {
	state.cookieTime = cookieTime
}

// Set how long a loogin cookie should last

// New password hashing function, with the username as part of the salt
func (state *UserState) HashPassword(username, password string) string {
	hasher := sha256.New()
	// Use the cookie secret as additional salt
	io.WriteString(hasher, password+state.cookieSecret+username)
	return string(hasher.Sum(nil))
}

// Check if a password is correct. username is used as part of the hash.
func (state *UserState) CorrectPassword(username, password string) bool {
	passwordHash, err := state.GetPasswordHash(username)
	if err != nil {
		return false
	}
	return passwordHash == state.HashPassword(username, password)
}

// Goes through all the confirmationCodes of all the unconfirmed users
// and checks if this confirmationCode already is in use
func (state *UserState) AlreadyHasConfirmationCode(confirmationCode string) bool {
	unconfirmedUsernames, err := state.GetAllUnconfirmedUsernames()
	if err != nil {
		return false
	}
	for _, aUsername := range unconfirmedUsernames {
		aConfirmationCode, err := state.GetConfirmationCode(aUsername)
		if err != nil {
			// If the confirmation code can not be found, that's okay too
			return false
		}
		if confirmationCode == aConfirmationCode {
			// Found it
			return true
		}
	}
	return false
}

func (state *UserState) FindUserByConfirmationCode(confirmationcode string) (string, error) {
	unconfirmedUsernames, err := state.GetAllUnconfirmedUsernames()
	if err != nil {
		return "", errors.New("All existing users are already confirmed.")
	}

	// Find the username by looking up the confirmationcode on unconfirmed users
	username := ""
	for _, aUsername := range unconfirmedUsernames {
		aConfirmationCode, err := state.GetConfirmationCode(aUsername)
		if err != nil {
			// If the confirmation code can not be found, just skip this one
			continue
		}
		if confirmationcode == aConfirmationCode {
			// Found the right user
			username = aUsername
			break
		}
	}

	// Check that the user is there
	if username == "" {
		return username, errors.New("The confirmation code is no longer valid.")
	}
	hasUser := state.HasUser(username)
	if !hasUser {
		return username, errors.New("The user that is to be confirmed no longer exists.")
	}

	return username, nil
}

// Both remove the username from the list of unconfirmed users and mark the user as confirmed
func (state *UserState) Confirm(username string) {
	// Remove from the list of unconfirmed usernames
	state.RemoveUnconfirmed(username)

	// Mark user as confirmed
	state.MarkConfirmed(username)
}

// Take a confirmation code and mark the corresponding unconfirmed user as confirmed
func (state *UserState) ConfirmUserByConfirmationCode(confirmationcode string) error {
	if username, err := state.FindUserByConfirmationCode(confirmationcode); err != nil {
		return err
	} else {
		state.Confirm(username)
	}
	return nil
}

// Set the minimum length of the user confirmation code. The default is 20.
func (state *UserState) SetMinimumConfirmationCodeLength(length int) {
	minConfirmationCodeLength = length
}

func (state *UserState) GenerateUniqueConfirmationCode() (string, error) {
	length := minConfirmationCodeLength
	confirmationCode := RandomHumanFriendlyString(length)
	for state.AlreadyHasConfirmationCode(confirmationCode) {
		// Increase the length of the confirmationCode random string every time there is a collision
		length++
		confirmationCode = RandomHumanFriendlyString(length)
		if length > 100 {
			// This should never happen
			return confirmationCode, errors.New("ERROR: Too many generated confirmation codes are not unique, something is wrong")
		}
	}
	return confirmationCode, nil
}

// Check that the given username and password are different.
// Also check if the chosen letters are a-å, A-Å, 0-9 and/or _.
func ValidUsernamePassword(username, password string) error {
	const allowed_letters = "abcdefghijklmnopqrstuvwxyzæøåABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ_0123456789"
NEXT:
	for _, letter := range username {
		for _, allowedLetter := range allowed_letters {
			if letter == allowedLetter {
				continue NEXT // check the next letter in the username
			}
		}
		return errors.New("Only a-å, A-Å, 0-9 and _ are allowed in usernames.")
	}
	if username == password {
		return errors.New("Username and password must be different, try another password.")
	}
	return nil
}
<|endoftext|>


<commit_before>package util

import (
	"crypto"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/mundipagg/boleto-api/certificate"

	s "github.com/fullsailor/pkcs7"
	"github.com/mundipagg/boleto-api/config"
)

var defaultDialer = &net.Dialer{Timeout: 16 * time.Second, KeepAlive: 16 * time.Second}

var (
	client    = &http.Client{}
	tlsClient = &http.Client{}
	once      = &sync.Once{}
	icpCert   certificate.ICPCertificate
	transport *http.Transport
)

// DefaultHTTPClient retorna um cliente http configurado para dar um skip na validação do certificado digital
func DefaultHTTPClient() *http.Client {
	once.Do(func() {
		client.Transport = &http.Transport{
			Dial:                defaultDialer.Dial,
			TLSHandshakeTimeout: 16 * time.Second,
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		}
	})
	return client
}

//Post faz um requisição POST para uma URL e retorna o response, status e erro
func Post(url, body, timeout string, header map[string]string) (string, int, error) {
	return doRequest("POST", url, body, timeout, header)
}

//Get faz um requisição GET para uma URL e retorna o response, status e erro
func Get(url, body, timeout string, header map[string]string) (string, int, error) {
	return doRequest("GET", url, body, timeout, header)
}

func doRequest(method, url, body, timeout string, header map[string]string) (string, int, error) {
	client := DefaultHTTPClient()
	client.Timeout = GetDurationTimeoutRequest(timeout) * time.Second
	message := strings.NewReader(body)
	req, err := http.NewRequest(method, url, message)
	if err != nil {
		return "", http.StatusInternalServerError, err
	}
	if header != nil {
		for k, v := range header {
			req.Header.Add(k, v)
		}
	}
	resp, errResp := client.Do(req)
	if errResp != nil {
		return "", 0, errResp
	}
	defer resp.Body.Close()
	data, errResponse := ioutil.ReadAll(resp.Body)
	if errResponse != nil {
		return "", resp.StatusCode, errResponse
	}
	sData := string(data)
	return sData, resp.StatusCode, nil
}

// BuildTLSTransport creates a TLS Client Transport from crt, ca and key files
func BuildTLSTransport() (*http.Transport, error) {
	var errF error
	once.Do(func() {

		ssl, err := certificate.GetCertificateFromStore(config.Get().CertificateSSLName)
		if err != nil {
			errF = err
			return
		}

		cert, err := tls.X509KeyPair(ssl.(certificate.SSLCertificate).PemData, ssl.(certificate.SSLCertificate).PemData)
		if err != nil {
			errF = err
			return
		}

		transport = &http.Transport{
			Dial:                defaultDialer.Dial,
			TLSHandshakeTimeout: 16 * time.Second,
			TLSClientConfig: &tls.Config{
				Certificates:       []tls.Certificate{cert},
				InsecureSkipVerify: true,
			},
		}
		return
	})
	return transport, errF
}

//Sign request
func SignRequest(request string) (string, error) {

	if icpCert == (certificate.ICPCertificate{}) {
		icp, err := certificate.GetCertificateFromStore(config.Get().CertificateICPName)
		if err != nil {
			return "", err
		}
		icpCert = icp.(certificate.ICPCertificate)
	}

	signedData, err := s.NewSignedData([]byte(request))
	if err != nil {
		return "", err
	}

	if err := signedData.AddSigner(icpCert.Certificate, icpCert.RsaPrivateKey, s.SignerInfoConfig{}); err != nil {
		return "", err
	}

	detachedSignature, err := signedData.Finish()
	if err != nil {
		return "", err
	}

	signedRequest := base64.StdEncoding.EncodeToString(detachedSignature)

	return signedRequest, nil
}

//Read privatekey and parse to PKCS#1
func parsePrivateKey() (crypto.PrivateKey, error) {

	pkeyBytes, err := ioutil.ReadFile(config.Get().CertICP_PathPkey)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(pkeyBytes)
	if block == nil {
		return nil, errors.New("Key Not Found")
	}

	switch block.Type {
	case "RSA PRIVATE KEY":
		rsa, err := x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			return nil, err
		}
		return rsa, nil
	default:
		return nil, fmt.Errorf("SSH: Unsupported key type %q", block.Type)
	}

}

///Read chainCertificates and adapter to x509.Certificate
func parseChainCertificates() (*x509.Certificate, error) {

	chainCertsBytes, err := ioutil.ReadFile(config.Get().CertICP_PathChainCertificates)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(chainCertsBytes)
	if block == nil {
		return nil, errors.New("Key Not Found")
	}

	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, err
	}

	return cert, nil
}

func doRequestTLS(method, url, body, timeout string, header map[string]string, transport *http.Transport) (string, int, error) {
	tlsClient.Transport = transport
	tlsClient.Timeout = GetDurationTimeoutRequest(timeout) * time.Second
	b := strings.NewReader(body)
	req, err := http.NewRequest(method, url, b)
	if err != nil {
		return "", 0, err
	}

	if header != nil {
		for k, v := range header {
			req.Header.Add(k, v)
		}
	}
	resp, err := tlsClient.Do(req)
	if err != nil {
		return "", 0, err
	}
	defer resp.Body.Close()
	// Dump response
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", 0, err
	}
	sData := string(data)
	return sData, resp.StatusCode, nil
}

func PostTLS(url, body, timeout string, header map[string]string, transport *http.Transport) (string, int, error) {
	return doRequestTLS("POST", url, body, timeout, header, transport)
}

//HeaderToMap converte um http Header para um dicionário string -> string
func HeaderToMap(h http.Header) map[string]string {
	m := make(map[string]string)
	for k, v := range h {
		m[k] = v[0]
	}
	return m
}
<commit_msg>fix instance default http.client<commit_after>package util

import (
	"context"
	"crypto"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/mundipagg/boleto-api/certificate"

	s "github.com/fullsailor/pkcs7"
	"github.com/mundipagg/boleto-api/config"
)

var defaultDialer = &net.Dialer{Timeout: 16 * time.Second, KeepAlive: 16 * time.Second}

var (
	client            *http.Client
	onceDefaultClient = &sync.Once{}
	onceTransport     = &sync.Once{}
	icpCert           certificate.ICPCertificate
	transport         *http.Transport
)

// DefaultHTTPClient retorna um cliente http configurado para dar um skip na validação do certificado digital
func DefaultHTTPClient() *http.Client {
	onceDefaultClient.Do(func() {
		client = &http.Client{
			Transport: &http.Transport{
				Dial:                defaultDialer.Dial,
				TLSHandshakeTimeout: 16 * time.Second,
				TLSClientConfig: &tls.Config{
					InsecureSkipVerify: true,
				},
			},
		}
	})
	return client
}

//Post faz um requisição POST para uma URL e retorna o response, status e erro
func Post(url, body, timeout string, header map[string]string) (string, int, error) {
	return doRequest("POST", url, body, timeout, header)
}

//Get faz um requisição GET para uma URL e retorna o response, status e erro
func Get(url, body, timeout string, header map[string]string) (string, int, error) {
	return doRequest("GET", url, body, timeout, header)
}

func doRequest(method, url, body, timeout string, header map[string]string) (string, int, error) {
	t := GetDurationTimeoutRequest(timeout) * time.Second

	ctx, cls := context.WithTimeout(context.Background(), t)
	defer cls()

	client := DefaultHTTPClient()

	message := strings.NewReader(body)

	req, err := http.NewRequestWithContext(ctx, method, url, message)
	if err != nil {
		return "", http.StatusInternalServerError, err
	}
	if header != nil {
		for k, v := range header {
			req.Header.Add(k, v)
		}
	}
	resp, errResp := client.Do(req)
	if errResp != nil {
		return "", 0, errResp
	}
	defer resp.Body.Close()
	data, errResponse := ioutil.ReadAll(resp.Body)
	if errResponse != nil {
		return "", resp.StatusCode, errResponse
	}
	sData := string(data)
	return sData, resp.StatusCode, nil
}

// BuildTLSTransport creates a TLS Client Transport from crt, ca and key files
func BuildTLSTransport() (*http.Transport, error) {
	var errF error
	onceTransport.Do(func() {

		ssl, err := certificate.GetCertificateFromStore(config.Get().CertificateSSLName)
		if err != nil {
			errF = err
			return
		}

		cert, err := tls.X509KeyPair(ssl.(certificate.SSLCertificate).PemData, ssl.(certificate.SSLCertificate).PemData)
		if err != nil {
			errF = err
			return
		}

		transport = &http.Transport{
			Dial:                defaultDialer.Dial,
			TLSHandshakeTimeout: 16 * time.Second,
			TLSClientConfig: &tls.Config{
				Certificates:       []tls.Certificate{cert},
				InsecureSkipVerify: true,
			},
		}
		return
	})
	return transport, errF
}

//Sign request
func SignRequest(request string) (string, error) {

	if icpCert == (certificate.ICPCertificate{}) {
		icp, err := certificate.GetCertificateFromStore(config.Get().CertificateICPName)
		if err != nil {
			return "", err
		}
		icpCert = icp.(certificate.ICPCertificate)
	}

	signedData, err := s.NewSignedData([]byte(request))
	if err != nil {
		return "", err
	}

	if err := signedData.AddSigner(icpCert.Certificate, icpCert.RsaPrivateKey, s.SignerInfoConfig{}); err != nil {
		return "", err
	}

	detachedSignature, err := signedData.Finish()
	if err != nil {
		return "", err
	}

	signedRequest := base64.StdEncoding.EncodeToString(detachedSignature)

	return signedRequest, nil
}

//Read privatekey and parse to PKCS#1
func parsePrivateKey() (crypto.PrivateKey, error) {

	pkeyBytes, err := ioutil.ReadFile(config.Get().CertICP_PathPkey)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(pkeyBytes)
	if block == nil {
		return nil, errors.New("Key Not Found")
	}

	switch block.Type {
	case "RSA PRIVATE KEY":
		rsa, err := x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			return nil, err
		}
		return rsa, nil
	default:
		return nil, fmt.Errorf("SSH: Unsupported key type %q", block.Type)
	}

}

///Read chainCertificates and adapter to x509.Certificate
func parseChainCertificates() (*x509.Certificate, error) {

	chainCertsBytes, err := ioutil.ReadFile(config.Get().CertICP_PathChainCertificates)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(chainCertsBytes)
	if block == nil {
		return nil, errors.New("Key Not Found")
	}

	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, err
	}

	return cert, nil
}

func doRequestTLS(method, url, body, timeout string, header map[string]string, transport *http.Transport) (string, int, error) {
	tlsClient := &http.Client{}
	tlsClient.Transport = transport
	tlsClient.Timeout = GetDurationTimeoutRequest(timeout) * time.Second
	b := strings.NewReader(body)
	req, err := http.NewRequest(method, url, b)
	if err != nil {
		return "", 0, err
	}

	if header != nil {
		for k, v := range header {
			req.Header.Add(k, v)
		}
	}
	resp, err := tlsClient.Do(req)
	if err != nil {
		return "", 0, err
	}
	defer resp.Body.Close()
	// Dump response
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", 0, err
	}
	sData := string(data)
	return sData, resp.StatusCode, nil
}

func PostTLS(url, body, timeout string, header map[string]string, transport *http.Transport) (string, int, error) {
	return doRequestTLS("POST", url, body, timeout, header, transport)
}

//HeaderToMap converte um http Header para um dicionário string -> string
func HeaderToMap(h http.Header) map[string]string {
	m := make(map[string]string)
	for k, v := range h {
		m[k] = v[0]
	}
	return m
}
<|endoftext|>


<commit_before>/*
 Copyright 2021 The GoPlus Authors (goplus.org)

 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 cl

import (
	"fmt"
	"go/token"
	"go/types"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/goplus/gox"
	"golang.org/x/mod/modfile"
	"golang.org/x/tools/go/packages"
)

// -----------------------------------------------------------------------------

func FindGoModFile(dir string) (file string, err error) {
	if dir == "" {
		dir = "."
	}
	if dir, err = filepath.Abs(dir); err != nil {
		return
	}
	for dir != "" {
		file = filepath.Join(dir, "go.mod")
		if fi, e := os.Lstat(file); e == nil && !fi.IsDir() {
			return
		}
		if dir, file = filepath.Split(strings.TrimRight(dir, "/\\")); file == "" {
			break
		}
	}
	return "", syscall.ENOENT
}

func GetModulePath(file string) (pkgPath string, err error) {
	src, err := ioutil.ReadFile(file)
	if err != nil {
		return
	}
	f, err := modfile.ParseLax(file, src, nil)
	if err != nil {
		return
	}
	return f.Module.Mod.Path, nil
}

// -----------------------------------------------------------------------------

var (
	GenGoPkg func(pkgDir string) error
	GopGo    func(cfg *packages.Config, notFounds []string) error
)

func GenGoPkgs(cfg *packages.Config, notFounds []string) (err error) {
	if GenGoPkg == nil {
		return syscall.ENOENT
	}
	file, err := FindGoModFile(cfg.Dir)
	if err != nil {
		return
	}
	root, _ := filepath.Split(file)
	pkgPath, err := GetModulePath(file)
	if err != nil {
		return
	}
	pkgPathSlash := pkgPath + "/"
	for _, notFound := range notFounds {
		if strings.HasPrefix(notFound, pkgPathSlash) || notFound == pkgPath {
			if err = GenGoPkg(root + notFound[len(pkgPath):]); err != nil {
				return
			}
		} else {
			return syscall.ENOENT
		}
	}
	return nil
}

func LoadGop(cfg *packages.Config, patterns ...string) ([]*packages.Package, error) {
retry:
	loadPkgs, err := packages.Load(cfg, patterns...)
	if err == nil && GenGoPkg != nil {
		var notFounds []string
		packages.Visit(loadPkgs, nil, func(pkg *packages.Package) {
			const goGetCmd = "go get "
			for _, err := range pkg.Errors {
				if pos := strings.LastIndex(err.Msg, goGetCmd); pos > 0 {
					notFounds = append(notFounds, err.Msg[pos+len(goGetCmd):])
				}
			}
		})
		if notFounds != nil {
			gopGenGo := GopGo
			if gopGenGo == nil {
				gopGenGo = GenGoPkgs
			}
			if e := gopGenGo(cfg, notFounds); e == nil {
				goto retry
			}
		}
	}
	return loadPkgs, err
}

// LoadGopPkgs loads and returns the Go+ packages named by the given pkgPaths.
func LoadGopPkgs(at *gox.Package, importPkgs map[string]*gox.PkgRef, pkgPaths ...string) int {
	conf := at.InternalGetLoadConfig()
	loadPkgs, err := LoadGop(conf, pkgPaths...)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}
	if n := packages.PrintErrors(loadPkgs); n > 0 {
		return n
	}
	for _, loadPkg := range loadPkgs {
		gox.LoadGoPkg(at, importPkgs, loadPkg)
	}
	return 0
}

// -----------------------------------------------------------------------------

func initGopBuiltin(pkg gox.PkgImporter, builtin *types.Package) {
	big := pkg.Import("github.com/goplus/gop/builtin")
	big.Ref("Gop_bigint")
	scope := big.Types.Scope()
	for i, n := 0, scope.Len(); i < n; i++ {
		names := scope.Names()
		for _, name := range names {
			builtin.Scope().Insert(scope.Lookup(name))
		}
	}
}

func initBuiltin(pkg gox.PkgImporter, builtin *types.Package) {
	fmt := pkg.Import("fmt")
	fns := []string{"print", "println", "printf", "errorf", "fprint", "fprintln", "fprintf"}
	for _, fn := range fns {
		fnTitle := string(fn[0]-'a'+'A') + fn[1:]
		builtin.Scope().Insert(gox.NewOverloadFunc(token.NoPos, builtin, fn, fmt.Ref(fnTitle)))
	}
}

func newBuiltinDefault(pkg gox.PkgImporter, prefix string, contracts *gox.BuiltinContracts) *types.Package {
	builtin := types.NewPackage("", "")
	initBuiltin(pkg, builtin)
	initGopBuiltin(pkg, builtin)
	gox.InitBuiltinOps(builtin, prefix, contracts)
	gox.InitBuiltinFuncs(builtin)
	return builtin
}

// -----------------------------------------------------------------------------
<commit_msg>newBuiltinDefault: merge load pkgs<commit_after>/*
 Copyright 2021 The GoPlus Authors (goplus.org)

 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 cl

import (
	"fmt"
	"go/token"
	"go/types"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/goplus/gox"
	"golang.org/x/mod/modfile"
	"golang.org/x/tools/go/packages"
)

// -----------------------------------------------------------------------------

func FindGoModFile(dir string) (file string, err error) {
	if dir == "" {
		dir = "."
	}
	if dir, err = filepath.Abs(dir); err != nil {
		return
	}
	for dir != "" {
		file = filepath.Join(dir, "go.mod")
		if fi, e := os.Lstat(file); e == nil && !fi.IsDir() {
			return
		}
		if dir, file = filepath.Split(strings.TrimRight(dir, "/\\")); file == "" {
			break
		}
	}
	return "", syscall.ENOENT
}

func GetModulePath(file string) (pkgPath string, err error) {
	src, err := ioutil.ReadFile(file)
	if err != nil {
		return
	}
	f, err := modfile.ParseLax(file, src, nil)
	if err != nil {
		return
	}
	return f.Module.Mod.Path, nil
}

// -----------------------------------------------------------------------------

var (
	GenGoPkg func(pkgDir string) error
	GopGo    func(cfg *packages.Config, notFounds []string) error
)

func GenGoPkgs(cfg *packages.Config, notFounds []string) (err error) {
	if GenGoPkg == nil {
		return syscall.ENOENT
	}
	file, err := FindGoModFile(cfg.Dir)
	if err != nil {
		return
	}
	root, _ := filepath.Split(file)
	pkgPath, err := GetModulePath(file)
	if err != nil {
		return
	}
	pkgPathSlash := pkgPath + "/"
	for _, notFound := range notFounds {
		if strings.HasPrefix(notFound, pkgPathSlash) || notFound == pkgPath {
			if err = GenGoPkg(root + notFound[len(pkgPath):]); err != nil {
				return
			}
		} else {
			return syscall.ENOENT
		}
	}
	return nil
}

func LoadGop(cfg *packages.Config, patterns ...string) ([]*packages.Package, error) {
retry:
	loadPkgs, err := packages.Load(cfg, patterns...)
	if err == nil && GenGoPkg != nil {
		var notFounds []string
		packages.Visit(loadPkgs, nil, func(pkg *packages.Package) {
			const goGetCmd = "go get "
			for _, err := range pkg.Errors {
				if pos := strings.LastIndex(err.Msg, goGetCmd); pos > 0 {
					notFounds = append(notFounds, err.Msg[pos+len(goGetCmd):])
				}
			}
		})
		if notFounds != nil {
			gopGenGo := GopGo
			if gopGenGo == nil {
				gopGenGo = GenGoPkgs
			}
			if e := gopGenGo(cfg, notFounds); e == nil {
				goto retry
			}
		}
	}
	return loadPkgs, err
}

// LoadGopPkgs loads and returns the Go+ packages named by the given pkgPaths.
func LoadGopPkgs(at *gox.Package, importPkgs map[string]*gox.PkgRef, pkgPaths ...string) int {
	conf := at.InternalGetLoadConfig()
	loadPkgs, err := LoadGop(conf, pkgPaths...)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}
	if n := packages.PrintErrors(loadPkgs); n > 0 {
		return n
	}
	for _, loadPkg := range loadPkgs {
		gox.LoadGoPkg(at, importPkgs, loadPkg)
	}
	return 0
}

// -----------------------------------------------------------------------------

func initGopBuiltin(pkg gox.PkgImporter, builtin *types.Package, big *gox.PkgRef) {
	big.Ref("Gop_bigint")
	scope := big.Types.Scope()
	for i, n := 0, scope.Len(); i < n; i++ {
		names := scope.Names()
		for _, name := range names {
			builtin.Scope().Insert(scope.Lookup(name))
		}
	}
}

func initBuiltin(pkg gox.PkgImporter, builtin *types.Package, fmt *gox.PkgRef) {
	fns := []string{"print", "println", "printf", "errorf", "fprint", "fprintln", "fprintf"}
	for _, fn := range fns {
		fnTitle := string(fn[0]-'a'+'A') + fn[1:]
		builtin.Scope().Insert(gox.NewOverloadFunc(token.NoPos, builtin, fn, fmt.Ref(fnTitle)))
	}
}

func newBuiltinDefault(pkg gox.PkgImporter, prefix string, contracts *gox.BuiltinContracts) *types.Package {
	builtin := types.NewPackage("", "")
	fmt := pkg.Import("fmt")
	big := pkg.Import("github.com/goplus/gop/builtin")
	initBuiltin(pkg, builtin, fmt)
	initGopBuiltin(pkg, builtin, big)
	gox.InitBuiltinOps(builtin, prefix, contracts)
	gox.InitBuiltinFuncs(builtin)
	return builtin
}

// -----------------------------------------------------------------------------
<|endoftext|>


<commit_before>/*
Package log provides provides a minimal interface for structured logging
*/
package log

import (
	"log"
	"os"

	gklog "github.com/go-kit/kit/log"
	gklevels "github.com/go-kit/kit/log/levels"
)

const (
	loggerFormatJSON    = "json"
	loggerFormatLogFmt  = "logfmt"
)

// Logger provides provides a minimal interface for structured logging
// It supplies leveled logging functionw which create a log event from keyvals,
// a variadic sequence of alternating keys and values.
type Logger interface {
	Debug(keyvals ...interface{})
	Info(keyvals ...interface{})
	Error(keyvals ...interface{})
	Warn(keyvals ...interface{})
	Crit(keyvals ...interface{})

	With(keyvals ...interface{}) Logger
}

// newLogger takes the name of the file and format of the logger as an argument, creates the file and returns
// a leveled logger that logs to the file.
// @format can have values logfmt or json. Default value is logfmt.
func newLogger(file string, format string) Logger {
	fw, err := GetFile(file)
	if err != nil {
		log.Fatal("error opening log file",err)
	}

	var l log.Logger
	if format == loggerFormatJSON{
		l = gklog.NewJSONLogger(fw)
	} else {
		l = gklog.NewLogfmtLogger(fw)
	}

	kitlevels := gklevels.New(
		l,

		// Fudge values so that switching between debug/info levels does not
		// mess with the log justification
		gklevels.DebugValue("dbug"),
		gklevels.ErrorValue("errr"),
	)

	kitlevels = kitlevels.With("ts", gklog.DefaultTimestampUTC)

	return levels{kitlevels}
}

//Return a Json Logger
func NewJsonLogger(fle string) Logger {
	return newLogger(fle,loggerFormatJSON)
}

//Return a Fmt Logger
func NewLogfmtLogger(fle string) Logger {
	return newLogger(fle,loggerFormatLogFmt)
}
//GetFile opens a file in read/write to append data to it
func GetFile(name string) (*os.File, error) {
	return os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
}

type levels struct {
	kit gklevels.Levels
}

func (l levels) Debug(keyvals ...interface{}) {
	if err := l.kit.Debug().Log(keyvals...); err != nil {
		log.Println("Error while logging(debug):",err)
	}
}

func (l levels) Info(keyvals ...interface{}) {
	if err := l.kit.Info().Log(keyvals...); err != nil {
		log.Println("Error while logging(info):",err)
	}
}

func (l levels) Error(keyvals ...interface{}) {
	if err := l.kit.Error().Log(keyvals...); err != nil {
		log.Println("Error while logging(error):",err)
	}
}
func (l levels) Warn(keyvals ...interface{}) {
	if err := l.kit.Warn().Log(keyvals...); err != nil {
		log.Println("Error while logging(warn):",err)
	}
}
func (l levels) Crit(keyvals ...interface{}) {
	if err := l.kit.Crit().Log(keyvals...); err != nil {
		log.Println("Error while logging(crit):",err)
	}
}

func (l levels) With(keyvals ...interface{}) Logger {
	return levels{l.kit.With(keyvals...)}
}
<commit_msg>typo fix<commit_after>/*
Package log provides provides a minimal interface for structured logging
*/
package log

import (
	"log"
	"os"

	gklog "github.com/go-kit/kit/log"
	gklevels "github.com/go-kit/kit/log/levels"
)

const (
	loggerFormatJSON   = "json"
	loggerFormatLogFmt = "logfmt"
)

// Logger provides provides a minimal interface for structured logging
// It supplies leveled logging functionw which create a log event from keyvals,
// a variadic sequence of alternating keys and values.
type Logger interface {
	Debug(keyvals ...interface{})
	Info(keyvals ...interface{})
	Error(keyvals ...interface{})
	Warn(keyvals ...interface{})
	Crit(keyvals ...interface{})

	With(keyvals ...interface{}) Logger
}

// newLogger takes the name of the file and format of the logger as an argument, creates the file and returns
// a leveled logger that logs to the file.
// @format can have values logfmt or json. Default value is logfmt.
func newLogger(file string, format string) Logger {
	fw, err := GetFile(file)
	if err != nil {
		log.Fatal("error opening log file", err)
	}

	var l gklog.Logger
	if format == loggerFormatJSON {
		l = gklog.NewJSONLogger(fw)
	} else {
		l = gklog.NewLogfmtLogger(fw)
	}

	kitlevels := gklevels.New(
		l,

		// Fudge values so that switching between debug/info levels does not
		// mess with the log justification
		gklevels.DebugValue("dbug"),
		gklevels.ErrorValue("errr"),
	)

	kitlevels = kitlevels.With("ts", gklog.DefaultTimestampUTC)

	return levels{kitlevels}
}

//Return a Json Logger
func NewJsonLogger(fle string) Logger {
	return newLogger(fle, loggerFormatJSON)
}

//Return a Fmt Logger
func NewLogfmtLogger(fle string) Logger {
	return newLogger(fle, loggerFormatLogFmt)
}

//GetFile opens a file in read/write to append data to it
func GetFile(name string) (*os.File, error) {
	return os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
}

type levels struct {
	kit gklevels.Levels
}

func (l levels) Debug(keyvals ...interface{}) {
	if err := l.kit.Debug().Log(keyvals...); err != nil {
		log.Println("Error while logging(debug):", err)
	}
}

func (l levels) Info(keyvals ...interface{}) {
	if err := l.kit.Info().Log(keyvals...); err != nil {
		log.Println("Error while logging(info):", err)
	}
}

func (l levels) Error(keyvals ...interface{}) {
	if err := l.kit.Error().Log(keyvals...); err != nil {
		log.Println("Error while logging(error):", err)
	}
}
func (l levels) Warn(keyvals ...interface{}) {
	if err := l.kit.Warn().Log(keyvals...); err != nil {
		log.Println("Error while logging(warn):", err)
	}
}
func (l levels) Crit(keyvals ...interface{}) {
	if err := l.kit.Crit().Log(keyvals...); err != nil {
		log.Println("Error while logging(crit):", err)
	}
}

func (l levels) With(keyvals ...interface{}) Logger {
	return levels{l.kit.With(keyvals...)}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"syscall"
	"time"
)

const (
	resultsDir = "results"

	modelFilename    = "model.zpl"
	solutionFilename = "scip.sol"
	outputFilename   = "output.log"

	queueLength = 1024
)

var (
	timeLimitSec  = flag.Int("time", 3*60, "SCIP time limit (s)")
	memoryLimitMB = flag.Int("mem", 100, "SCIP memory limit (MB)")
	sleepTime     = flag.Int("sleep", 100, "sleep before redirect to results (ms)")
	address       = flag.String("address", ":8080", "hostname:port of server")
	processLimit  = flag.Int("processes", 4, "limit on number of SCIP processes")
	scipExec      = flag.String("scipExec", "scip", "(path to) scip executable")
)

// a job is identified by the path to run in
type Job struct {
	dir string
}

// channel-based semaphore
type Sem chan int

// the solveHandler submits jobs here, the workers get them
var queue = make(chan Job, queueLength)

// take jobs from queue and start processes
// cf. github.com/golang/go/wiki/BoundingResourceUse
func processQueue(sem Sem) {
	for {
		sem <- 1 // block until there's capacity to start a new process
		job := <-queue
		go runSolver(job, sem) // don't wait for solver to finish.
	}
}

// run subprocess and wait to finish
func runSolver(job Job, sem Sem) {
	if _, err := os.Stat(job.dir); os.IsNotExist(err) {
		return
	}

	outputFile, err := os.Create(path.Join(job.dir, outputFilename))
	if err != nil {
		return
	}
	defer outputFile.Close()

	commands := fmt.Sprintf("set limits time %d "+
		"set limits memory %d "+
		"read %s "+
		"optimize "+
		"display statistics "+
		"write solution %s "+
		"quit",
		*timeLimitSec, *memoryLimitMB,
		modelFilename, solutionFilename)
	cmd := exec.Command(*scipExec, "-c", commands)
	cmd.Dir = job.dir
	cmd.Stdout = outputFile
	cmd.Stderr = outputFile

	if err := cmd.Start(); err != nil {
		log.Printf("Solver in %s failed to start.", job.dir)
	}
	log.Printf("Solver in %s started with PID %d", job.dir, cmd.Process.Pid)

	if err := cmd.Wait(); err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			// The program has exited with an exit code != 0
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				log.Printf("Solver in %s failed with code %d.",
					job.dir, status.ExitStatus())
				fmt.Fprintf(outputFile, "Terminated with return code %d!",
					status.ExitStatus())
			}
		} else {
			log.Print("Solver in %s failed: %v", job.dir, err)
		}
	}

	log.Printf("Solver finished in %s", job.dir)

	<-sem // done, resource freed
}

// submit job to queue
func submit(job Job) (err error) {
	if _, err := os.Stat(job.dir); os.IsNotExist(err) {
		return err
	}

	log.Printf("Submitting job for %s", job.dir)

	// submit job to queue, never block
	go func() {
		queue <- job
	}()

	return nil
}

type Input struct {
	Model string
}

func inputHandler(w http.ResponseWriter, r *http.Request) {
	// optional prefilled model text
	prefilled := r.FormValue("prefilled")
	input := &Result{Model: prefilled}

	err := inputTemplate.Execute(w, input)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func solveHandler(w http.ResponseWriter, r *http.Request) {
	model := r.FormValue("model")
	hash := fmt.Sprintf("%x", sha1.Sum([]byte(model)))

	log.Printf("Request for model %s", hash)

	dir := path.Join(resultsDir, hash)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err = os.MkdirAll(dir, 0755)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		filename := path.Join(dir, modelFilename)
		err = ioutil.WriteFile(filename, []byte(model), 0644)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		err = submit(Job{dir})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	// add short sleep so that result might already exist when we
	// finally redirect.
	time.Sleep(time.Duration(*sleepTime) * time.Millisecond)

	http.Redirect(w, r, "/result/"+hash, http.StatusFound)
}

type Result struct {
	Hash     string
	Model    string
	Solution string
	Output   string
}

func resultHandler(w http.ResponseWriter, r *http.Request) {
	hash := r.URL.Path[len("/result/"):]

	dir := path.Join(resultsDir, hash)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	res := &Result{Hash: hash, Model: "", Solution: "", Output: ""}

	model := path.Join(dir, modelFilename)
	if _, err := os.Stat(model); err == nil {
		content, err := ioutil.ReadFile(model)
		if err == nil {
			res.Model = string(content)
		}
	}

	sol := path.Join(dir, solutionFilename)
	if _, err := os.Stat(sol); err == nil {
		content, err := ioutil.ReadFile(sol)
		if err == nil {
			res.Solution = string(content)
		}
	}

	out := path.Join(dir, outputFilename)
	if _, err := os.Stat(out); err == nil {
		content, err := ioutil.ReadFile(out)
		if err == nil {
			res.Output = string(content)
		}
	}

	if err := resultTemplate.Execute(w, res); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func main() {
	flag.Parse()

	// check that solver exists
	scipPath, err := exec.LookPath(*scipExec)
	if err != nil {
		log.Fatalf("No solver executable found at %s!", *scipExec)
	} else {
		log.Printf("Using solver executable at %s.", scipPath)
	}

	// semaphore for number of go routines starting processes
	var sem = make(Sem, *processLimit)
	go processQueue(sem)

	http.HandleFunc("/", inputHandler)
	http.HandleFunc("/input/", inputHandler)
	http.HandleFunc("/solve/", solveHandler)
	http.HandleFunc("/result/", resultHandler)
	http.Handle("/static/",
		http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

	log.Printf("listening on %s", *address)
	log.Fatal(http.ListenAndServe(*address, nil))
}
<commit_msg>fix comments according to go-lint<commit_after>package main

import (
	"crypto/sha1"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"syscall"
	"time"
)

const (
	resultsDir = "results"

	modelFilename    = "model.zpl"
	solutionFilename = "scip.sol"
	outputFilename   = "output.log"

	queueLength = 1024
)

var (
	timeLimitSec  = flag.Int("time", 3*60, "SCIP time limit (s)")
	memoryLimitMB = flag.Int("mem", 100, "SCIP memory limit (MB)")
	sleepTime     = flag.Int("sleep", 100, "sleep before redirect to results (ms)")
	address       = flag.String("address", ":8080", "hostname:port of server")
	processLimit  = flag.Int("processes", 4, "limit on number of SCIP processes")
	scipExec      = flag.String("scipExec", "scip", "(path to) scip executable")
)

// A Job is identified by the path to run in
type Job struct {
	dir string
}

// Sem is a channel-based semaphore
type Sem chan int

// the solveHandler submits jobs here, the workers get them
var queue = make(chan Job, queueLength)

// take jobs from queue and start processes
// cf. github.com/golang/go/wiki/BoundingResourceUse
func processQueue(sem Sem) {
	for {
		sem <- 1 // block until there's capacity to start a new process
		job := <-queue
		go runSolver(job, sem) // don't wait for solver to finish.
	}
}

// run subprocess and wait to finish
func runSolver(job Job, sem Sem) {
	if _, err := os.Stat(job.dir); os.IsNotExist(err) {
		return
	}

	outputFile, err := os.Create(path.Join(job.dir, outputFilename))
	if err != nil {
		return
	}
	defer outputFile.Close()

	commands := fmt.Sprintf("set limits time %d "+
		"set limits memory %d "+
		"read %s "+
		"optimize "+
		"display statistics "+
		"write solution %s "+
		"quit",
		*timeLimitSec, *memoryLimitMB,
		modelFilename, solutionFilename)
	cmd := exec.Command(*scipExec, "-c", commands)
	cmd.Dir = job.dir
	cmd.Stdout = outputFile
	cmd.Stderr = outputFile

	if err := cmd.Start(); err != nil {
		log.Printf("Solver in %s failed to start.", job.dir)
	}
	log.Printf("Solver in %s started with PID %d", job.dir, cmd.Process.Pid)

	if err := cmd.Wait(); err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			// The program has exited with an exit code != 0
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				log.Printf("Solver in %s failed with code %d.",
					job.dir, status.ExitStatus())
				fmt.Fprintf(outputFile, "Terminated with return code %d!",
					status.ExitStatus())
			}
		} else {
			log.Print("Solver in %s failed: %v", job.dir, err)
		}
	}

	log.Printf("Solver finished in %s", job.dir)

	<-sem // done, resource freed
}

// submit job to queue
func submit(job Job) (err error) {
	if _, err := os.Stat(job.dir); os.IsNotExist(err) {
		return err
	}

	log.Printf("Submitting job for %s", job.dir)

	// submit job to queue, never block
	go func() {
		queue <- job
	}()

	return nil
}

// The Input contains the whole model text.
type Input struct {
	Model string
}

func inputHandler(w http.ResponseWriter, r *http.Request) {
	// optional prefilled model text
	prefilled := r.FormValue("prefilled")
	input := &Result{Model: prefilled}

	err := inputTemplate.Execute(w, input)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func solveHandler(w http.ResponseWriter, r *http.Request) {
	model := r.FormValue("model")
	hash := fmt.Sprintf("%x", sha1.Sum([]byte(model)))

	log.Printf("Request for model %s", hash)

	dir := path.Join(resultsDir, hash)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err = os.MkdirAll(dir, 0755)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		filename := path.Join(dir, modelFilename)
		err = ioutil.WriteFile(filename, []byte(model), 0644)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		err = submit(Job{dir})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	// add short sleep so that result might already exist when we
	// finally redirect.
	time.Sleep(time.Duration(*sleepTime) * time.Millisecond)

	http.Redirect(w, r, "/result/"+hash, http.StatusFound)
}

// The Result contains several segments of text, including the input,
// log and solution
type Result struct {
	Hash     string
	Model    string
	Solution string
	Output   string
}

func resultHandler(w http.ResponseWriter, r *http.Request) {
	hash := r.URL.Path[len("/result/"):]

	dir := path.Join(resultsDir, hash)
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	res := &Result{Hash: hash, Model: "", Solution: "", Output: ""}

	model := path.Join(dir, modelFilename)
	if _, err := os.Stat(model); err == nil {
		content, err := ioutil.ReadFile(model)
		if err == nil {
			res.Model = string(content)
		}
	}

	sol := path.Join(dir, solutionFilename)
	if _, err := os.Stat(sol); err == nil {
		content, err := ioutil.ReadFile(sol)
		if err == nil {
			res.Solution = string(content)
		}
	}

	out := path.Join(dir, outputFilename)
	if _, err := os.Stat(out); err == nil {
		content, err := ioutil.ReadFile(out)
		if err == nil {
			res.Output = string(content)
		}
	}

	if err := resultTemplate.Execute(w, res); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func main() {
	flag.Parse()

	// check that solver exists
	scipPath, err := exec.LookPath(*scipExec)
	if err != nil {
		log.Fatalf("No solver executable found at %s!", *scipExec)
	} else {
		log.Printf("Using solver executable at %s.", scipPath)
	}

	// semaphore for number of go routines starting processes
	var sem = make(Sem, *processLimit)
	go processQueue(sem)

	http.HandleFunc("/", inputHandler)
	http.HandleFunc("/input/", inputHandler)
	http.HandleFunc("/solve/", solveHandler)
	http.HandleFunc("/result/", resultHandler)
	http.Handle("/static/",
		http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

	log.Printf("listening on %s", *address)
	log.Fatal(http.ListenAndServe(*address, nil))
}
<|endoftext|>


<commit_before>package util

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/bmatcuk/doublestar"
	"github.com/mgutz/ansi"
	"github.com/op/go-logging"
	"gopkg.in/coryb/yaml.v2"
)

var log = logging.MustGetLogger("util")

func FindParentPaths(fileName string) []string {
	cwd, _ := os.Getwd()

	paths := make([]string, 0)

	// special case if homedir is not in current path then check there anyway
	homedir := os.Getenv("HOME")
	if !strings.HasPrefix(cwd, homedir) {
		file := fmt.Sprintf("%s/%s", homedir, fileName)
		if _, err := os.Stat(file); err == nil {
			paths = append(paths, file)
		}
	}

	var dir string
	for _, part := range strings.Split(cwd, string(os.PathSeparator)) {
		if dir == "/" {
			dir = fmt.Sprintf("/%s", part)
		} else {
			dir = fmt.Sprintf("%s/%s", dir, part)
		}
		file := fmt.Sprintf("%s/%s", dir, fileName)
		if _, err := os.Stat(file); err == nil {
			paths = append(paths, file)
		}
	}
	return paths
}

func FindClosestParentPath(fileName string) (string, error) {
	paths := FindParentPaths(fileName)
	if len(paths) > 0 {
		return paths[len(paths)-1], nil
	}
	return "", errors.New(fmt.Sprintf("%s not found in parent directory hierarchy", fileName))
}

func ReadFile(file string) string {
	var bytes []byte
	var err error
	if bytes, err = ioutil.ReadFile(file); err != nil {
		log.Error("Failed to read file %s: %s", file, err)
		os.Exit(1)
	}
	return string(bytes)
}

func CopyFile(src, dst string) (err error) {
	var s, d *os.File
	if s, err = os.Open(src); err == nil {
		defer s.Close()
		if d, err = os.Create(dst); err == nil {
			if _, err = io.Copy(d, s); err != nil {
				d.Close()
				return
			}
			return d.Close()
		}
	}
	return
}

func FuzzyAge(start string) (string, error) {
	if t, err := time.Parse("2006-01-02T15:04:05.000-0700", start); err != nil {
		return "", err
	} else {
		delta := time.Now().Sub(t)
		if delta.Minutes() < 2 {
			return "a minute", nil
		} else if dm := delta.Minutes(); dm < 45 {
			return fmt.Sprintf("%d minutes", int(dm)), nil
		} else if dm := delta.Minutes(); dm < 90 {
			return "an hour", nil
		} else if dh := delta.Hours(); dh < 24 {
			return fmt.Sprintf("%d hours", int(dh)), nil
		} else if dh := delta.Hours(); dh < 48 {
			return "a day", nil
		} else {
			return fmt.Sprintf("%d days", int(delta.Hours()/24)), nil
		}
	}
	return "unknown", nil
}

func RunTemplate(templateContent string, data interface{}, out io.Writer) error {

	if out == nil {
		out = os.Stdout
	}

	funcs := map[string]interface{}{
		"toJson": func(content interface{}) (string, error) {
			if bytes, err := json.MarshalIndent(content, "", "    "); err != nil {
				return "", err
			} else {
				return string(bytes), nil
			}
		},
		"toYAML": func(content interface{}) (string, error) {
			if bytes, err := yaml.Marshal(content); err != nil {
				return "", err
			} else {
				return string(bytes), nil
			}
		},
		"append": func(more string, content interface{}) (string, error) {
			switch value := content.(type) {
			case string:
				return string(append([]byte(content.(string)), []byte(more)...)), nil
			case []byte:
				return string(append(content.([]byte), []byte(more)...)), nil
			default:
				return "", errors.New(fmt.Sprintf("Unknown type: %s", value))
			}
		},
		"indent": func(spaces int, content string) string {
			indent := make([]rune, spaces+1, spaces+1)
			indent[0] = '\n'
			for i := 1; i < spaces+1; i += 1 {
				indent[i] = ' '
			}

			lineSeps := []rune{'\n', '\u0085', '\u2028', '\u2029'}
			for _, sep := range lineSeps {
				indent[0] = sep
				content = strings.Replace(content, string(sep), string(indent), -1)
			}
			return content

		},
		"comment": func(content string) string {
			lineSeps := []rune{'\n', '\u0085', '\u2028', '\u2029'}
			for _, sep := range lineSeps {
				content = strings.Replace(content, string(sep), string([]rune{sep, '#', ' '}), -1)
			}
			return content
		},
		"color": func(color string) string {
			return ansi.ColorCode(color)
		},
		"split": func(sep string, content string) []string {
			return strings.Split(content, sep)
		},
		"abbrev": func(max int, content string) string {
			if len(content) > max {
				var buffer bytes.Buffer
				buffer.WriteString(content[:max-3])
				buffer.WriteString("...")
				return buffer.String()
			}
			return content
		},
		"rep": func(count int, content string) string {
			var buffer bytes.Buffer
			for i := 0; i < count; i += 1 {
				buffer.WriteString(content)
			}
			return buffer.String()
		},
		"age": func(content string) (string, error) {
			return FuzzyAge(content)
		},
		"toUpper": func(content string) string {
			return strings.ToUpper(content)
		},
		"toLower": func(content string) string {
			return strings.ToLower(content)
		},
		"ftime": func(format string, t int64) string {
			return time.Unix(t, 0).Format(format)
		},
		"env": func(name string) string {
			return os.Getenv(name)
		},
		"hasPrefix": func(content, prefix string) bool {
			return strings.HasPrefix(content, prefix)
		},
		"hasSuffix": func(content, suffix string) bool {
			return strings.HasSuffix(content, suffix)
		},
		"trimPrefix": func(content, prefix string) string {
			return strings.TrimPrefix(content, prefix)
		},
		"trimSuffix": func(content, suffix string) string {
			return strings.TrimSuffix(content, suffix)
		},
		"cwd": func() (string, error) {
			return os.Getwd()
		},
		"findLatestFile": func(glob string) (string, error) {
			matches, err := doublestar.Glob(glob)
			if err != nil {
				return "", err
			}
			artifact := struct {
				Path string
				Info os.FileInfo
			}{}
			for _, matchPath := range matches {
				info, err := os.Stat(matchPath)
				if err != nil {
					return "", err
				}
				if artifact.Path == "" {
					artifact.Info = info
					artifact.Path = matchPath
				} else if artifact.Info.ModTime().Before(info.ModTime()) {
					artifact.Info = info
					artifact.Path = matchPath
				}
			}
			if artifact.Path == "" {
				return "", fmt.Errorf("No file found matching glob: %s", glob)
			}
			return artifact.Path, nil
		},
	}
	if tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent); err != nil {
		log.Error("Failed to parse template: %s", err)
		return err
	} else {
		if err := tmpl.Execute(out, data); err != nil {
			log.Error("Failed to execute template: %s", err)
			return err
		}
	}
	return nil
}

func ResponseToJson(resp *http.Response, err error) (interface{}, error) {
	if err != nil {
		return nil, err
	}

	data := JsonDecode(resp.Body)
	if resp.StatusCode == 400 {
		if val, ok := data.(map[string]interface{})["errorMessages"]; ok {
			for _, errMsg := range val.([]interface{}) {
				log.Error("%s", errMsg)
			}
		}
	}

	return data, nil
}

func JsonDecode(io io.Reader) interface{} {
	content, err := ioutil.ReadAll(io)
	var data interface{}
	err = json.Unmarshal(content, &data)
	if err != nil {
		log.Error("JSON Parse Error: %s from %s", err, content)
	}
	return data
}

func JsonEncode(data interface{}) (string, error) {
	buffer := bytes.NewBuffer(make([]byte, 0))
	enc := json.NewEncoder(buffer)

	err := enc.Encode(data)
	if err != nil {
		log.Error("Failed to encode data %s: %s", data, err)
		return "", err
	}
	return buffer.String(), nil
}

func JsonWrite(file string, data interface{}) {
	fh, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	defer fh.Close()
	if err != nil {
		log.Error("Failed to open %s: %s", file, err)
		os.Exit(1)
	}
	enc := json.NewEncoder(fh)
	enc.Encode(data)
}

func PromptYN(prompt string, yes bool) bool {
	reader := bufio.NewReader(os.Stdin)
	if !yes {
		prompt = fmt.Sprintf("%s [y/N]: ", prompt)
	} else {
		prompt = fmt.Sprintf("%s [Y/n]: ", prompt)
	}

	fmt.Printf("%s", prompt)
	text, _ := reader.ReadString('\n')
	ans := strings.ToLower(strings.TrimRight(text, "\n"))
	if ans == "" {
		return yes
	}
	if strings.HasPrefix(ans, "y") {
		return true
	}
	return false
}

func Prompt(prompt string) string {
	reader := bufio.NewReader(os.Stdin)
	prompt = fmt.Sprintf("%s: ", prompt)
	fmt.Printf("%s", prompt)
	out, _ := reader.ReadString('\n')
	return strings.TrimSpace(out)
}

func PromptWithDefault(prompt string, defaultValue string) string {
	reader := bufio.NewReader(os.Stdin)
	prompt = fmt.Sprintf("%s [%s]: ", prompt, defaultValue)
	fmt.Printf("%s", prompt)
	out, _ := reader.ReadString('\n')
	if len(strings.TrimSpace(out)) == 0 {
		return defaultValue
	} else {
		return strings.TrimSpace(out)
	}
}

func ParseYaml(file string, opts *map[string]interface{}) {
	if fh, err := ioutil.ReadFile(file); err == nil {
		log.Debug("Found Config file: %s", file)
		yaml.Unmarshal(fh, opts)
		fixed, _ := YamlFixup(*opts)
		*opts = fixed.(map[string]interface{})
	}
}

func YamlFixup(data interface{}) (interface{}, error) {
	switch d := data.(type) {
	case map[interface{}]interface{}:
		// need to copy this map into a string map so json can encode it
		copy := make(map[string]interface{})
		for key, val := range d {
			switch k := key.(type) {
			case string:
				if fixed, err := YamlFixup(val); err != nil {
					return nil, err
				} else if fixed != nil {
					copy[k] = fixed
				}
			default:
				err := fmt.Errorf("YAML: key %s is type '%T', require 'string'", key, k)
				log.Error("%s", err)
				return nil, err
			}
		}
		return copy, nil
	case map[string]interface{}:
		copy := make(map[string]interface{})
		for k, v := range d {
			if fixed, err := YamlFixup(v); err != nil {
				return nil, err
			} else if fixed != nil {
				copy[k] = fixed
			}
		}
		return copy, nil
	case []interface{}:
		copy := make([]interface{}, 0, len(d))
		for _, val := range d {
			if fixed, err := YamlFixup(val); err != nil {
				return nil, err
			} else if fixed != nil {
				copy = append(copy, fixed)
			}
		}
		return copy, nil
	case string:
		if d == "" || d == "\n" {
			return nil, nil
		}
		return d, nil
	default:
		return d, nil
	}
}

func Mkdir(dir string) error {
	if stat, err := os.Stat(dir); err != nil && !os.IsNotExist(err) {
		log.Error("Failed to stat %s: %s", dir, err)
		return err
	} else if err == nil && !stat.IsDir() {
		err := fmt.Errorf("%s exists and is not a directory!", dir)
		log.Error("%s", err)
		return err
	} else {
		// dir does not exist, so try to create it
		if err := os.MkdirAll(dir, 0755); err != nil {
			log.Error("Failed to mkdir -p %s: %s", dir, err)
			return err
		}
	}
	return nil
}
<commit_msg>prompts no longer used, use github.com/AlecAivazis/survey instead<commit_after>package util

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/bmatcuk/doublestar"
	"github.com/mgutz/ansi"
	"github.com/op/go-logging"
	"gopkg.in/coryb/yaml.v2"
)

var log = logging.MustGetLogger("util")

func FindParentPaths(fileName string) []string {
	cwd, _ := os.Getwd()

	paths := make([]string, 0)

	// special case if homedir is not in current path then check there anyway
	homedir := os.Getenv("HOME")
	if !strings.HasPrefix(cwd, homedir) {
		file := fmt.Sprintf("%s/%s", homedir, fileName)
		if _, err := os.Stat(file); err == nil {
			paths = append(paths, file)
		}
	}

	var dir string
	for _, part := range strings.Split(cwd, string(os.PathSeparator)) {
		if dir == "/" {
			dir = fmt.Sprintf("/%s", part)
		} else {
			dir = fmt.Sprintf("%s/%s", dir, part)
		}
		file := fmt.Sprintf("%s/%s", dir, fileName)
		if _, err := os.Stat(file); err == nil {
			paths = append(paths, file)
		}
	}
	return paths
}

func FindClosestParentPath(fileName string) (string, error) {
	paths := FindParentPaths(fileName)
	if len(paths) > 0 {
		return paths[len(paths)-1], nil
	}
	return "", errors.New(fmt.Sprintf("%s not found in parent directory hierarchy", fileName))
}

func ReadFile(file string) string {
	var bytes []byte
	var err error
	if bytes, err = ioutil.ReadFile(file); err != nil {
		log.Error("Failed to read file %s: %s", file, err)
		os.Exit(1)
	}
	return string(bytes)
}

func CopyFile(src, dst string) (err error) {
	var s, d *os.File
	if s, err = os.Open(src); err == nil {
		defer s.Close()
		if d, err = os.Create(dst); err == nil {
			if _, err = io.Copy(d, s); err != nil {
				d.Close()
				return
			}
			return d.Close()
		}
	}
	return
}

func FuzzyAge(start string) (string, error) {
	if t, err := time.Parse("2006-01-02T15:04:05.000-0700", start); err != nil {
		return "", err
	} else {
		delta := time.Now().Sub(t)
		if delta.Minutes() < 2 {
			return "a minute", nil
		} else if dm := delta.Minutes(); dm < 45 {
			return fmt.Sprintf("%d minutes", int(dm)), nil
		} else if dm := delta.Minutes(); dm < 90 {
			return "an hour", nil
		} else if dh := delta.Hours(); dh < 24 {
			return fmt.Sprintf("%d hours", int(dh)), nil
		} else if dh := delta.Hours(); dh < 48 {
			return "a day", nil
		} else {
			return fmt.Sprintf("%d days", int(delta.Hours()/24)), nil
		}
	}
	return "unknown", nil
}

func RunTemplate(templateContent string, data interface{}, out io.Writer) error {

	if out == nil {
		out = os.Stdout
	}

	funcs := map[string]interface{}{
		"toJson": func(content interface{}) (string, error) {
			if bytes, err := json.MarshalIndent(content, "", "    "); err != nil {
				return "", err
			} else {
				return string(bytes), nil
			}
		},
		"toYAML": func(content interface{}) (string, error) {
			if bytes, err := yaml.Marshal(content); err != nil {
				return "", err
			} else {
				return string(bytes), nil
			}
		},
		"append": func(more string, content interface{}) (string, error) {
			switch value := content.(type) {
			case string:
				return string(append([]byte(content.(string)), []byte(more)...)), nil
			case []byte:
				return string(append(content.([]byte), []byte(more)...)), nil
			default:
				return "", errors.New(fmt.Sprintf("Unknown type: %s", value))
			}
		},
		"indent": func(spaces int, content string) string {
			indent := make([]rune, spaces+1, spaces+1)
			indent[0] = '\n'
			for i := 1; i < spaces+1; i += 1 {
				indent[i] = ' '
			}

			lineSeps := []rune{'\n', '\u0085', '\u2028', '\u2029'}
			for _, sep := range lineSeps {
				indent[0] = sep
				content = strings.Replace(content, string(sep), string(indent), -1)
			}
			return content

		},
		"comment": func(content string) string {
			lineSeps := []rune{'\n', '\u0085', '\u2028', '\u2029'}
			for _, sep := range lineSeps {
				content = strings.Replace(content, string(sep), string([]rune{sep, '#', ' '}), -1)
			}
			return content
		},
		"color": func(color string) string {
			return ansi.ColorCode(color)
		},
		"split": func(sep string, content string) []string {
			return strings.Split(content, sep)
		},
		"abbrev": func(max int, content string) string {
			if len(content) > max {
				var buffer bytes.Buffer
				buffer.WriteString(content[:max-3])
				buffer.WriteString("...")
				return buffer.String()
			}
			return content
		},
		"rep": func(count int, content string) string {
			var buffer bytes.Buffer
			for i := 0; i < count; i += 1 {
				buffer.WriteString(content)
			}
			return buffer.String()
		},
		"age": func(content string) (string, error) {
			return FuzzyAge(content)
		},
		"toUpper": func(content string) string {
			return strings.ToUpper(content)
		},
		"toLower": func(content string) string {
			return strings.ToLower(content)
		},
		"ftime": func(format string, t int64) string {
			return time.Unix(t, 0).Format(format)
		},
		"env": func(name string) string {
			return os.Getenv(name)
		},
		"hasPrefix": func(content, prefix string) bool {
			return strings.HasPrefix(content, prefix)
		},
		"hasSuffix": func(content, suffix string) bool {
			return strings.HasSuffix(content, suffix)
		},
		"trimPrefix": func(content, prefix string) string {
			return strings.TrimPrefix(content, prefix)
		},
		"trimSuffix": func(content, suffix string) string {
			return strings.TrimSuffix(content, suffix)
		},
		"cwd": func() (string, error) {
			return os.Getwd()
		},
		"findLatestFile": func(glob string) (string, error) {
			matches, err := doublestar.Glob(glob)
			if err != nil {
				return "", err
			}
			artifact := struct {
				Path string
				Info os.FileInfo
			}{}
			for _, matchPath := range matches {
				info, err := os.Stat(matchPath)
				if err != nil {
					return "", err
				}
				if artifact.Path == "" {
					artifact.Info = info
					artifact.Path = matchPath
				} else if artifact.Info.ModTime().Before(info.ModTime()) {
					artifact.Info = info
					artifact.Path = matchPath
				}
			}
			if artifact.Path == "" {
				return "", fmt.Errorf("No file found matching glob: %s", glob)
			}
			return artifact.Path, nil
		},
	}
	if tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent); err != nil {
		log.Error("Failed to parse template: %s", err)
		return err
	} else {
		if err := tmpl.Execute(out, data); err != nil {
			log.Error("Failed to execute template: %s", err)
			return err
		}
	}
	return nil
}

func ResponseToJson(resp *http.Response, err error) (interface{}, error) {
	if err != nil {
		return nil, err
	}

	data := JsonDecode(resp.Body)
	if resp.StatusCode == 400 {
		if val, ok := data.(map[string]interface{})["errorMessages"]; ok {
			for _, errMsg := range val.([]interface{}) {
				log.Error("%s", errMsg)
			}
		}
	}

	return data, nil
}

func JsonDecode(io io.Reader) interface{} {
	content, err := ioutil.ReadAll(io)
	var data interface{}
	err = json.Unmarshal(content, &data)
	if err != nil {
		log.Error("JSON Parse Error: %s from %s", err, content)
	}
	return data
}

func JsonEncode(data interface{}) (string, error) {
	buffer := bytes.NewBuffer(make([]byte, 0))
	enc := json.NewEncoder(buffer)

	err := enc.Encode(data)
	if err != nil {
		log.Error("Failed to encode data %s: %s", data, err)
		return "", err
	}
	return buffer.String(), nil
}

func JsonWrite(file string, data interface{}) {
	fh, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	defer fh.Close()
	if err != nil {
		log.Error("Failed to open %s: %s", file, err)
		os.Exit(1)
	}
	enc := json.NewEncoder(fh)
	enc.Encode(data)
}

// func PromptYN(prompt string, yes bool) bool {
// 	reader := bufio.NewReader(os.Stdin)
// 	if !yes {
// 		prompt = fmt.Sprintf("%s [y/N]: ", prompt)
// 	} else {
// 		prompt = fmt.Sprintf("%s [Y/n]: ", prompt)
// 	}

// 	fmt.Printf("%s", prompt)
// 	text, _ := reader.ReadString('\n')
// 	ans := strings.ToLower(strings.TrimRight(text, "\n"))
// 	if ans == "" {
// 		return yes
// 	}
// 	if strings.HasPrefix(ans, "y") {
// 		return true
// 	}
// 	return false
// }

// func Prompt(prompt string) string {
// 	reader := bufio.NewReader(os.Stdin)
// 	prompt = fmt.Sprintf("%s: ", prompt)
// 	fmt.Printf("%s", prompt)
// 	out, _ := reader.ReadString('\n')
// 	return strings.TrimSpace(out)
// }

// func PromptWithDefault(prompt string, defaultValue string) string {
// 	reader := bufio.NewReader(os.Stdin)
// 	prompt = fmt.Sprintf("%s [%s]: ", prompt, defaultValue)
// 	fmt.Printf("%s", prompt)
// 	out, _ := reader.ReadString('\n')
// 	if len(strings.TrimSpace(out)) == 0 {
// 		return defaultValue
// 	} else {
// 		return strings.TrimSpace(out)
// 	}
// }

func ParseYaml(file string, opts *map[string]interface{}) {
	if fh, err := ioutil.ReadFile(file); err == nil {
		log.Debug("Found Config file: %s", file)
		yaml.Unmarshal(fh, opts)
		fixed, _ := YamlFixup(*opts)
		*opts = fixed.(map[string]interface{})
	}
}

func YamlFixup(data interface{}) (interface{}, error) {
	switch d := data.(type) {
	case map[interface{}]interface{}:
		// need to copy this map into a string map so json can encode it
		copy := make(map[string]interface{})
		for key, val := range d {
			switch k := key.(type) {
			case string:
				if fixed, err := YamlFixup(val); err != nil {
					return nil, err
				} else if fixed != nil {
					copy[k] = fixed
				}
			default:
				err := fmt.Errorf("YAML: key %s is type '%T', require 'string'", key, k)
				log.Error("%s", err)
				return nil, err
			}
		}
		return copy, nil
	case map[string]interface{}:
		copy := make(map[string]interface{})
		for k, v := range d {
			if fixed, err := YamlFixup(v); err != nil {
				return nil, err
			} else if fixed != nil {
				copy[k] = fixed
			}
		}
		return copy, nil
	case []interface{}:
		copy := make([]interface{}, 0, len(d))
		for _, val := range d {
			if fixed, err := YamlFixup(val); err != nil {
				return nil, err
			} else if fixed != nil {
				copy = append(copy, fixed)
			}
		}
		return copy, nil
	case string:
		if d == "" || d == "\n" {
			return nil, nil
		}
		return d, nil
	default:
		return d, nil
	}
}

func Mkdir(dir string) error {
	if stat, err := os.Stat(dir); err != nil && !os.IsNotExist(err) {
		log.Error("Failed to stat %s: %s", dir, err)
		return err
	} else if err == nil && !stat.IsDir() {
		err := fmt.Errorf("%s exists and is not a directory!", dir)
		log.Error("%s", err)
		return err
	} else {
		// dir does not exist, so try to create it
		if err := os.MkdirAll(dir, 0755); err != nil {
			log.Error("Failed to mkdir -p %s: %s", dir, err)
			return err
		}
	}
	return nil
}
<|endoftext|>