<commit_before>package metadata

import (
	"bytes"
	"encoding/json"
	"errors"
	"koding/kites/config"
)

//go:generate $GOPATH/bin/go-bindata -mode 420 -modtime 1470666525 -pkg metadata -o provision.sh.go provision.sh
//go:generate gofmt -l -w -s provision.sh.go

// Default asset URLs used by kloud when deploying klient.
//
// They are being downloaded on a remote vm during provisioning.
//
// TODO(rjeczalik): make it configurable during ./configure = add to kites/config/config.json
var (
	DefaultEntrypointBaseURL = "https://koding-klient.s3.amazonaws.com/entrypoint"
	DefaultScreenURL         = "https://koding-dl.s3.amazonaws.com/screen.tar.gz"
	DefaultCertURL           = "https://koding-dl.s3.amazonaws.com/ca-certificates.crt.gz"
)

var provision = string(mustAsset("provision.sh"))

func mustAsset(file string) []byte {
	p, err := Asset(file)
	if err != nil {
		panic(err)
	}
	return p
}

// Config is used to build cloud-init from user_data
// in order to install klient service on a remote vm
// and authenticate and connect to Koding.
type Config struct {
	Konfig   *config.Konfig // Koding endpoints configuration
	KiteKey  string         // KiteKey used by klient to authenticate with Koding
	Userdata string         // User script to run after provisioning, if any
	Debug    bool           // Whether klient should be started in debug mode; configurable by debug field in stack template
}

// New builds new cloud-init for the given configuration.
//
// If cfg.Userdata is a cloud-init file, it will be
// merged back into kloud's one. If there are conflicts
// between the two cloud-init scripts, stack build will
// fail - we do not allow user to overwrite kloud's
// cloud-init.
func New(cfg *Config) (CloudInit, error) {
	metadata, err := newMetadata(cfg)
	if err != nil {
		return nil, errors.New("error building metadata: " + err.Error())
	}

	ci, err := ParseCloudInit([]byte(cfg.Userdata))
	switch err {
	case nil:
		user := ci
		ci = NewCloudInit(&CloudConfig{
			Metadata: string(metadata),
		})

		if err := Merge(user, ci); err != nil {
			return nil, errors.New("error merging cloud-init from user-data: " + err.Error())
		}

	case ErrNotCloudInit:
		ci = NewCloudInit(&CloudConfig{
			Metadata: string(metadata),
			Userdata: cfg.Userdata,
		})
	default:
		return nil, errors.New("error parsing cloud-init from user-data: " + err.Error())
	}

	return ci, nil
}

// TODO(rjeczalik): refactor to a separate function in kites/config
// and use in marathon as well.
func newMetadata(cfg *Config) ([]byte, error) {
	konfig := &config.Konfig{
		Endpoints: cfg.Konfig.Endpoints,
		KiteKey:   cfg.KiteKey,
		Debug:     cfg.Debug,
	}

	m := map[string]interface{}{
		"konfig.konfig.konfigs": map[string]interface{}{
			konfig.ID(): konfig,
		},
		"konfig.konfig.konfigs.used": map[string]interface{}{
			"id": konfig.ID(),
		},
	}

	var buf bytes.Buffer

	enc := json.NewEncoder(&buf)
	enc.SetEscapeHTML(false)

	if err := enc.Encode(m); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}
<commit_msg>kloud/metadata: reverse proxy s3 files through koding.com<commit_after>package metadata

import (
	"bytes"
	"encoding/json"
	"errors"
	"koding/kites/config"
)

//go:generate $GOPATH/bin/go-bindata -mode 420 -modtime 1470666525 -pkg metadata -o provision.sh.go provision.sh
//go:generate gofmt -l -w -s provision.sh.go

// Default asset URLs used by kloud when deploying klient.
//
// They are being downloaded on a remote vm during provisioning.
//
// TODO(rjeczalik): make it configurable during ./configure = add to kites/config/config.json
var (
	DefaultEntrypointBaseURL = "https://koding.com/s3/koding-klient/entrypoint"
	DefaultScreenURL         = "https://koding.com/d/screen.tar.gz"
	DefaultCertURL           = "https://koding.com/d/ca-certificates.crt.gz"
)

var provision = string(mustAsset("provision.sh"))

func mustAsset(file string) []byte {
	p, err := Asset(file)
	if err != nil {
		panic(err)
	}
	return p
}

// Config is used to build cloud-init from user_data
// in order to install klient service on a remote vm
// and authenticate and connect to Koding.
type Config struct {
	Konfig   *config.Konfig // Koding endpoints configuration
	KiteKey  string         // KiteKey used by klient to authenticate with Koding
	Userdata string         // User script to run after provisioning, if any
	Debug    bool           // Whether klient should be started in debug mode; configurable by debug field in stack template
}

// New builds new cloud-init for the given configuration.
//
// If cfg.Userdata is a cloud-init file, it will be
// merged back into kloud's one. If there are conflicts
// between the two cloud-init scripts, stack build will
// fail - we do not allow user to overwrite kloud's
// cloud-init.
func New(cfg *Config) (CloudInit, error) {
	metadata, err := newMetadata(cfg)
	if err != nil {
		return nil, errors.New("error building metadata: " + err.Error())
	}

	ci, err := ParseCloudInit([]byte(cfg.Userdata))
	switch err {
	case nil:
		user := ci
		ci = NewCloudInit(&CloudConfig{
			Metadata: string(metadata),
		})

		if err := Merge(user, ci); err != nil {
			return nil, errors.New("error merging cloud-init from user-data: " + err.Error())
		}

	case ErrNotCloudInit:
		ci = NewCloudInit(&CloudConfig{
			Metadata: string(metadata),
			Userdata: cfg.Userdata,
		})
	default:
		return nil, errors.New("error parsing cloud-init from user-data: " + err.Error())
	}

	return ci, nil
}

// TODO(rjeczalik): refactor to a separate function in kites/config
// and use in marathon as well.
func newMetadata(cfg *Config) ([]byte, error) {
	konfig := &config.Konfig{
		Endpoints: cfg.Konfig.Endpoints,
		KiteKey:   cfg.KiteKey,
		Debug:     cfg.Debug,
	}

	m := map[string]interface{}{
		"konfig.konfig.konfigs": map[string]interface{}{
			konfig.ID(): konfig,
		},
		"konfig.konfig.konfigs.used": map[string]interface{}{
			"id": konfig.ID(),
		},
	}

	var buf bytes.Buffer

	enc := json.NewEncoder(&buf)
	enc.SetEscapeHTML(false)

	if err := enc.Encode(m); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}
<|endoftext|>


<commit_before>/*
Package jsonptrerror extends encoding/json.Decoder to return unmarshal errors
located with JSON Pointer (RFC 6091).

The current implementation keeps a duplicate copy of the JSON document in memory.
*/
package jsonptrerror

import (
	"bytes"
	"encoding/json"
	"io"

	"github.com/dolmen-go/jsonptr"
)

// UnmarshalTypeError is an extension of encoding/json.UnmarshalTypeError
// that also includes the error location as a JSON Pointer (RFC 6901)
type UnmarshalTypeError struct {
	json.UnmarshalTypeError
	Pointer jsonptr.Pointer
}

func (e UnmarshalTypeError) Error() string {
	return e.Pointer.String() + ": cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}

type decoder interface {
	Decode(interface{}) error
	UseNumber()
}

// Decoder is the same as encoding/json.Decoder, except Decode returns
// our UnmarshalTypeError (providing a JSON Pointer) instead of encoding/json.UnmarshalTypeError.
type Decoder struct {
	decoder decoder
	input   bytes.Buffer
	err     error
}

func NewDecoder(r io.Reader) *Decoder {
	var d Decoder
	d.decoder = json.NewDecoder(io.TeeReader(r, &d.input))
	return &d
}

func (d *Decoder) UseNumber() {
	d.decoder.UseNumber()
}

const (
	tokenTopValue = iota
	tokenArrayValue
	tokenArrayComma
	tokenObjectKey
	tokenObjectColon
	tokenObjectValue
	tokenObjectComma
)

func (d *Decoder) Decode(v interface{}) error {
	if d.err != nil {
		return d.err
	}
	d.err = d.decoder.Decode(v)
	if err, ok := d.err.(*json.UnmarshalTypeError); ok {
		offset := int(err.Offset)
		input := d.input.Bytes()
		i := 0
		type elem struct {
			container byte
			property  []byte
			index     int
		}
		var elemStack []elem
		var expectKey bool
		for {
			if i == offset {
				ptr := jsonptr.Pointer{}
				for _, e := range elemStack {
					switch e.container {
					case '{':
						var name string
						json.Unmarshal(e.property, &name)
						ptr.Property(name)
					case '[':
						ptr.Index(e.index)
					}
				}
				d.err = &UnmarshalTypeError{*err, ptr}
				break
			}
			switch input[i] {
			case '{':
				elemStack = append(elemStack, elem{container: '{'})
				expectKey = true
			case '[':
				elemStack = append(elemStack, elem{container: '[', index: 0})
			case '}', ']':
				elemStack = elemStack[:len(elemStack)-1]
			case '"':
				j := i
			str:
				for {
					i++
					switch input[i] {
					case '\\':
						i++
					case '"':
						break str
					}
				}
				if expectKey {
					elemStack[len(elemStack)-1].property = input[j : i+1]
					expectKey = false
				}
			case ',':
				if elemStack[len(elemStack)-1].container == '{' {
					expectKey = true
				} else {
					elemStack[len(elemStack)-1].index++
				}
			}
			i++
		}
	}
	if d.err != nil {
		d.decoder = nil
		d.input = bytes.Buffer{}
	}
	return d.err
}
<commit_msg>Refactor: extract pointerAtOffset as a separate func<commit_after>/*
Package jsonptrerror extends encoding/json.Decoder to return unmarshal errors
located with JSON Pointer (RFC 6091).

The current implementation keeps a duplicate copy of the JSON document in memory.
*/
package jsonptrerror

import (
	"bytes"
	"encoding/json"
	"io"

	"github.com/dolmen-go/jsonptr"
)

// UnmarshalTypeError is an extension of encoding/json.UnmarshalTypeError
// that also includes the error location as a JSON Pointer (RFC 6901)
type UnmarshalTypeError struct {
	json.UnmarshalTypeError
	Pointer jsonptr.Pointer
}

func (e UnmarshalTypeError) Error() string {
	return e.Pointer.String() + ": cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}

type decoder interface {
	Decode(interface{}) error
	UseNumber()
}

// Decoder is the same as encoding/json.Decoder, except Decode returns
// our UnmarshalTypeError (providing a JSON Pointer) instead of encoding/json.UnmarshalTypeError.
type Decoder struct {
	decoder decoder
	input   bytes.Buffer
	err     error
}

func NewDecoder(r io.Reader) *Decoder {
	var d Decoder
	d.decoder = json.NewDecoder(io.TeeReader(r, &d.input))
	return &d
}

func (d *Decoder) UseNumber() {
	d.decoder.UseNumber()
}

func (d *Decoder) Decode(v interface{}) error {
	if d.err != nil {
		return d.err
	}
	d.err = d.decoder.Decode(v)
	if err, ok := d.err.(*json.UnmarshalTypeError); ok {
		d.err = &UnmarshalTypeError{*err, pointerAtOffset(d.input.Bytes(), int(err.Offset))}
	}
	if d.err != nil {
		d.decoder = nil
		d.input = bytes.Buffer{}
	}
	return d.err
}

// pointerAtOffset extracts the JSON Pointer at the start of a value in a *valid* JSON document
func pointerAtOffset(input []byte, offset int) jsonptr.Pointer {
	var ptr jsonptr.Pointer
	i := 0
	type elem struct {
		container byte
		property  []byte
		index     int
	}
	var elemStack []elem
	var expectKey bool
	for {
		if i == offset {
			for _, e := range elemStack {
				switch e.container {
				case '{':
					var name string
					json.Unmarshal(e.property, &name)
					ptr.Property(name)
				case '[':
					ptr.Index(e.index)
				}
			}
			break
		}
		switch input[i] {
		case '{':
			elemStack = append(elemStack, elem{container: '{'})
			expectKey = true
		case '[':
			elemStack = append(elemStack, elem{container: '[', index: 0})
		case '}', ']':
			elemStack = elemStack[:len(elemStack)-1]
		case '"':
			j := i
		str:
			for {
				i++
				switch input[i] {
				case '\\':
					i++
				case '"':
					break str
				}
			}
			if expectKey {
				elemStack[len(elemStack)-1].property = input[j : i+1]
				expectKey = false
			}
		case ',':
			if elemStack[len(elemStack)-1].container == '{' {
				expectKey = true
			} else {
				elemStack[len(elemStack)-1].index++
			}
		}
		i++
	}
	return ptr
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/Syfaro/telegram-bot-api"
)

// User - one telegram user who interact with bot
type User struct {
	UserName       string
	FirstName      string
	LastName       string
	AuthCode       string
	AuthCodeRoot   string
	IsAuthorized   bool
	IsRoot         bool
	PrivateChatID  int
	Counter        int
	LastAccessTime time.Time
}

// Users in chat
type Users struct {
	list                   map[int]*User
	predefinedAllowedUsers map[string]bool
	predefinedRootUsers    map[string]bool
}

// length of random code in bytes
const CODE_BYTES_LENGTH = 15

// clear old users after 20 minutes after login
const SECONDS_FOR_OLD_USERS_BEFORE_VACUUM = 1200

// NewUsers - create Users object
func NewUsers(appConfig Config) Users {
	users := Users{
		list: map[int]*User{},
		predefinedAllowedUsers: map[string]bool{},
		predefinedRootUsers:    map[string]bool{},
	}

	for _, name := range appConfig.predefinedAllowedUsers {
		users.predefinedAllowedUsers[name] = true
	}
	for _, name := range appConfig.predefinedRootUsers {
		users.predefinedAllowedUsers[name] = true
		users.predefinedRootUsers[name] = true
	}
	return users
}

// AddNew - add new user if not exists
func (users Users) AddNew(tgbotMessage tgbotapi.Message) {
	privateChatID := 0
	if !tgbotMessage.IsGroup() {
		privateChatID = tgbotMessage.Chat.ID
	}

	if _, ok := users.list[tgbotMessage.From.ID]; ok && privateChatID > 0 {
		users.list[tgbotMessage.From.ID].PrivateChatID = privateChatID
	} else if !ok {
		users.list[tgbotMessage.From.ID] = &User{
			UserName:      tgbotMessage.From.UserName,
			FirstName:     tgbotMessage.From.FirstName,
			LastName:      tgbotMessage.From.LastName,
			IsAuthorized:  users.predefinedAllowedUsers[tgbotMessage.From.UserName],
			IsRoot:        users.predefinedRootUsers[tgbotMessage.From.UserName],
			PrivateChatID: privateChatID,
		}
	}

	// collect stat
	users.list[tgbotMessage.From.ID].LastAccessTime = time.Now()
	if users.list[tgbotMessage.From.ID].IsAuthorized {
		users.list[tgbotMessage.From.ID].Counter++
	}
}

// DoLogin - generate secret code
func (users Users) DoLogin(userID int, forRoot bool) string {
	code := getRandomCode()
	if forRoot {
		users.list[userID].IsRoot = false
		users.list[userID].AuthCodeRoot = code
	} else {
		users.list[userID].IsAuthorized = false
		users.list[userID].AuthCode = code
	}
	return code
}

// SetAuthorized - set user authorized or authorized as root
func (users Users) SetAuthorized(userID int, forRoot bool) {
	users.list[userID].IsAuthorized = true
	if forRoot {
		users.list[userID].IsRoot = true
		users.list[userID].AuthCodeRoot = ""
	} else {
		users.list[userID].AuthCode = ""
	}
}

// IsValidCode - check secret code for user
func (users Users) IsValidCode(userID int, code string, forRoot bool) bool {
	var result bool
	if forRoot {
		result = code != "" && code == users.list[userID].AuthCodeRoot
	} else {
		result = code != "" && code == users.list[userID].AuthCode
	}
	return result
}

// IsAuthorized - check user is authorized
func (users Users) IsAuthorized(userID int) bool {
	isAuthorized := false
	if _, ok := users.list[userID]; ok && users.list[userID].IsAuthorized {
		isAuthorized = true
	}

	return isAuthorized
}

// IsRoot - check user is root
func (users Users) IsRoot(userID int) bool {
	isRoot := false
	if _, ok := users.list[userID]; ok && users.list[userID].IsRoot {
		isRoot = true
	}

	return isRoot
}

// broadcastForRoots - send message to all root users
func (users Users) BroadcastForRoots(bot *tgbotapi.BotAPI, message string) {
	for _, user := range users.list {
		if user.IsRoot && user.PrivateChatID > 0 {
			sendMessageWithLogging(bot, user.PrivateChatID, message)
		}
	}
}

// String - format user name
func (users Users) String(userID int) string {
	result := fmt.Sprintf("%s %s", users.list[userID].FirstName, users.list[userID].LastName)
	if users.list[userID].UserName != "" {
		result += fmt.Sprintf(" (@%s)", users.list[userID].UserName)
	}
	return result
}

// StringVerbose - format user name with all fields
func (users Users) StringVerbose(userID int) string {
	user := users.list[userID]
	result := fmt.Sprintf("%s: id: %d, auth: %v, root: %v, count: %d, last: %v",
		users.String(userID),
		userID,
		user.IsAuthorized,
		user.IsRoot,
		user.Counter,
		user.LastAccessTime.Format("2006-01-02 15:04:05"),
	)
	return result
}

// clearOldUsers - clear old users without login
func (users Users) ClearOldUsers() {
	for id, user := range users.list {
		if !user.IsAuthorized && !user.IsRoot && user.Counter == 0 &&
			time.Now().Sub(user.LastAccessTime).Seconds() > SECONDS_FOR_OLD_USERS_BEFORE_VACUUM {
			log.Printf("Vacuum: %d, %s", id, users.String(id))
			delete(users.list, id)
		}
	}
}

// getUserIDByName - find user by login
func (users Users) GetUserIDByName(userName string) int {
	userID := 0
	for id, user := range users.list {
		if userName == user.UserName {
			userID = id
			break
		}
	}

	return userID
}

// banUser - ban user by ID
func (users Users) BanUser(userID int) bool {
	if _, ok := users.list[userID]; ok {
		users.list[userID].IsAuthorized = false
		users.list[userID].IsRoot = false
		return true
	}

	return false
}

// Search - search users
func (users Users) Search(query string) (result []int) {
	queryUserID, _ := strconv.Atoi(query)
	query = strings.ToLower(query)
	queryAsLogin := regexp.MustCompile("@").ReplaceAllLiteralString(query, "")

	for userID, user := range users.list {
		if queryUserID == userID ||
			strings.Contains(strings.ToLower(user.UserName), queryAsLogin) ||
			strings.Contains(strings.ToLower(user.FirstName), query) ||
			strings.Contains(strings.ToLower(user.LastName), query) {
			result = append(result, userID)
		}
	}

	return result
}

// getRandomCode - generate random code for authorize user
func getRandomCode() string {
	buffer := make([]byte, CODE_BYTES_LENGTH)
	_, err := rand.Read(buffer)
	if err != nil {
		log.Print("Get code error: ", err)
		return ""
	}

	return base64.URLEncoding.EncodeToString(buffer)
}
<commit_msg>Delete user from predefined lists in ban command<commit_after>package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/Syfaro/telegram-bot-api"
)

// User - one telegram user who interact with bot
type User struct {
	UserName       string
	FirstName      string
	LastName       string
	AuthCode       string
	AuthCodeRoot   string
	IsAuthorized   bool
	IsRoot         bool
	PrivateChatID  int
	Counter        int
	LastAccessTime time.Time
}

// Users in chat
type Users struct {
	list                   map[int]*User
	predefinedAllowedUsers map[string]bool
	predefinedRootUsers    map[string]bool
}

// length of random code in bytes
const CODE_BYTES_LENGTH = 15

// clear old users after 20 minutes after login
const SECONDS_FOR_OLD_USERS_BEFORE_VACUUM = 1200

// NewUsers - create Users object
func NewUsers(appConfig Config) Users {
	users := Users{
		list: map[int]*User{},
		predefinedAllowedUsers: map[string]bool{},
		predefinedRootUsers:    map[string]bool{},
	}

	for _, name := range appConfig.predefinedAllowedUsers {
		users.predefinedAllowedUsers[name] = true
	}
	for _, name := range appConfig.predefinedRootUsers {
		users.predefinedAllowedUsers[name] = true
		users.predefinedRootUsers[name] = true
	}
	return users
}

// AddNew - add new user if not exists
func (users Users) AddNew(tgbotMessage tgbotapi.Message) {
	privateChatID := 0
	if !tgbotMessage.IsGroup() {
		privateChatID = tgbotMessage.Chat.ID
	}

	if _, ok := users.list[tgbotMessage.From.ID]; ok && privateChatID > 0 {
		users.list[tgbotMessage.From.ID].PrivateChatID = privateChatID
	} else if !ok {
		users.list[tgbotMessage.From.ID] = &User{
			UserName:      tgbotMessage.From.UserName,
			FirstName:     tgbotMessage.From.FirstName,
			LastName:      tgbotMessage.From.LastName,
			IsAuthorized:  users.predefinedAllowedUsers[tgbotMessage.From.UserName],
			IsRoot:        users.predefinedRootUsers[tgbotMessage.From.UserName],
			PrivateChatID: privateChatID,
		}
	}

	// collect stat
	users.list[tgbotMessage.From.ID].LastAccessTime = time.Now()
	if users.list[tgbotMessage.From.ID].IsAuthorized {
		users.list[tgbotMessage.From.ID].Counter++
	}
}

// DoLogin - generate secret code
func (users Users) DoLogin(userID int, forRoot bool) string {
	code := getRandomCode()
	if forRoot {
		users.list[userID].IsRoot = false
		users.list[userID].AuthCodeRoot = code
	} else {
		users.list[userID].IsAuthorized = false
		users.list[userID].AuthCode = code
	}
	return code
}

// SetAuthorized - set user authorized or authorized as root
func (users Users) SetAuthorized(userID int, forRoot bool) {
	users.list[userID].IsAuthorized = true
	if forRoot {
		users.list[userID].IsRoot = true
		users.list[userID].AuthCodeRoot = ""
	} else {
		users.list[userID].AuthCode = ""
	}
}

// IsValidCode - check secret code for user
func (users Users) IsValidCode(userID int, code string, forRoot bool) bool {
	var result bool
	if forRoot {
		result = code != "" && code == users.list[userID].AuthCodeRoot
	} else {
		result = code != "" && code == users.list[userID].AuthCode
	}
	return result
}

// IsAuthorized - check user is authorized
func (users Users) IsAuthorized(userID int) bool {
	isAuthorized := false
	if _, ok := users.list[userID]; ok && users.list[userID].IsAuthorized {
		isAuthorized = true
	}

	return isAuthorized
}

// IsRoot - check user is root
func (users Users) IsRoot(userID int) bool {
	isRoot := false
	if _, ok := users.list[userID]; ok && users.list[userID].IsRoot {
		isRoot = true
	}

	return isRoot
}

// broadcastForRoots - send message to all root users
func (users Users) BroadcastForRoots(bot *tgbotapi.BotAPI, message string) {
	for _, user := range users.list {
		if user.IsRoot && user.PrivateChatID > 0 {
			sendMessageWithLogging(bot, user.PrivateChatID, message)
		}
	}
}

// String - format user name
func (users Users) String(userID int) string {
	result := fmt.Sprintf("%s %s", users.list[userID].FirstName, users.list[userID].LastName)
	if users.list[userID].UserName != "" {
		result += fmt.Sprintf(" (@%s)", users.list[userID].UserName)
	}
	return result
}

// StringVerbose - format user name with all fields
func (users Users) StringVerbose(userID int) string {
	user := users.list[userID]
	result := fmt.Sprintf("%s: id: %d, auth: %v, root: %v, count: %d, last: %v",
		users.String(userID),
		userID,
		user.IsAuthorized,
		user.IsRoot,
		user.Counter,
		user.LastAccessTime.Format("2006-01-02 15:04:05"),
	)
	return result
}

// clearOldUsers - clear old users without login
func (users Users) ClearOldUsers() {
	for id, user := range users.list {
		if !user.IsAuthorized && !user.IsRoot && user.Counter == 0 &&
			time.Now().Sub(user.LastAccessTime).Seconds() > SECONDS_FOR_OLD_USERS_BEFORE_VACUUM {
			log.Printf("Vacuum: %d, %s", id, users.String(id))
			delete(users.list, id)
		}
	}
}

// getUserIDByName - find user by login
func (users Users) GetUserIDByName(userName string) int {
	userID := 0
	for id, user := range users.list {
		if userName == user.UserName {
			userID = id
			break
		}
	}

	return userID
}

// banUser - ban user by ID
func (users Users) BanUser(userID int) bool {
	if _, ok := users.list[userID]; ok {
		users.list[userID].IsAuthorized = false
		users.list[userID].IsRoot = false
		if users.list[userID].UserName != "" {
			delete(users.predefinedAllowedUsers, users.list[userID].UserName)
			delete(users.predefinedRootUsers, users.list[userID].UserName)
		}
		return true
	}

	return false
}

// Search - search users
func (users Users) Search(query string) (result []int) {
	queryUserID, _ := strconv.Atoi(query)
	query = strings.ToLower(query)
	queryAsLogin := regexp.MustCompile("@").ReplaceAllLiteralString(query, "")

	for userID, user := range users.list {
		if queryUserID == userID ||
			strings.Contains(strings.ToLower(user.UserName), queryAsLogin) ||
			strings.Contains(strings.ToLower(user.FirstName), query) ||
			strings.Contains(strings.ToLower(user.LastName), query) {
			result = append(result, userID)
		}
	}

	return result
}

// getRandomCode - generate random code for authorize user
func getRandomCode() string {
	buffer := make([]byte, CODE_BYTES_LENGTH)
	_, err := rand.Read(buffer)
	if err != nil {
		log.Print("Get code error: ", err)
		return ""
	}

	return base64.URLEncoding.EncodeToString(buffer)
}
<|endoftext|>


<commit_before>package search

import (
	"math"

	"github.com/couchbaselabs/bleve/index"
)

type PhraseSearcher struct {
	query        *PhraseQuery
	index        index.Index
	mustSearcher *TermConjunctionSearcher
	queryNorm    float64
	currMust     *DocumentMatch
	slop         int
}

func NewPhraseSearcher(index index.Index, query *PhraseQuery) (*PhraseSearcher, error) {
	// build the downstream searchres
	var err error
	var mustSearcher *TermConjunctionSearcher

	if query.Terms != nil {
		qterms := make([]Query, 0, len(query.Terms))
		for _, qt := range query.Terms {
			if qt != nil {
				qterms = append(qterms, qt)
			}
		}
		tcq := TermConjunctionQuery{
			Terms:    qterms,
			BoostVal: 1.0,
			Explain:  query.Explain,
		}

		mustSearcher, err = NewTermConjunctionSearcher(index, &tcq)
		if err != nil {
			return nil, err
		}
	}

	// build our searcher
	rv := PhraseSearcher{
		index:        index,
		query:        query,
		mustSearcher: mustSearcher,
	}
	rv.computeQueryNorm()
	err = rv.initSearchers()
	if err != nil {
		return nil, err
	}

	return &rv, nil
}

func (s *PhraseSearcher) computeQueryNorm() {
	// first calculate sum of squared weights
	sumOfSquaredWeights := 0.0
	if s.mustSearcher != nil {
		sumOfSquaredWeights += s.mustSearcher.Weight()
	}

	// now compute query norm from this
	s.queryNorm = 1.0 / math.Sqrt(sumOfSquaredWeights)
	// finally tell all the downsteam searchers the norm
	if s.mustSearcher != nil {
		s.mustSearcher.SetQueryNorm(s.queryNorm)
	}
}

func (s *PhraseSearcher) initSearchers() error {
	var err error
	// get all searchers pointing at their first match
	if s.mustSearcher != nil {
		s.currMust, err = s.mustSearcher.Next()
		if err != nil {
			return err
		}
	}

	return nil
}

func (s *PhraseSearcher) advanceNextMust() error {
	var err error

	if s.mustSearcher != nil {
		s.currMust, err = s.mustSearcher.Next()
		if err != nil {
			return err
		}
	}

	return nil
}

func (s *PhraseSearcher) Weight() float64 {
	var rv float64
	rv += s.mustSearcher.Weight()

	return rv
}

func (s *PhraseSearcher) SetQueryNorm(qnorm float64) {
	s.mustSearcher.SetQueryNorm(qnorm)
}

func (s *PhraseSearcher) Next() (*DocumentMatch, error) {
	var rv *DocumentMatch
	for s.currMust != nil {
		rvtlm := make(TermLocationMap, 0)
		freq := 0
		firstTerm := s.query.Terms[0]
		termLocMap, ok := s.currMust.Locations[firstTerm.Field]
		if ok {
			locations, ok := termLocMap[firstTerm.Term]
			if ok {
			OUTER:
				for _, location := range locations {
					crvtlm := make(TermLocationMap, 0)
				INNER:
					for i := 0; i < len(s.query.Terms); i++ {
						nextTerm := s.query.Terms[i]
						if nextTerm != nil {
							// look through all this terms locations
							// to try and find the correct offsets
							nextLocations, ok := termLocMap[nextTerm.Term]
							if ok {
								for _, nextLocation := range nextLocations {
									if nextLocation.Pos == location.Pos+float64(i) {
										// found a location match for this term
										crvtlm.AddLocation(nextTerm.Term, nextLocation)
										continue INNER
									}
								}
								// if we got here we didnt find location match for this term
								continue OUTER
							}
						}
					}
					// if we got here all the terms matched
					freq += 1
					mergeTermLocationMaps(rvtlm, crvtlm)
				}
			}
		}

		if freq > 0 {
			// return match
			rv = s.currMust
			rv.Locations = FieldTermLocationMap{
				firstTerm.Field: rvtlm,
			}
			s.advanceNextMust()
			return rv, nil
		}

		s.advanceNextMust()
	}

	return nil, nil
}

func (s *PhraseSearcher) Advance(ID string) (*DocumentMatch, error) {
	s.mustSearcher.Advance(ID)
	return s.Next()
}

func (s *PhraseSearcher) Count() uint64 {
	// for now return a worst case
	var sum uint64 = 0
	sum += s.mustSearcher.Count()
	return sum
}

func (s *PhraseSearcher) Close() {
	if s.mustSearcher != nil {
		s.mustSearcher.Close()
	}
}
<commit_msg>fix bug in phrase search on composite fields now correctly finds phrases in any of the contributing fields<commit_after>package search

import (
	"math"

	"github.com/couchbaselabs/bleve/index"
)

type PhraseSearcher struct {
	query        *PhraseQuery
	index        index.Index
	mustSearcher *TermConjunctionSearcher
	queryNorm    float64
	currMust     *DocumentMatch
	slop         int
}

func NewPhraseSearcher(index index.Index, query *PhraseQuery) (*PhraseSearcher, error) {
	// build the downstream searchres
	var err error
	var mustSearcher *TermConjunctionSearcher

	if query.Terms != nil {
		qterms := make([]Query, 0, len(query.Terms))
		for _, qt := range query.Terms {
			if qt != nil {
				qterms = append(qterms, qt)
			}
		}
		tcq := TermConjunctionQuery{
			Terms:    qterms,
			BoostVal: 1.0,
			Explain:  query.Explain,
		}

		mustSearcher, err = NewTermConjunctionSearcher(index, &tcq)
		if err != nil {
			return nil, err
		}
	}

	// build our searcher
	rv := PhraseSearcher{
		index:        index,
		query:        query,
		mustSearcher: mustSearcher,
	}
	rv.computeQueryNorm()
	err = rv.initSearchers()
	if err != nil {
		return nil, err
	}

	return &rv, nil
}

func (s *PhraseSearcher) computeQueryNorm() {
	// first calculate sum of squared weights
	sumOfSquaredWeights := 0.0
	if s.mustSearcher != nil {
		sumOfSquaredWeights += s.mustSearcher.Weight()
	}

	// now compute query norm from this
	s.queryNorm = 1.0 / math.Sqrt(sumOfSquaredWeights)
	// finally tell all the downsteam searchers the norm
	if s.mustSearcher != nil {
		s.mustSearcher.SetQueryNorm(s.queryNorm)
	}
}

func (s *PhraseSearcher) initSearchers() error {
	var err error
	// get all searchers pointing at their first match
	if s.mustSearcher != nil {
		s.currMust, err = s.mustSearcher.Next()
		if err != nil {
			return err
		}
	}

	return nil
}

func (s *PhraseSearcher) advanceNextMust() error {
	var err error

	if s.mustSearcher != nil {
		s.currMust, err = s.mustSearcher.Next()
		if err != nil {
			return err
		}
	}

	return nil
}

func (s *PhraseSearcher) Weight() float64 {
	var rv float64
	rv += s.mustSearcher.Weight()

	return rv
}

func (s *PhraseSearcher) SetQueryNorm(qnorm float64) {
	s.mustSearcher.SetQueryNorm(qnorm)
}

func (s *PhraseSearcher) Next() (*DocumentMatch, error) {
	var rv *DocumentMatch
	for s.currMust != nil {
		rvftlm := make(FieldTermLocationMap, 0)
		freq := 0
		firstTerm := s.query.Terms[0]
		for field, termLocMap := range s.currMust.Locations {
			rvtlm := make(TermLocationMap, 0)
			locations, ok := termLocMap[firstTerm.Term]
			if ok {
			OUTER:
				for _, location := range locations {
					crvtlm := make(TermLocationMap, 0)
				INNER:
					for i := 0; i < len(s.query.Terms); i++ {
						nextTerm := s.query.Terms[i]
						if nextTerm != nil {
							// look through all this terms locations
							// to try and find the correct offsets
							nextLocations, ok := termLocMap[nextTerm.Term]
							if ok {
								for _, nextLocation := range nextLocations {
									if nextLocation.Pos == location.Pos+float64(i) {
										// found a location match for this term
										crvtlm.AddLocation(nextTerm.Term, nextLocation)
										continue INNER
									}
								}
								// if we got here we didnt find location match for this term
								continue OUTER
							} else {
								continue OUTER
							}
						}
					}
					// if we got here all the terms matched
					freq += 1
					mergeTermLocationMaps(rvtlm, crvtlm)
					rvftlm[field] = rvtlm
				}
			}
		}

		if freq > 0 {
			// return match
			rv = s.currMust
			rv.Locations = rvftlm
			s.advanceNextMust()
			return rv, nil
		}

		s.advanceNextMust()
	}

	return nil, nil
}

func (s *PhraseSearcher) Advance(ID string) (*DocumentMatch, error) {
	s.mustSearcher.Advance(ID)
	return s.Next()
}

func (s *PhraseSearcher) Count() uint64 {
	// for now return a worst case
	var sum uint64 = 0
	sum += s.mustSearcher.Count()
	return sum
}

func (s *PhraseSearcher) Close() {
	if s.mustSearcher != nil {
		s.mustSearcher.Close()
	}
}
<|endoftext|>


<commit_before>// program to check if you actually get better odds by changing doors if shown one to be empty
// backstory: the popular riddle goes as that at a game show you're shown three doors, behind one
// there's an expensive gift - the others are empty. once you select a door, the host opens one of the
// remaining doors and shows that it's empty, and gives you a chance to revise your choice -
// do you stick with your original selection or do you swap?

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	totalRuns := 0.0
	totalWins := 0.0
	change := true

	for i := 0; i < 10000; i++ {
		totalRuns++

		// initialize three doors with a gift behind one
		doors := []int{0, 0, 0}	// doors are slice indexes 0, 1, and 2 - gift will be changed to 1
		giftIndex := rand.Intn(3)
		doors[giftIndex] = 1

		// contestant selects a door
		contestantChoice := rand.Intn(3)

		// host demonstrates the empty door from the remaining
		empty := demoEmptyDoor(doors, contestantChoice)

		// change or not
		contestantChoice = changeOrKeep(contestantChoice, empty, change)

		// outcome
		if doors[contestantChoice] == 1 {
			totalWins++
		}
	}

	odds := (totalWins * 100) / totalRuns
	fmt.Printf("Change: %v\nRuns: %.0f\nWins: %.0f\nOdds: %.2f%%\n", change, totalRuns, totalWins, odds)
}

// given contestant's choice, shown empty door, and a flag denoting to change selection or not, return a selection
// (changed if flag set)
func changeOrKeep(choice int, empty int, change bool) int {
	if !change {
		return choice
	}

	for i := 0; i < 3; i++ {
		if i == choice || i == empty {
			continue
		}

		return i
	}

	return 10
}

func demoEmptyDoor(doors []int, choice int) int {
	for i := 0; i < 3; i++ {
		if i == choice {
			continue
		}

		if doors[i] == 0 {
			return i
		}
	}

	return 10
}
<commit_msg>More comments + go fmt<commit_after>// program to check if you actually get better odds by changing doors if shown one to be empty
// backstory: the popular riddle goes as that at a game show you're shown three doors, behind one
// there's an expensive gift - the others are empty. once you select a door, the host opens one of the
// remaining doors and shows that it's empty, and gives you a chance to revise your choice -
// do you stick with your original selection or do you swap?

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	totalRuns := 0.0 // number of total selections
	totalWins := 0.0 // number of total wins by contestant
	change := false  // flag denoting whether contestant changes selection upon empty door reveal or not

	// run through some number of game shows
	for i := 0; i < 10000; i++ {
		totalRuns++

		// initialize three doors with a gift behind one
		doors := []int{0, 0, 0} // doors are slice indexes 0, 1, and 2 - gift will be changed to 1
		doors[rand.Intn(3)] = 1 // gift placed behind random door

		// contestant selects a door
		contestantChoice := rand.Intn(3)

		// host demonstrates the empty door from the remaining ones
		empty := demoEmptyDoor(doors, contestantChoice)

		// change or not
		contestantChoice = changeOrKeep(contestantChoice, empty, change)

		// outcome
		if doors[contestantChoice] == 1 {
			totalWins++
		}
	}

	// calculate odds / output results
	odds := (totalWins * 100) / totalRuns
	fmt.Printf("Change: %v\nRuns: %.0f\nWins: %.0f\nOdds: %.2f%%\n", change, totalRuns, totalWins, odds)
}

// given contestant's choice, shown empty door, and a flag denoting to change selection or not, return a selection
// (changed if flag set)
func changeOrKeep(choice int, empty int, change bool) int {
	if !change {
		return choice
	}

	for i := 0; i < 3; i++ {
		if i == choice || i == empty {
			continue
		}

		return i
	}

	// dummy int return at end of function to make compiler happy
	return 10
}

// given the selection the contestant made and the knowledge of what's behind each door,
// the host opens an empty door from the remaining two
func demoEmptyDoor(doors []int, choice int) int {
	for i := 0; i < 3; i++ {
		if i == choice {
			continue
		}

		if doors[i] == 0 {
			return i
		}
	}

	// dummy int return at end of function to make compiler happy
	return 10
}
<|endoftext|>


<commit_before>package slack

import (
	"context"
	"encoding/json"
	"errors"
	"net/url"
)

const (
	DEFAULT_USER_PHOTO_CROP_X = -1
	DEFAULT_USER_PHOTO_CROP_Y = -1
	DEFAULT_USER_PHOTO_CROP_W = -1
)

// UserProfile contains all the information details of a given user
type UserProfile struct {
	FirstName             string `json:"first_name"`
	LastName              string `json:"last_name"`
	RealName              string `json:"real_name"`
	RealNameNormalized    string `json:"real_name_normalized"`
	DisplayName           string `json:"display_name"`
	DisplayNameNormalized string `json:"display_name_normalized"`
	Email                 string `json:"email"`
	Skype                 string `json:"skype"`
	Phone                 string `json:"phone"`
	Image24               string `json:"image_24"`
	Image32               string `json:"image_32"`
	Image48               string `json:"image_48"`
	Image72               string `json:"image_72"`
	Image192              string `json:"image_192"`
	ImageOriginal         string `json:"image_original"`
	Title                 string `json:"title"`
	BotID                 string `json:"bot_id,omitempty"`
	ApiAppID              string `json:"api_app_id,omitempty"`
	StatusText            string `json:"status_text,omitempty"`
	StatusEmoji           string `json:"status_emoji,omitempty"`
	Team                  string `json:"team"`
}

// User contains all the information of a user
type User struct {
	ID                string      `json:"id"`
	TeamID            string      `json:"team_id"`
	Name              string      `json:"name"`
	Deleted           bool        `json:"deleted"`
	Color             string      `json:"color"`
	RealName          string      `json:"real_name"`
	TZ                string      `json:"tz,omitempty"`
	TZLabel           string      `json:"tz_label"`
	TZOffset          int         `json:"tz_offset"`
	Profile           UserProfile `json:"profile"`
	IsBot             bool        `json:"is_bot"`
	IsAdmin           bool        `json:"is_admin"`
	IsOwner           bool        `json:"is_owner"`
	IsPrimaryOwner    bool        `json:"is_primary_owner"`
	IsRestricted      bool        `json:"is_restricted"`
	IsUltraRestricted bool        `json:"is_ultra_restricted"`
	IsStranger        bool        `json:"is_stranger"`
	IsAppUser         bool        `json:"is_app_user"`
	Has2FA            bool        `json:"has_2fa"`
	HasFiles          bool        `json:"has_files"`
	Presence          string      `json:"presence"`
	Locale            string      `json:"locale"`
}

// UserPresence contains details about a user online status
type UserPresence struct {
	Presence        string   `json:"presence,omitempty"`
	Online          bool     `json:"online,omitempty"`
	AutoAway        bool     `json:"auto_away,omitempty"`
	ManualAway      bool     `json:"manual_away,omitempty"`
	ConnectionCount int      `json:"connection_count,omitempty"`
	LastActivity    JSONTime `json:"last_activity,omitempty"`
}

type UserIdentityResponse struct {
	User UserIdentity `json:"user"`
	Team TeamIdentity `json:"team"`
	SlackResponse
}

type UserIdentity struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Email    string `json:"email"`
	Image24  string `json:"image_24"`
	Image32  string `json:"image_32"`
	Image48  string `json:"image_48"`
	Image72  string `json:"image_72"`
	Image192 string `json:"image_192"`
	Image512 string `json:"image_512"`
}

type TeamIdentity struct {
	ID            string `json:"id"`
	Name          string `json:"name"`
	Domain        string `json:"domain"`
	Image34       string `json:"image_34"`
	Image44       string `json:"image_44"`
	Image68       string `json:"image_68"`
	Image88       string `json:"image_88"`
	Image102      string `json:"image_102"`
	Image132      string `json:"image_132"`
	Image230      string `json:"image_230"`
	ImageDefault  bool   `json:"image_default"`
	ImageOriginal string `json:"image_original"`
}

type userResponseFull struct {
	Members      []User                  `json:"members,omitempty"` // ListUsers
	User         `json:"user,omitempty"` // GetUserInfo
	UserPresence                         // GetUserPresence
	SlackResponse
}

type UserSetPhotoParams struct {
	CropX int
	CropY int
	CropW int
}

func NewUserSetPhotoParams() UserSetPhotoParams {
	return UserSetPhotoParams{
		CropX: DEFAULT_USER_PHOTO_CROP_X,
		CropY: DEFAULT_USER_PHOTO_CROP_Y,
		CropW: DEFAULT_USER_PHOTO_CROP_W,
	}
}

func userRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userResponseFull, error) {
	response := &userResponseFull{}
	err := postForm(ctx, client, SLACK_API+path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// GetUserPresence will retrieve the current presence status of given user.
func (api *Client) GetUserPresence(user string) (*UserPresence, error) {
	return api.GetUserPresenceContext(context.Background(), user)
}

// GetUserPresenceContext will retrieve the current presence status of given user with a custom context.
func (api *Client) GetUserPresenceContext(ctx context.Context, user string) (*UserPresence, error) {
	values := url.Values{
		"token": {api.token},
		"user":  {user},
	}

	response, err := userRequest(ctx, api.httpclient, "users.getPresence", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.UserPresence, nil
}

// GetUserInfo will retrieve the complete user information
func (api *Client) GetUserInfo(user string) (*User, error) {
	return api.GetUserInfoContext(context.Background(), user)
}

// GetUserInfoContext will retrieve the complete user information with a custom context
func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User, error) {
	values := url.Values{
		"token": {api.token},
		"user":  {user},
	}

	response, err := userRequest(ctx, api.httpclient, "users.info", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.User, nil
}

// GetUsers returns the list of users (with their detailed information)
func (api *Client) GetUsers() ([]User, error) {
	return api.GetUsersContext(context.Background())
}

// GetUsersContext returns the list of users (with their detailed information) with a custom context
func (api *Client) GetUsersContext(ctx context.Context) ([]User, error) {
	values := url.Values{
		"token":    {api.token},
		"presence": {"1"},
	}

	response, err := userRequest(ctx, api.httpclient, "users.list", values, api.debug)
	if err != nil {
		return nil, err
	}
	return response.Members, nil
}

// GetUserByEmail will retrieve the complete user information by email
func (api *Client) GetUserByEmail(email string) (*User, error) {
	return api.GetUserByEmailContext(context.Background(), email)
}

// GetUserByEmailContext will retrieve the complete user information by email with a custom context
func (api *Client) GetUserByEmailContext(ctx context.Context, email string) (*User, error) {
	values := url.Values{
		"token": {api.token},
		"email": {email},
	}
	response, err := userRequest(ctx, api.httpclient, "users.lookupByEmail", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.User, nil
}

// SetUserAsActive marks the currently authenticated user as active
func (api *Client) SetUserAsActive() error {
	return api.SetUserAsActiveContext(context.Background())
}

// SetUserAsActiveContext marks the currently authenticated user as active with a custom context
func (api *Client) SetUserAsActiveContext(ctx context.Context) (err error) {
	values := url.Values{
		"token": {api.token},
	}

	if _, err := userRequest(ctx, api.httpclient, "users.setActive", values, api.debug); err != nil {
		return err
	}

	return nil
}

// SetUserPresence changes the currently authenticated user presence
func (api *Client) SetUserPresence(presence string) error {
	return api.SetUserPresenceContext(context.Background(), presence)
}

// SetUserPresenceContext changes the currently authenticated user presence with a custom context
func (api *Client) SetUserPresenceContext(ctx context.Context, presence string) error {
	values := url.Values{
		"token":    {api.token},
		"presence": {presence},
	}

	_, err := userRequest(ctx, api.httpclient, "users.setPresence", values, api.debug)
	if err != nil {
		return err
	}
	return nil

}

// GetUserIdentity will retrieve user info available per identity scopes
func (api *Client) GetUserIdentity() (*UserIdentityResponse, error) {
	return api.GetUserIdentityContext(context.Background())
}

// GetUserIdentityContext will retrieve user info available per identity scopes with a custom context
func (api *Client) GetUserIdentityContext(ctx context.Context) (*UserIdentityResponse, error) {
	values := url.Values{
		"token": {api.token},
	}
	response := &UserIdentityResponse{}

	err := postForm(ctx, api.httpclient, SLACK_API+"users.identity", values, response, api.debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// SetUserPhoto changes the currently authenticated user's profile image
func (api *Client) SetUserPhoto(image string, params UserSetPhotoParams) error {
	return api.SetUserPhotoContext(context.Background(), image, params)
}

// SetUserPhotoContext changes the currently authenticated user's profile image using a custom context
func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params UserSetPhotoParams) error {
	response := &SlackResponse{}
	values := url.Values{
		"token": {api.token},
	}
	if params.CropX != DEFAULT_USER_PHOTO_CROP_X {
		values.Add("crop_x", string(params.CropX))
	}
	if params.CropY != DEFAULT_USER_PHOTO_CROP_Y {
		values.Add("crop_y", string(params.CropY))
	}
	if params.CropW != DEFAULT_USER_PHOTO_CROP_W {
		values.Add("crop_w", string(params.CropW))
	}

	err := postLocalWithMultipartResponse(ctx, api.httpclient, "users.setPhoto", image, "image", values, response, api.debug)
	if err != nil {
		return err
	}
	if !response.Ok {
		return errors.New(response.Error)
	}
	return nil
}

// DeleteUserPhoto deletes the current authenticated user's profile image
func (api *Client) DeleteUserPhoto() error {
	return api.DeleteUserPhotoContext(context.Background())
}

// DeleteUserPhotoContext deletes the current authenticated user's profile image with a custom context
func (api *Client) DeleteUserPhotoContext(ctx context.Context) error {
	response := &SlackResponse{}
	values := url.Values{
		"token": {api.token},
	}

	err := postForm(ctx, api.httpclient, SLACK_API+"users.deletePhoto", values, response, api.debug)
	if err != nil {
		return err
	}
	if !response.Ok {
		return errors.New(response.Error)
	}
	return nil
}

// SetUserCustomStatus will set a custom status and emoji for the currently
// authenticated user. If statusEmoji is "" and statusText is not, the Slack API
// will automatically set it to ":speech_balloon:". Otherwise, if both are ""
// the Slack API will unset the custom status/emoji.
func (api *Client) SetUserCustomStatus(statusText, statusEmoji string) error {
	return api.SetUserCustomStatusContext(context.Background(), statusText, statusEmoji)
}

// SetUserCustomStatusContext will set a custom status and emoji for the currently authenticated user with a custom context
//
// For more information see SetUserCustomStatus
func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, statusEmoji string) error {
	// XXX(theckman): this anonymous struct is for making requests to the Slack
	// API for setting and unsetting a User's Custom Status/Emoji. To change
	// these values we must provide a JSON document as the profile POST field.
	//
	// We use an anonymous struct over UserProfile because to unset the values
	// on the User's profile we cannot use the `json:"omitempty"` tag. This is
	// because an empty string ("") is what's used to unset the values. Check
	// out the API docs for more details:
	//
	// - https://api.slack.com/docs/presence-and-status#custom_status
	profile, err := json.Marshal(
		&struct {
			StatusText  string `json:"status_text"`
			StatusEmoji string `json:"status_emoji"`
		}{
			StatusText:  statusText,
			StatusEmoji: statusEmoji,
		},
	)

	if err != nil {
		return err
	}

	values := url.Values{
		"token":   {api.token},
		"profile": {string(profile)},
	}

	response := &userResponseFull{}
	if err = postForm(ctx, api.httpclient, SLACK_API+"users.profile.set", values, response, api.debug); err != nil {
		return err
	}

	if !response.Ok {
		return errors.New(response.Error)
	}

	return nil
}

// UnsetUserCustomStatus removes the custom status message for the currently
// authenticated user. This is a convenience method that wraps (*Client).SetUserCustomStatus().
func (api *Client) UnsetUserCustomStatus() error {
	return api.UnsetUserCustomStatusContext(context.Background())
}

// UnsetUserCustomStatusContext removes the custom status message for the currently authenticated user
// with a custom context. This is a convenience method that wraps (*Client).SetUserCustomStatus().
func (api *Client) UnsetUserCustomStatusContext(ctx context.Context) error {
	return api.SetUserCustomStatusContext(ctx, "", "")
}
<commit_msg>Use strconv.Itoa for converting Crop{X,Y,W} integer values to string values<commit_after>package slack

import (
	"context"
	"encoding/json"
	"errors"
	"net/url"
	"strconv"
)

const (
	DEFAULT_USER_PHOTO_CROP_X = -1
	DEFAULT_USER_PHOTO_CROP_Y = -1
	DEFAULT_USER_PHOTO_CROP_W = -1
)

// UserProfile contains all the information details of a given user
type UserProfile struct {
	FirstName             string `json:"first_name"`
	LastName              string `json:"last_name"`
	RealName              string `json:"real_name"`
	RealNameNormalized    string `json:"real_name_normalized"`
	DisplayName           string `json:"display_name"`
	DisplayNameNormalized string `json:"display_name_normalized"`
	Email                 string `json:"email"`
	Skype                 string `json:"skype"`
	Phone                 string `json:"phone"`
	Image24               string `json:"image_24"`
	Image32               string `json:"image_32"`
	Image48               string `json:"image_48"`
	Image72               string `json:"image_72"`
	Image192              string `json:"image_192"`
	ImageOriginal         string `json:"image_original"`
	Title                 string `json:"title"`
	BotID                 string `json:"bot_id,omitempty"`
	ApiAppID              string `json:"api_app_id,omitempty"`
	StatusText            string `json:"status_text,omitempty"`
	StatusEmoji           string `json:"status_emoji,omitempty"`
	Team                  string `json:"team"`
}

// User contains all the information of a user
type User struct {
	ID                string      `json:"id"`
	TeamID            string      `json:"team_id"`
	Name              string      `json:"name"`
	Deleted           bool        `json:"deleted"`
	Color             string      `json:"color"`
	RealName          string      `json:"real_name"`
	TZ                string      `json:"tz,omitempty"`
	TZLabel           string      `json:"tz_label"`
	TZOffset          int         `json:"tz_offset"`
	Profile           UserProfile `json:"profile"`
	IsBot             bool        `json:"is_bot"`
	IsAdmin           bool        `json:"is_admin"`
	IsOwner           bool        `json:"is_owner"`
	IsPrimaryOwner    bool        `json:"is_primary_owner"`
	IsRestricted      bool        `json:"is_restricted"`
	IsUltraRestricted bool        `json:"is_ultra_restricted"`
	IsStranger        bool        `json:"is_stranger"`
	IsAppUser         bool        `json:"is_app_user"`
	Has2FA            bool        `json:"has_2fa"`
	HasFiles          bool        `json:"has_files"`
	Presence          string      `json:"presence"`
	Locale            string      `json:"locale"`
}

// UserPresence contains details about a user online status
type UserPresence struct {
	Presence        string   `json:"presence,omitempty"`
	Online          bool     `json:"online,omitempty"`
	AutoAway        bool     `json:"auto_away,omitempty"`
	ManualAway      bool     `json:"manual_away,omitempty"`
	ConnectionCount int      `json:"connection_count,omitempty"`
	LastActivity    JSONTime `json:"last_activity,omitempty"`
}

type UserIdentityResponse struct {
	User UserIdentity `json:"user"`
	Team TeamIdentity `json:"team"`
	SlackResponse
}

type UserIdentity struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Email    string `json:"email"`
	Image24  string `json:"image_24"`
	Image32  string `json:"image_32"`
	Image48  string `json:"image_48"`
	Image72  string `json:"image_72"`
	Image192 string `json:"image_192"`
	Image512 string `json:"image_512"`
}

type TeamIdentity struct {
	ID            string `json:"id"`
	Name          string `json:"name"`
	Domain        string `json:"domain"`
	Image34       string `json:"image_34"`
	Image44       string `json:"image_44"`
	Image68       string `json:"image_68"`
	Image88       string `json:"image_88"`
	Image102      string `json:"image_102"`
	Image132      string `json:"image_132"`
	Image230      string `json:"image_230"`
	ImageDefault  bool   `json:"image_default"`
	ImageOriginal string `json:"image_original"`
}

type userResponseFull struct {
	Members      []User                  `json:"members,omitempty"` // ListUsers
	User         `json:"user,omitempty"` // GetUserInfo
	UserPresence                         // GetUserPresence
	SlackResponse
}

type UserSetPhotoParams struct {
	CropX int
	CropY int
	CropW int
}

func NewUserSetPhotoParams() UserSetPhotoParams {
	return UserSetPhotoParams{
		CropX: DEFAULT_USER_PHOTO_CROP_X,
		CropY: DEFAULT_USER_PHOTO_CROP_Y,
		CropW: DEFAULT_USER_PHOTO_CROP_W,
	}
}

func userRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userResponseFull, error) {
	response := &userResponseFull{}
	err := postForm(ctx, client, SLACK_API+path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// GetUserPresence will retrieve the current presence status of given user.
func (api *Client) GetUserPresence(user string) (*UserPresence, error) {
	return api.GetUserPresenceContext(context.Background(), user)
}

// GetUserPresenceContext will retrieve the current presence status of given user with a custom context.
func (api *Client) GetUserPresenceContext(ctx context.Context, user string) (*UserPresence, error) {
	values := url.Values{
		"token": {api.token},
		"user":  {user},
	}

	response, err := userRequest(ctx, api.httpclient, "users.getPresence", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.UserPresence, nil
}

// GetUserInfo will retrieve the complete user information
func (api *Client) GetUserInfo(user string) (*User, error) {
	return api.GetUserInfoContext(context.Background(), user)
}

// GetUserInfoContext will retrieve the complete user information with a custom context
func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User, error) {
	values := url.Values{
		"token": {api.token},
		"user":  {user},
	}

	response, err := userRequest(ctx, api.httpclient, "users.info", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.User, nil
}

// GetUsers returns the list of users (with their detailed information)
func (api *Client) GetUsers() ([]User, error) {
	return api.GetUsersContext(context.Background())
}

// GetUsersContext returns the list of users (with their detailed information) with a custom context
func (api *Client) GetUsersContext(ctx context.Context) ([]User, error) {
	values := url.Values{
		"token":    {api.token},
		"presence": {"1"},
	}

	response, err := userRequest(ctx, api.httpclient, "users.list", values, api.debug)
	if err != nil {
		return nil, err
	}
	return response.Members, nil
}

// GetUserByEmail will retrieve the complete user information by email
func (api *Client) GetUserByEmail(email string) (*User, error) {
	return api.GetUserByEmailContext(context.Background(), email)
}

// GetUserByEmailContext will retrieve the complete user information by email with a custom context
func (api *Client) GetUserByEmailContext(ctx context.Context, email string) (*User, error) {
	values := url.Values{
		"token": {api.token},
		"email": {email},
	}
	response, err := userRequest(ctx, api.httpclient, "users.lookupByEmail", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.User, nil
}

// SetUserAsActive marks the currently authenticated user as active
func (api *Client) SetUserAsActive() error {
	return api.SetUserAsActiveContext(context.Background())
}

// SetUserAsActiveContext marks the currently authenticated user as active with a custom context
func (api *Client) SetUserAsActiveContext(ctx context.Context) (err error) {
	values := url.Values{
		"token": {api.token},
	}

	if _, err := userRequest(ctx, api.httpclient, "users.setActive", values, api.debug); err != nil {
		return err
	}

	return nil
}

// SetUserPresence changes the currently authenticated user presence
func (api *Client) SetUserPresence(presence string) error {
	return api.SetUserPresenceContext(context.Background(), presence)
}

// SetUserPresenceContext changes the currently authenticated user presence with a custom context
func (api *Client) SetUserPresenceContext(ctx context.Context, presence string) error {
	values := url.Values{
		"token":    {api.token},
		"presence": {presence},
	}

	_, err := userRequest(ctx, api.httpclient, "users.setPresence", values, api.debug)
	if err != nil {
		return err
	}
	return nil

}

// GetUserIdentity will retrieve user info available per identity scopes
func (api *Client) GetUserIdentity() (*UserIdentityResponse, error) {
	return api.GetUserIdentityContext(context.Background())
}

// GetUserIdentityContext will retrieve user info available per identity scopes with a custom context
func (api *Client) GetUserIdentityContext(ctx context.Context) (*UserIdentityResponse, error) {
	values := url.Values{
		"token": {api.token},
	}
	response := &UserIdentityResponse{}

	err := postForm(ctx, api.httpclient, SLACK_API+"users.identity", values, response, api.debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// SetUserPhoto changes the currently authenticated user's profile image
func (api *Client) SetUserPhoto(image string, params UserSetPhotoParams) error {
	return api.SetUserPhotoContext(context.Background(), image, params)
}

// SetUserPhotoContext changes the currently authenticated user's profile image using a custom context
func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params UserSetPhotoParams) error {
	response := &SlackResponse{}
	values := url.Values{
		"token": {api.token},
	}
	if params.CropX != DEFAULT_USER_PHOTO_CROP_X {
		values.Add("crop_x", strconv.Itoa(params.CropX))
	}
	if params.CropY != DEFAULT_USER_PHOTO_CROP_Y {
		values.Add("crop_y", strconv.Itoa(params.CropX))
	}
	if params.CropW != DEFAULT_USER_PHOTO_CROP_W {
		values.Add("crop_w", strconv.Itoa(params.CropW))
	}

	err := postLocalWithMultipartResponse(ctx, api.httpclient, "users.setPhoto", image, "image", values, response, api.debug)
	if err != nil {
		return err
	}
	if !response.Ok {
		return errors.New(response.Error)
	}
	return nil
}

// DeleteUserPhoto deletes the current authenticated user's profile image
func (api *Client) DeleteUserPhoto() error {
	return api.DeleteUserPhotoContext(context.Background())
}

// DeleteUserPhotoContext deletes the current authenticated user's profile image with a custom context
func (api *Client) DeleteUserPhotoContext(ctx context.Context) error {
	response := &SlackResponse{}
	values := url.Values{
		"token": {api.token},
	}

	err := postForm(ctx, api.httpclient, SLACK_API+"users.deletePhoto", values, response, api.debug)
	if err != nil {
		return err
	}
	if !response.Ok {
		return errors.New(response.Error)
	}
	return nil
}

// SetUserCustomStatus will set a custom status and emoji for the currently
// authenticated user. If statusEmoji is "" and statusText is not, the Slack API
// will automatically set it to ":speech_balloon:". Otherwise, if both are ""
// the Slack API will unset the custom status/emoji.
func (api *Client) SetUserCustomStatus(statusText, statusEmoji string) error {
	return api.SetUserCustomStatusContext(context.Background(), statusText, statusEmoji)
}

// SetUserCustomStatusContext will set a custom status and emoji for the currently authenticated user with a custom context
//
// For more information see SetUserCustomStatus
func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, statusEmoji string) error {
	// XXX(theckman): this anonymous struct is for making requests to the Slack
	// API for setting and unsetting a User's Custom Status/Emoji. To change
	// these values we must provide a JSON document as the profile POST field.
	//
	// We use an anonymous struct over UserProfile because to unset the values
	// on the User's profile we cannot use the `json:"omitempty"` tag. This is
	// because an empty string ("") is what's used to unset the values. Check
	// out the API docs for more details:
	//
	// - https://api.slack.com/docs/presence-and-status#custom_status
	profile, err := json.Marshal(
		&struct {
			StatusText  string `json:"status_text"`
			StatusEmoji string `json:"status_emoji"`
		}{
			StatusText:  statusText,
			StatusEmoji: statusEmoji,
		},
	)

	if err != nil {
		return err
	}

	values := url.Values{
		"token":   {api.token},
		"profile": {string(profile)},
	}

	response := &userResponseFull{}
	if err = postForm(ctx, api.httpclient, SLACK_API+"users.profile.set", values, response, api.debug); err != nil {
		return err
	}

	if !response.Ok {
		return errors.New(response.Error)
	}

	return nil
}

// UnsetUserCustomStatus removes the custom status message for the currently
// authenticated user. This is a convenience method that wraps (*Client).SetUserCustomStatus().
func (api *Client) UnsetUserCustomStatus() error {
	return api.UnsetUserCustomStatusContext(context.Background())
}

// UnsetUserCustomStatusContext removes the custom status message for the currently authenticated user
// with a custom context. This is a convenience method that wraps (*Client).SetUserCustomStatus().
func (api *Client) UnsetUserCustomStatusContext(ctx context.Context) error {
	return api.SetUserCustomStatusContext(ctx, "", "")
}
<|endoftext|>


<commit_before>/*-
Copyright (c) 2016, Jörg Pernfuß <code.jpe@gmail.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Package somaauth implements the common key exchange and
// authentication bits between SOMA service and client.
package somaauth

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"hash"
	"math/big"
	"net"
	"net/http"
	"time"

	"golang.org/x/crypto/nacl/box"

	"github.com/dchest/blake2b"
	"github.com/satori/go.uuid"
)

type KexRequest struct {
	Public               string    `json:"public_key"`
	Request              uuid.UUID `json:"request,omitempty"`
	InitializationVector string    `json:"initialization_vector"`
	Token                string    `json:"password_token,omitempty"`
	// unexported private fields
	private  string    `json:"-"`
	peer     string    `json:"-"`
	sourceIP net.IP    `json:"-"`
	count    uint      `json:"-"`
	time     time.Time `json:"-"`
}

// NewKexRequest returns a KexRequest with a set random
// InitializationVector and new generated random keypair
func NewKexRequest() *KexRequest {
	var (
		err error
		bIV []byte
		k   KexRequest
	)

	k = KexRequest{}

	// generate the IV from 192bit random
	bIV = make([]byte, 24)
	if _, err = rand.Read(bIV); err != nil {
		return nil
	}
	k.InitializationVector = hex.EncodeToString(bIV)

	if err = k.GenerateNewKeypair(); err != nil {
		return nil
	}
	return &k
}

// GenerateNewKeypair generate a new public,private Keypair
func (k *KexRequest) GenerateNewKeypair() error {
	var (
		err                   error
		bRandom, bSecret      []byte
		hBlake                hash.Hash
		publicKey, privateKey *[32]byte
	)

	// generate keypair, starting with 1024bit random as
	// noted in DJB's docs
	bRandom = make([]byte, 128)
	if _, err = rand.Read(bRandom); err != nil {
		return err
	}

	// hash 1k down to 256bit
	hBlake = blake2b.New256()
	hBlake.Write(bRandom)
	bSecret = hBlake.Sum(nil)
	// generate keys
	if publicKey, privateKey, err = box.GenerateKey(
		bytes.NewReader(bSecret),
	); err != nil {
		return err
	}

	// set keys
	k.Public = hex.EncodeToString(publicKey[:])
	k.private = hex.EncodeToString(privateKey[:])
	return nil
}

// GenerateNewRequestID generate a new UUIDv4 for this Kex
func (k *KexRequest) GenerateNewRequestID() {
	k.Request = uuid.NewV4()
}

// IsExpired returns true if the Kex-Exchange is more than
// KexExpirySeconds seconds old
func (k *KexRequest) IsExpired() bool {
	return time.Now().UTC().After(k.time.UTC().Add(
		time.Duration(KexExpirySeconds) * time.Second))
}

// IsSameSource returns true if the paramter IP address is the
// same as the one recorded in the KexRequest
func (k *KexRequest) IsSameSource(ip net.IP) bool {
	return k.sourceIP.Equal(ip)
}

// NextNonce returns the next nonce to use. Nonces are built by
// interpreting the IV as a positive integer number and adding the
// count of requested nonces; thus implementing a simple non-repeating
// counter. The IV itself is never used as a nonce.
// Returns nil on error
func (k *KexRequest) NextNonce() *[24]byte {
	var (
		ib    []byte
		iv    *big.Int
		nonce *[24]byte
		err   error
	)

	k.count += 1
	if ib, err = hex.DecodeString(k.InitializationVector); err != nil {
		// hex decode failed
		return nil
	}

	// initialize
	iv = big.NewInt(0)
	// convert k.IV to Int by setting ib bytes as value
	iv.SetBytes(ib)
	// ensure the resulting number is positive
	iv.Abs(iv)
	// add the current counter value on top
	iv.Add(iv, big.NewInt(int64(k.count)))
	// check the resulting number still fits *[24]byte
	if len(iv.Bytes()) != 24 {
		return nil
	}

	// build nonce
	nonce = &[24]byte{}
	copy(nonce[:], iv.Bytes()[0:24])
	return nonce
}

// PeerKey returns the public key of the kex peer, or nil if it has
// not been set yet.
func (k *KexRequest) PeerKey() *[32]byte {
	var (
		pk   []byte
		err  error
		peer *[32]byte
	)

	// k.peer has not been set yet
	if k.peer == "" {
		return nil
	}

	// k.peer was set incorrect
	if pk, err = hex.DecodeString(k.peer); err != nil {
		return nil
	}

	// how?!
	if len(pk) != 32 {
		return nil
	}

	// return public key bytes
	peer = &[32]byte{}
	copy(peer[:], pk[0:32])
	return peer
}

// PrivateKey returns our private key for this kex, or nil if it has
// not been set yet.
func (k *KexRequest) PrivateKey() *[32]byte {
	var (
		pk      []byte
		err     error
		private *[32]byte
	)

	// k.private has not been set yet
	if k.private == "" {
		return nil
	}

	// k.private was set incorrect
	if pk, err = hex.DecodeString(k.private); err != nil {
		return nil
	}

	// again: how?
	if len(pk) != 32 {
		return nil
	}

	// return private key bytes
	private = &[32]byte{}
	copy(private[:], pk[0:32])
	return private
}

// PublicKey returns our public key for this key exchange, or nil if
// it has not been set yet.
func (k *KexRequest) PublicKey() *[32]byte {
	var (
		pk     []byte
		err    error
		public *[32]byte
	)

	// k.Public has not been set yet
	if k.Public == "" {
		return nil
	}

	// k.Public was set incorrect
	if pk, err = hex.DecodeString(k.Public); err != nil {
		return nil
	}

	// you know it.
	if len(pk) != 32 {
		return nil
	}

	// return public key bytes
	public = &[32]byte{}
	copy(public[:], pk[0:32])
	return public
}

// SetPeerKey sets the kex peer public key
func (k *KexRequest) SetPeerKey(pk *[32]byte) {
	k.peer = hex.EncodeToString(pk[:])
}

// SetRequestUUID sets the UUID of this Kex from a string
func (k *KexRequest) SetRequestUUID(s string) error {
	var err error

	if k.Request, err = uuid.FromString(s); err != nil {
		return err
	}

	return nil
}

// SetTimeUTC records the current time within the Kex
func (k *KexRequest) SetTimeUTC() {
	k.time = time.Now().UTC()
}

// SetIPAddress records the client's IP address
func (k *KexRequest) SetIPAddress(r *http.Request) {
	k.sourceIP = net.ParseIP(extractAddress(r.RemoteAddr))
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Rename KexRequest -> Kex<commit_after>/*-
Copyright (c) 2016, Jörg Pernfuß <code.jpe@gmail.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Package somaauth implements the common key exchange and
// authentication bits between SOMA service and client.
package somaauth

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"hash"
	"math/big"
	"net"
	"net/http"
	"time"

	"golang.org/x/crypto/nacl/box"

	"github.com/dchest/blake2b"
	"github.com/satori/go.uuid"
)

type Kex struct {
	Public               string    `json:"public_key"`
	Request              uuid.UUID `json:"request,omitempty"`
	InitializationVector string    `json:"initialization_vector"`
	Token                string    `json:"password_token,omitempty"`
	// unexported private fields
	private  string    `json:"-"`
	peer     string    `json:"-"`
	sourceIP net.IP    `json:"-"`
	count    uint      `json:"-"`
	time     time.Time `json:"-"`
}

// NewKex returns a Kex with a set random
// InitializationVector and new generated random keypair
func NewKex() *Kex {
	var (
		err error
		bIV []byte
		k   Kex
	)

	k = Kex{}

	// generate the IV from 192bit random
	bIV = make([]byte, 24)
	if _, err = rand.Read(bIV); err != nil {
		return nil
	}
	k.InitializationVector = hex.EncodeToString(bIV)

	if err = k.GenerateNewKeypair(); err != nil {
		return nil
	}
	return &k
}

// GenerateNewKeypair generate a new public,private Keypair
func (k *Kex) GenerateNewKeypair() error {
	var (
		err                   error
		bRandom, bSecret      []byte
		hBlake                hash.Hash
		publicKey, privateKey *[32]byte
	)

	// generate keypair, starting with 1024bit random as
	// noted in DJB's docs
	bRandom = make([]byte, 128)
	if _, err = rand.Read(bRandom); err != nil {
		return err
	}

	// hash 1k down to 256bit
	hBlake = blake2b.New256()
	hBlake.Write(bRandom)
	bSecret = hBlake.Sum(nil)
	// generate keys
	if publicKey, privateKey, err = box.GenerateKey(
		bytes.NewReader(bSecret),
	); err != nil {
		return err
	}

	// set keys
	k.Public = hex.EncodeToString(publicKey[:])
	k.private = hex.EncodeToString(privateKey[:])
	return nil
}

// GenerateNewRequestID generate a new UUIDv4 for this Kex
func (k *Kex) GenerateNewRequestID() {
	k.Request = uuid.NewV4()
}

// IsExpired returns true if the Kex-Exchange is more than
// KexExpirySeconds seconds old
func (k *Kex) IsExpired() bool {
	return time.Now().UTC().After(k.time.UTC().Add(
		time.Duration(KexExpirySeconds) * time.Second))
}

// IsSameSource returns true if the paramter IP address is the
// same as the one recorded in the Kex
func (k *Kex) IsSameSource(ip net.IP) bool {
	return k.sourceIP.Equal(ip)
}

// NextNonce returns the next nonce to use. Nonces are built by
// interpreting the IV as a positive integer number and adding the
// count of requested nonces; thus implementing a simple non-repeating
// counter. The IV itself is never used as a nonce.
// Returns nil on error
func (k *Kex) NextNonce() *[24]byte {
	var (
		ib    []byte
		iv    *big.Int
		nonce *[24]byte
		err   error
	)

	k.count += 1
	if ib, err = hex.DecodeString(k.InitializationVector); err != nil {
		// hex decode failed
		return nil
	}

	// initialize
	iv = big.NewInt(0)
	// convert k.IV to Int by setting ib bytes as value
	iv.SetBytes(ib)
	// ensure the resulting number is positive
	iv.Abs(iv)
	// add the current counter value on top
	iv.Add(iv, big.NewInt(int64(k.count)))
	// check the resulting number still fits *[24]byte
	if len(iv.Bytes()) != 24 {
		return nil
	}

	// build nonce
	nonce = &[24]byte{}
	copy(nonce[:], iv.Bytes()[0:24])
	return nonce
}

// PeerKey returns the public key of the kex peer, or nil if it has
// not been set yet.
func (k *Kex) PeerKey() *[32]byte {
	var (
		pk   []byte
		err  error
		peer *[32]byte
	)

	// k.peer has not been set yet
	if k.peer == "" {
		return nil
	}

	// k.peer was set incorrect
	if pk, err = hex.DecodeString(k.peer); err != nil {
		return nil
	}

	// how?!
	if len(pk) != 32 {
		return nil
	}

	// return public key bytes
	peer = &[32]byte{}
	copy(peer[:], pk[0:32])
	return peer
}

// PrivateKey returns our private key for this kex, or nil if it has
// not been set yet.
func (k *Kex) PrivateKey() *[32]byte {
	var (
		pk      []byte
		err     error
		private *[32]byte
	)

	// k.private has not been set yet
	if k.private == "" {
		return nil
	}

	// k.private was set incorrect
	if pk, err = hex.DecodeString(k.private); err != nil {
		return nil
	}

	// again: how?
	if len(pk) != 32 {
		return nil
	}

	// return private key bytes
	private = &[32]byte{}
	copy(private[:], pk[0:32])
	return private
}

// PublicKey returns our public key for this key exchange, or nil if
// it has not been set yet.
func (k *Kex) PublicKey() *[32]byte {
	var (
		pk     []byte
		err    error
		public *[32]byte
	)

	// k.Public has not been set yet
	if k.Public == "" {
		return nil
	}

	// k.Public was set incorrect
	if pk, err = hex.DecodeString(k.Public); err != nil {
		return nil
	}

	// you know it.
	if len(pk) != 32 {
		return nil
	}

	// return public key bytes
	public = &[32]byte{}
	copy(public[:], pk[0:32])
	return public
}

// SetPeerKey sets the kex peer public key
func (k *Kex) SetPeerKey(pk *[32]byte) {
	k.peer = hex.EncodeToString(pk[:])
}

// SetRequestUUID sets the UUID of this Kex from a string
func (k *Kex) SetRequestUUID(s string) error {
	var err error

	if k.Request, err = uuid.FromString(s); err != nil {
		return err
	}

	return nil
}

// SetTimeUTC records the current time within the Kex
func (k *Kex) SetTimeUTC() {
	k.time = time.Now().UTC()
}

// SetIPAddress records the client's IP address
func (k *Kex) SetIPAddress(r *http.Request) {
	k.sourceIP = net.ParseIP(extractAddress(r.RemoteAddr))
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	// static assets
	_ "github.com/SpectoLabs/hoverfly/statik"
	"github.com/rakyll/statik/fs"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/go-zoo/bone"
	"github.com/meatballhat/negroni-logrus"
)

// recordedRequests struct encapsulates payload data
type recordedRequests struct {
	Data []Payload `json:"data"`
}

type recordsCount struct {
	Count int `json:"count"`
}

type stateRequest struct {
	Mode        string `json:"mode"`
	Destination string `json:"destination"`
}

type messageResponse struct {
	Message string `json:"message"`
}

func (d *DBClient) startAdminInterface() {
	// starting admin interface
	mux := getBoneRouter(*d)
	n := negroni.Classic()

	loglevel := log.WarnLevel

	if d.cfg.verbose {
		loglevel = log.DebugLevel
	}

	n.Use(negronilogrus.NewCustomMiddleware(loglevel, &log.JSONFormatter{}, "admin"))
	n.UseHandler(mux)

	// admin interface starting message
	log.WithFields(log.Fields{
		"AdminPort": d.cfg.adminPort,
	}).Info("Admin interface is starting...")

	n.Run(fmt.Sprintf(":%s", d.cfg.adminPort))
}

// getBoneRouter returns mux for admin interface
func getBoneRouter(d DBClient) *bone.Mux {
	mux := bone.New()

	// preparing static assets for embedded admin
	statikFS, err := fs.New()
	if err != nil {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to load statikFS, admin UI might not work :(")
	}

	mux.Get("/records", http.HandlerFunc(d.AllRecordsHandler))
	mux.Delete("/records", http.HandlerFunc(d.DeleteAllRecordsHandler))
	mux.Post("/records", http.HandlerFunc(d.ImportRecordsHandler))

	mux.Get("/count", http.HandlerFunc(d.RecordsCount))

	mux.Get("/state", http.HandlerFunc(d.CurrentStateHandler))
	mux.Post("/state", http.HandlerFunc(d.StateHandler))

	mux.Handle("/*", http.FileServer(statikFS))

	return mux
}

// AllRecordsHandler returns JSON content type http response
func (d *DBClient) AllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	records, err := d.cache.GetAllRequests()

	if err == nil {

		w.Header().Set("Content-Type", "application/json")

		var response recordedRequests
		response.Data = records
		b, err := json.Marshal(response)

		if err != nil {
			log.Error(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			w.Write(b)
			return
		}
	} else {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to get data from cache!")

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(500) // can't process this entity
		return
	}
}

// RecordsCount returns number of captured requests as a JSON payload
func (d *DBClient) RecordsCount(w http.ResponseWriter, req *http.Request) {
	records, err := d.cache.GetAllRequests()

	if err == nil {

		w.Header().Set("Content-Type", "application/json")

		var response recordsCount
		response.Count = len(records)
		b, err := json.Marshal(response)

		if err != nil {
			log.Error(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			w.Write(b)
			return
		}
	} else {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to get data from cache!")

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(500) // can't process this entity
		return
	}
}

// categoryWSFilterHandler is used for searching categories based on names and keywords through the websocket
func (d *DBClient) StatsWSHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	for {
		messageType, p, err := conn.ReadMessage()
		if err != nil {
			return
		}
		log.WithFields(log.Fields{
			"message": string(p),
		}).Info("Got message...")

		for _ = range time.Tick(1 * time.Second) {

			stats := d.counter.Flush()
			var sr statsResponse
			sr.Stats = stats

			b, err := json.Marshal(sr)

			if err = conn.WriteMessage(messageType, b); err != nil {
				log.WithFields(log.Fields{
					"message": p,
					"error":   err.Error(),
				}).Error("Got error when writing message...")
				return
			}
		}

	}

}

// ImportRecordsHandler - accepts JSON payload and saves it to cache
func (d *DBClient) ImportRecordsHandler(w http.ResponseWriter, req *http.Request) {

	var requests recordedRequests

	defer req.Body.Close()
	body, err := ioutil.ReadAll(req.Body)

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	var response messageResponse

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		response.Message = "Bad request. Nothing to import!"
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &requests)

	if err != nil {
		w.WriteHeader(422) // can't process this entity
		return
	}

	payloads := requests.Data
	if len(payloads) > 0 {
		for _, pl := range payloads {
			bts, err := pl.encode()
			if err != nil {
				log.WithFields(log.Fields{
					"error": err.Error(),
				}).Error("Failed to encode payload")
			} else {
				// recalculating request hash and storing it in database
				r := request{details: pl.Request}
				d.cache.Set([]byte(r.hash()), bts)
			}
		}
		response.Message = fmt.Sprintf("%d requests imported successfully", len(payloads))
	} else {
		response.Message = "Bad request. Nothing to import!"
		w.WriteHeader(400)
	}

	b, err := json.Marshal(response)
	w.Write(b)

}

// DeleteAllRecordsHandler - deletes all captured requests
func (d *DBClient) DeleteAllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	err := d.cache.DeleteBucket(d.cache.requestsBucket)

	w.Header().Set("Content-Type", "application/json")

	var response messageResponse
	if err != nil {
		if err.Error() == "bucket not found" {
			response.Message = fmt.Sprintf("No records found")
			w.WriteHeader(200)
		} else {
			response.Message = fmt.Sprintf("Something went wrong: %s", err.Error())
			w.WriteHeader(500)
		}
	} else {
		response.Message = "Proxy cache deleted successfuly"
		w.WriteHeader(200)
	}
	b, err := json.Marshal(response)

	w.Write(b)
	return
}

// CurrentStateHandler returns current state
func (d *DBClient) CurrentStateHandler(w http.ResponseWriter, req *http.Request) {
	var resp stateRequest
	resp.Mode = d.cfg.GetMode()
	resp.Destination = d.cfg.destination

	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)
}

// StateHandler handles current proxy state
func (d *DBClient) StateHandler(w http.ResponseWriter, r *http.Request) {
	var sr stateRequest

	// this is mainly for testing, since when you create
	if r.Body == nil {
		r.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("")))
	}

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &sr)

	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(400) // can't process this entity
		return
	}

	availableModes := map[string]bool{
		"virtualize": true,
		"capture":    true,
		"modify":     true,
		"synthesize": true,
	}

	if !availableModes[sr.Mode] {
		log.WithFields(log.Fields{
			"suppliedMode": sr.Mode,
		}).Error("Wrong mode found, can't change state")
		http.Error(w, "Bad mode supplied, available modes: virtualize, capture, modify, synthesize.", 400)
		return
	}

	log.WithFields(log.Fields{
		"newState": sr.Mode,
		"body":     string(body),
	}).Info("Handling state change request!")

	// setting new state
	d.cfg.SetMode(sr.Mode)

	var resp stateRequest
	resp.Mode = d.cfg.GetMode()
	resp.Destination = d.cfg.destination
	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)

}
<commit_msg>return current stats through simple http api<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	// static assets
	_ "github.com/SpectoLabs/hoverfly/statik"
	"github.com/rakyll/statik/fs"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/go-zoo/bone"
	"github.com/meatballhat/negroni-logrus"
)

// recordedRequests struct encapsulates payload data
type recordedRequests struct {
	Data []Payload `json:"data"`
}

type recordsCount struct {
	Count int `json:"count"`
}

type stateRequest struct {
	Mode        string `json:"mode"`
	Destination string `json:"destination"`
}

type messageResponse struct {
	Message string `json:"message"`
}

func (d *DBClient) startAdminInterface() {
	// starting admin interface
	mux := getBoneRouter(*d)
	n := negroni.Classic()

	loglevel := log.WarnLevel

	if d.cfg.verbose {
		loglevel = log.DebugLevel
	}

	n.Use(negronilogrus.NewCustomMiddleware(loglevel, &log.JSONFormatter{}, "admin"))
	n.UseHandler(mux)

	// admin interface starting message
	log.WithFields(log.Fields{
		"AdminPort": d.cfg.adminPort,
	}).Info("Admin interface is starting...")

	n.Run(fmt.Sprintf(":%s", d.cfg.adminPort))
}

// getBoneRouter returns mux for admin interface
func getBoneRouter(d DBClient) *bone.Mux {
	mux := bone.New()

	// preparing static assets for embedded admin
	statikFS, err := fs.New()
	if err != nil {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to load statikFS, admin UI might not work :(")
	}

	mux.Get("/records", http.HandlerFunc(d.AllRecordsHandler))
	mux.Delete("/records", http.HandlerFunc(d.DeleteAllRecordsHandler))
	mux.Post("/records", http.HandlerFunc(d.ImportRecordsHandler))

	mux.Get("/count", http.HandlerFunc(d.RecordsCount))

	mux.Get("/state", http.HandlerFunc(d.CurrentStateHandler))
	mux.Post("/state", http.HandlerFunc(d.StateHandler))

	mux.Handle("/*", http.FileServer(statikFS))

	return mux
}

// AllRecordsHandler returns JSON content type http response
func (d *DBClient) AllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	records, err := d.cache.GetAllRequests()

	if err == nil {

		w.Header().Set("Content-Type", "application/json")

		var response recordedRequests
		response.Data = records
		b, err := json.Marshal(response)

		if err != nil {
			log.Error(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			w.Write(b)
			return
		}
	} else {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to get data from cache!")

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(500) // can't process this entity
		return
	}
}

// RecordsCount returns number of captured requests as a JSON payload
func (d *DBClient) RecordsCount(w http.ResponseWriter, req *http.Request) {
	records, err := d.cache.GetAllRequests()

	if err == nil {

		w.Header().Set("Content-Type", "application/json")

		var response recordsCount
		response.Count = len(records)
		b, err := json.Marshal(response)

		if err != nil {
			log.Error(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			w.Write(b)
			return
		}
	} else {
		log.WithFields(log.Fields{
			"Error": err.Error(),
		}).Error("Failed to get data from cache!")

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(500) // can't process this entity
		return
	}
}

func (d *DBClient) StatsHandler(w http.ResponseWriter, req *http.Request) {
	stats := d.counter.Flush()

	var sr statsResponse
	sr.Stats = stats

	w.Header().Set("Content-Type", "application/json")

	b, err := json.Marshal(sr)

	if err != nil {
		log.Error(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else {
		w.Write(b)
		return
	}

}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// categoryWSFilterHandler is used for searching categories based on names and keywords through the websocket
func (d *DBClient) StatsWSHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	for {
		messageType, p, err := conn.ReadMessage()
		if err != nil {
			return
		}
		log.WithFields(log.Fields{
			"message": string(p),
		}).Info("Got message...")

		for _ = range time.Tick(1 * time.Second) {

			stats := d.counter.Flush()
			var sr statsResponse
			sr.Stats = stats

			b, err := json.Marshal(sr)

			if err = conn.WriteMessage(messageType, b); err != nil {
				log.WithFields(log.Fields{
					"message": p,
					"error":   err.Error(),
				}).Error("Got error when writing message...")
				return
			}
		}

	}

}

// ImportRecordsHandler - accepts JSON payload and saves it to cache
func (d *DBClient) ImportRecordsHandler(w http.ResponseWriter, req *http.Request) {

	var requests recordedRequests

	defer req.Body.Close()
	body, err := ioutil.ReadAll(req.Body)

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	var response messageResponse

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		response.Message = "Bad request. Nothing to import!"
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &requests)

	if err != nil {
		w.WriteHeader(422) // can't process this entity
		return
	}

	payloads := requests.Data
	if len(payloads) > 0 {
		for _, pl := range payloads {
			bts, err := pl.encode()
			if err != nil {
				log.WithFields(log.Fields{
					"error": err.Error(),
				}).Error("Failed to encode payload")
			} else {
				// recalculating request hash and storing it in database
				r := request{details: pl.Request}
				d.cache.Set([]byte(r.hash()), bts)
			}
		}
		response.Message = fmt.Sprintf("%d requests imported successfully", len(payloads))
	} else {
		response.Message = "Bad request. Nothing to import!"
		w.WriteHeader(400)
	}

	b, err := json.Marshal(response)
	w.Write(b)

}

// DeleteAllRecordsHandler - deletes all captured requests
func (d *DBClient) DeleteAllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	err := d.cache.DeleteBucket(d.cache.requestsBucket)

	w.Header().Set("Content-Type", "application/json")

	var response messageResponse
	if err != nil {
		if err.Error() == "bucket not found" {
			response.Message = fmt.Sprintf("No records found")
			w.WriteHeader(200)
		} else {
			response.Message = fmt.Sprintf("Something went wrong: %s", err.Error())
			w.WriteHeader(500)
		}
	} else {
		response.Message = "Proxy cache deleted successfuly"
		w.WriteHeader(200)
	}
	b, err := json.Marshal(response)

	w.Write(b)
	return
}

// CurrentStateHandler returns current state
func (d *DBClient) CurrentStateHandler(w http.ResponseWriter, req *http.Request) {
	var resp stateRequest
	resp.Mode = d.cfg.GetMode()
	resp.Destination = d.cfg.destination

	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)
}

// StateHandler handles current proxy state
func (d *DBClient) StateHandler(w http.ResponseWriter, r *http.Request) {
	var sr stateRequest

	// this is mainly for testing, since when you create
	if r.Body == nil {
		r.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("")))
	}

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &sr)

	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(400) // can't process this entity
		return
	}

	availableModes := map[string]bool{
		"virtualize": true,
		"capture":    true,
		"modify":     true,
		"synthesize": true,
	}

	if !availableModes[sr.Mode] {
		log.WithFields(log.Fields{
			"suppliedMode": sr.Mode,
		}).Error("Wrong mode found, can't change state")
		http.Error(w, "Bad mode supplied, available modes: virtualize, capture, modify, synthesize.", 400)
		return
	}

	log.WithFields(log.Fields{
		"newState": sr.Mode,
		"body":     string(body),
	}).Info("Handling state change request!")

	// setting new state
	d.cfg.SetMode(sr.Mode)

	var resp stateRequest
	resp.Mode = d.cfg.GetMode()
	resp.Destination = d.cfg.destination
	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)

}
<|endoftext|>