<commit_before>package dialog

// Very simple for the unix 'dialog' utility; just executes it

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

	"golang.org/x/crypto/ssh/terminal"
)

const (
	DIALOG_PROC         = "dialog"
	DEFAULT_HEIGHT      = 20
	DEFAULT_WIDTH       = 60
	DEFAULT_MENU_HEIGHT = 15
)

var (
	DefaultTitle  = "Title"
	ErrorDialogRc = "/etc/error.dialogrc"
)

func run(args []string) (string, error) {
	return runStdin(args, os.Stdin)
}

func runStdin(args []string, stdin io.Reader) (string, error) {
	choiceOutput := &bytes.Buffer{}
	cmdArgs := append([]string{"--output-fd", "3"}, args...)
	cmd := exec.Command(DIALOG_PROC, cmdArgs...)
	cmd.Stdin = stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	//cmd.Env = []string{"TERM=xterm"}

	outpiperd, outpipewr, err := os.Pipe()
	if err != nil {
		return "", err
	}

	// input (pty) for ctrl characters etc is stdin.
	// cmd output (menu option etc) goes to output buffer
	cmd.ExtraFiles = []*os.File{outpipewr}
	doneChan := make(chan error)
	go func() {
		_, err := io.Copy(choiceOutput, outpiperd)
		doneChan <- err
	}()

	err = cmd.Run()
	outpipewr.Close()
	<-doneChan
	choice := string(choiceOutput.Bytes())
	//fmt.Fprintf(os.Stdout, "args %#v choice is %v err %v\n", cmdArgs, choice, err)
	//fmt.Fprintf(os.Stderr, "args %#v choice is %v err %v\n", cmdArgs, choice, err)
	return choice, err
}

type ChildDialog struct {
	Dialog
	Crumb      func() string
	ParentMenu *Menu
	MenuOption *MenuOption
}

type Dialog interface {
	Run(string) (Dialog, error) // new child dialog, new sibling dialog, error.
}

type Common struct {
	DialogRc string
	Title    string
	Width    int
	Height   int
}

func (c *Common) title() string {
	if c.Title != "" {
		return c.Title
	}
	return DefaultTitle
}
func (c *Common) width() int {
	if c.Width > 0 {
		return c.Width
	}
	if c.Width < 0 {
		// use screen width - x
		if w, _, err := terminal.GetSize(int(os.Stdin.Fd())); err == nil {
			return w + c.Width
		}
	}
	return DEFAULT_WIDTH
}
func (c *Common) height() int {
	if c.Height > 0 {
		return c.Height
	}
	if c.Height < 0 {
		// use screen height - y
		if _, h, err := terminal.GetSize(int(os.Stdin.Fd())); err == nil {
			return h + c.Height
		}
	}
	return DEFAULT_HEIGHT
}
func (c *Common) runArgs() []string {
	return []string{"--title", c.title()}
}

type MsgBox struct {
	Common
	Text        string
	NextSibling Dialog
}

func (m *MsgBox) Run(crumbs string) (Dialog, error) {
	args := m.Common.runArgs()
	args = append(args,
		"--msgbox", m.Text,
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()))

	_, err := run(args)
	if err != nil {
		return nil, err
	}
	return m.NextSibling, nil
}

type Pause struct {
	Common
	Text        string
	Seconds     int
	NextSibling Dialog
}

func (m *Pause) Run(crumbs string) (Dialog, error) {
	args := m.Common.runArgs()
	args = append(args,
		"--pause", crumbs+"\\n"+m.Text,
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()),
		strconv.Itoa(m.Seconds))

	_, err := run(args)
	if err != nil {
		return nil, err
	}
	return m.NextSibling, nil
}

type Menu struct {
	Common
	Text       func() string
	MenuHeight int
	DefaultKey string
	Options    func() ([]MenuOption, error)
}

type MenuOption struct {
	Key            string
	Text           string
	Next           Dialog
	NextDefaultKey string
	CrumbText      func() string
}

func (m *Menu) menuHeight(optlen int) int {
	if m.MenuHeight > 0 {
		return m.MenuHeight
	}
	return optlen
}

func (m *Menu) Run(crumbs string) (Dialog, error) {
	opts, err := m.Options()
	if err != nil {
		return nil, err
	}

	args := m.Common.runArgs()
	if m.DefaultKey != "" {
		args = append(args, "--default-item", m.DefaultKey)
	}
	text := crumbs
	if m.Text != nil {
		text = text + "\\n" + m.Text()
	}
	args = append(args,
		"--menu", text,
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()),
		strconv.Itoa(m.menuHeight(len(opts))))
	for _, mo := range opts {
		args = append(args, mo.Key, mo.Text)
	}
	k, err := run(args)
	if err != nil {
		return nil, err
	}
	for _, mo := range opts {
		if mo.Key == k {
			crumbFunc := mo.CrumbText
			if crumbFunc == nil {
				crumbFunc = func() string {
					return mo.Text
				}
			}
			return ChildDialog{mo.Next, crumbFunc, m, &mo}, nil
		}
	}
	return nil, fmt.Errorf("returned option '%s' not found", k)
}

type InputBox struct {
	Common
	Text        func() string
	Value       *string
	Validate    func(string) (string, bool)
	NextSibling Dialog
}

func (m *InputBox) Run(crumbs string) (Dialog, error) {
	if m.Value == nil {
		return nil, fmt.Errorf("inputbox has no result ptr")
	}
	if m.Text == nil {
		return nil, fmt.Errorf("inputbox has no text func")
	}
	args := m.Common.runArgs()
	args = append(args,
		"--inputbox", crumbs+"\\n"+m.Text(),
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()),
		*m.Value)
	k, err := run(args)
	if err != nil {
		return nil, err
	} else if m.Validate != nil {
		_, ok := m.Validate(k)
		if !ok {
			// TODO FIXME: flash error, return new sibling
		}
	}
	*m.Value = k
	return m.NextSibling, nil
}

type CheckListBox struct {
	Common
	Text        func() string
	Items       []CheckListItem
	Validate    func(string) (string, bool)
	NextSibling Dialog
}

type CheckListItem struct {
	Name  string
	Value *bool
}

func (m *CheckListBox) itemArgs() []string {
	ret := []string{}
	for i, item := range m.Items {
		// tag
		ret = append(ret, strconv.Itoa(i))
		// item
		ret = append(ret, item.Name)
		// status
		if *item.Value {
			ret = append(ret, "on")
		} else {
			ret = append(ret, "off")
		}
	}
	return ret
}

func (m *CheckListBox) Run(crumbs string) (Dialog, error) {
	for _, item := range m.Items {
		if item.Value == nil {
			return nil, fmt.Errorf("checklistbox has no result ptr")
		}
	}
	if m.Text == nil {
		return nil, fmt.Errorf("checklistbox has no text func")
	}
	args := m.Common.runArgs()
	args = append(args,
		"--checklist", crumbs+"\\n"+m.Text(),
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()),
		strconv.Itoa(len(m.Items)))
	args = append(args, m.itemArgs()...)
	k, err := run(args)
	if err != nil {
		return nil, err
	} else if m.Validate != nil {
		_, ok := m.Validate(k)
		if !ok {
			// TODO FIXME: flash error, return new sibling
		}
	}

	// parse returned values
	setIndices := map[int]bool{}
	for _, v := range regexp.MustCompile("\\s+").Split(strings.TrimSpace(k), -1) {
		if vi, err := strconv.Atoi(v); err == nil && vi >= 0 && vi < len(m.Items) {
			setIndices[vi] = true
		}
	}
	for i, item := range m.Items {
		*item.Value = setIndices[i]
	}

	return m.NextSibling, nil
}

type MixedForm struct {
	Text       string
	FormHeight int
	Items      []MixedFormItem
}

type MixedFormItem struct {
	Label string
}

//--mixedform text height width formheight [ label y x item y x flen ilen itype ]

type ProgramBox struct {
	Common
	Text    string
	Program func(io.WriteCloser) error
	Next    Dialog
}

func (m *ProgramBox) Run(crumbs string) (Dialog, error) {
	if m.Program == nil {
		return nil, fmt.Errorf("programbox has no program callback set")
	}
	piperd, pipewr := io.Pipe()

	// spawn the program
	doneChan := make(chan error)
	go func() {
		doneChan <- m.Program(pipewr)
	}()

	args := m.Common.runArgs()
	args = append(args,
		"--programbox", crumbs+"\\n"+m.Text,
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()))
	_, err := runStdin(args, piperd)
	if err != nil {
		return nil, err
	}
	if err := <-doneChan; err != nil {
		return nil, err
	}
	return m.Next, nil
}
<commit_msg>use getter and setter func, not value ptr for inputbox<commit_after>package dialog

// Very simple for the unix 'dialog' utility; just executes it

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

	"golang.org/x/crypto/ssh/terminal"
)

const (
	DIALOG_PROC         = "dialog"
	DEFAULT_HEIGHT      = 20
	DEFAULT_WIDTH       = 60
	DEFAULT_MENU_HEIGHT = 15
)

var (
	DefaultTitle  = "Title"
	ErrorDialogRc = "/etc/error.dialogrc"
)

func run(args []string) (string, error) {
	return runStdin(args, os.Stdin)
}

func runStdin(args []string, stdin io.Reader) (string, error) {
	choiceOutput := &bytes.Buffer{}
	cmdArgs := append([]string{"--output-fd", "3"}, args...)
	cmd := exec.Command(DIALOG_PROC, cmdArgs...)
	cmd.Stdin = stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	//cmd.Env = []string{"TERM=xterm"}

	outpiperd, outpipewr, err := os.Pipe()
	if err != nil {
		return "", err
	}

	// input (pty) for ctrl characters etc is stdin.
	// cmd output (menu option etc) goes to output buffer
	cmd.ExtraFiles = []*os.File{outpipewr}
	doneChan := make(chan error)
	go func() {
		_, err := io.Copy(choiceOutput, outpiperd)
		doneChan <- err
	}()

	err = cmd.Run()
	outpipewr.Close()
	<-doneChan
	choice := string(choiceOutput.Bytes())
	//fmt.Fprintf(os.Stdout, "args %#v choice is %v err %v\n", cmdArgs, choice, err)
	//fmt.Fprintf(os.Stderr, "args %#v choice is %v err %v\n", cmdArgs, choice, err)
	return choice, err
}

type ChildDialog struct {
	Dialog
	Crumb      func() string
	ParentMenu *Menu
	MenuOption *MenuOption
}

type Dialog interface {
	Run(string) (Dialog, error) // new child dialog, new sibling dialog, error.
}

type Common struct {
	DialogRc string
	Title    string
	Width    int
	Height   int
}

func (c *Common) title() string {
	if c.Title != "" {
		return c.Title
	}
	return DefaultTitle
}
func (c *Common) width() int {
	if c.Width > 0 {
		return c.Width
	}
	if c.Width < 0 {
		// use screen width - x
		if w, _, err := terminal.GetSize(int(os.Stdin.Fd())); err == nil {
			return w + c.Width
		}
	}
	return DEFAULT_WIDTH
}
func (c *Common) height() int {
	if c.Height > 0 {
		return c.Height
	}
	if c.Height < 0 {
		// use screen height - y
		if _, h, err := terminal.GetSize(int(os.Stdin.Fd())); err == nil {
			return h + c.Height
		}
	}
	return DEFAULT_HEIGHT
}
func (c *Common) runArgs() []string {
	return []string{"--title", c.title()}
}

type MsgBox struct {
	Common
	Text        string
	NextSibling Dialog
}

func (m *MsgBox) Run(crumbs string) (Dialog, error) {
	args := m.Common.runArgs()
	args = append(args,
		"--msgbox", m.Text,
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()))

	_, err := run(args)
	if err != nil {
		return nil, err
	}
	return m.NextSibling, nil
}

type Pause struct {
	Common
	Text        string
	Seconds     int
	NextSibling Dialog
}

func (m *Pause) Run(crumbs string) (Dialog, error) {
	args := m.Common.runArgs()
	args = append(args,
		"--pause", crumbs+"\\n"+m.Text,
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()),
		strconv.Itoa(m.Seconds))

	_, err := run(args)
	if err != nil {
		return nil, err
	}
	return m.NextSibling, nil
}

type Menu struct {
	Common
	Text       func() string
	MenuHeight int
	DefaultKey string
	Options    func() ([]MenuOption, error)
}

type MenuOption struct {
	Key            string
	Text           string
	Next           Dialog
	NextDefaultKey string
	CrumbText      func() string
}

func (m *Menu) menuHeight(optlen int) int {
	if m.MenuHeight > 0 {
		return m.MenuHeight
	}
	return optlen
}

func (m *Menu) Run(crumbs string) (Dialog, error) {
	opts, err := m.Options()
	if err != nil {
		return nil, err
	}

	args := m.Common.runArgs()
	if m.DefaultKey != "" {
		args = append(args, "--default-item", m.DefaultKey)
	}
	text := crumbs
	if m.Text != nil {
		text = text + "\\n" + m.Text()
	}
	args = append(args,
		"--menu", text,
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()),
		strconv.Itoa(m.menuHeight(len(opts))))
	for _, mo := range opts {
		args = append(args, mo.Key, mo.Text)
	}
	k, err := run(args)
	if err != nil {
		return nil, err
	}
	for _, mo := range opts {
		if mo.Key == k {
			crumbFunc := mo.CrumbText
			if crumbFunc == nil {
				crumbFunc = func() string {
					return mo.Text
				}
			}
			return ChildDialog{mo.Next, crumbFunc, m, &mo}, nil
		}
	}
	return nil, fmt.Errorf("returned option '%s' not found", k)
}

type InputBox struct {
	Common
	Text        func() string
	GetValue    func() string
	SetValue    func(string)
	Validate    func(string) (string, bool)
	NextSibling Dialog
}

func (m *InputBox) Run(crumbs string) (Dialog, error) {
	if m.GetValue == nil {
		return nil, fmt.Errorf("inputbox has no GetValue func")
	}
	if m.SetValue == nil {
		return nil, fmt.Errorf("inputbox has no SetValue func")
	}
	if m.Text == nil {
		return nil, fmt.Errorf("inputbox has no text func")
	}
	args := m.Common.runArgs()
	args = append(args,
		"--inputbox", crumbs+"\\n"+m.Text(),
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()),
		m.GetValue())
	k, err := run(args)
	if err != nil {
		return nil, err
	} else if m.Validate != nil {
		_, ok := m.Validate(k)
		if !ok {
			// TODO FIXME: flash error, return new sibling
		}
	}
	m.SetValue(k)
	return m.NextSibling, nil
}

type CheckListBox struct {
	Common
	Text        func() string
	Items       []CheckListItem
	Validate    func(string) (string, bool)
	NextSibling Dialog
}

type CheckListItem struct {
	Name  string
	Value *bool
}

func (m *CheckListBox) itemArgs() []string {
	ret := []string{}
	for i, item := range m.Items {
		// tag
		ret = append(ret, strconv.Itoa(i))
		// item
		ret = append(ret, item.Name)
		// status
		if *item.Value {
			ret = append(ret, "on")
		} else {
			ret = append(ret, "off")
		}
	}
	return ret
}

func (m *CheckListBox) Run(crumbs string) (Dialog, error) {
	for _, item := range m.Items {
		if item.Value == nil {
			return nil, fmt.Errorf("checklistbox has no result ptr")
		}
	}
	if m.Text == nil {
		return nil, fmt.Errorf("checklistbox has no text func")
	}
	args := m.Common.runArgs()
	args = append(args,
		"--checklist", crumbs+"\\n"+m.Text(),
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()),
		strconv.Itoa(len(m.Items)))
	args = append(args, m.itemArgs()...)
	k, err := run(args)
	if err != nil {
		return nil, err
	} else if m.Validate != nil {
		_, ok := m.Validate(k)
		if !ok {
			// TODO FIXME: flash error, return new sibling
		}
	}

	// parse returned values
	setIndices := map[int]bool{}
	for _, v := range regexp.MustCompile("\\s+").Split(strings.TrimSpace(k), -1) {
		if vi, err := strconv.Atoi(v); err == nil && vi >= 0 && vi < len(m.Items) {
			setIndices[vi] = true
		}
	}
	for i, item := range m.Items {
		*item.Value = setIndices[i]
	}

	return m.NextSibling, nil
}

type MixedForm struct {
	Text       string
	FormHeight int
	Items      []MixedFormItem
}

type MixedFormItem struct {
	Label string
}

//--mixedform text height width formheight [ label y x item y x flen ilen itype ]

type ProgramBox struct {
	Common
	Text    string
	Program func(io.WriteCloser) error
	Next    Dialog
}

func (m *ProgramBox) Run(crumbs string) (Dialog, error) {
	if m.Program == nil {
		return nil, fmt.Errorf("programbox has no program callback set")
	}
	piperd, pipewr := io.Pipe()

	// spawn the program
	doneChan := make(chan error)
	go func() {
		doneChan <- m.Program(pipewr)
	}()

	args := m.Common.runArgs()
	args = append(args,
		"--programbox", crumbs+"\\n"+m.Text,
		strconv.Itoa(m.height()),
		strconv.Itoa(m.width()))
	_, err := runStdin(args, piperd)
	if err != nil {
		return nil, err
	}
	if err := <-doneChan; err != nil {
		return nil, err
	}
	return m.Next, nil
}
<|endoftext|>


<commit_before>package resingo

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"time"

	"github.com/guregu/null"
)

//ErrDeviceNotFound is returned when there is  no device returned from an API call
//for devices.
//
// We assume that, a valid API call will alwayss return with valid results, so
// lack of any matching devices means we didn't find anything.
//
// NOTE: This should have been handled by resin api. Probably with status codes
// and the response body indicating nothing was dound.
var ErrDeviceNotFound = errors.New("resingo: device not found")

//Device represent the information about a resin device
type Device struct {
	ID            int64  `json:"id"`
	Name          string `json:"name"`
	WebAccessible bool   `json:"is_web_accessible"`
	Type          string `json:"device_type"`
	Application   struct {
		ID       int64 `json:"__id"`
		Metadata struct {
			URI string `json:"uri"`
		} `json:"__deferred"`
	} `json:"application"`
	UUID                  string    `json:"uuid"`
	User                  User      `json:"user"`
	Actor                 int64     `json:"actor"`
	IsOnline              bool      `json:"is_online"`
	Commit                string    `json:"commit"`
	Status                string    `json:"status"`
	LastConnectivityEvent null.Time `json:"last_connectivity_event"`
	IP                    string    `json:"ip_address"`
	VPNAddr               string    `json:"vpn_address"`
	PublicAddr            string    `json:"public_address"`
	SuprevisorVersion     string    `json:"supervisor_version"`
	Note                  string    `json:"note"`
	OsVersion             string    `json:"os_version"`
	Location              string    `json:"location"`
	Longitude             string    `json:"longitude"`
	Latitude              string    `json:"latitude"`
}

//DevGetAll returns all devices that belong to the user who authorized the
//context ctx.
func DevGetAll(ctx *Context) ([]*Device, error) {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint("device")
	b, err := doJSON(ctx, "GET", uri, h, nil, nil)
	if err != nil {
		return nil, err
	}
	var devRes = struct {
		D []*Device `json:"d"`
	}{}
	err = json.Unmarshal(b, &devRes)
	if err != nil {
		return nil, err
	}
	return devRes.D, nil
}

//GenerateUUID generates uuid suitable for resin devices
func GenerateUUID() (string, error) {
	src := make([]byte, 31)
	_, err := rand.Read(src)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(src), nil
}

//DevRegister registers the device with uuid to the the application with name
//appName.
func DevRegister(ctx *Context, appName, uuid string) (*Device, error) {
	app, err := AppGetByName(ctx, appName)
	if err != nil {
		return nil, err
	}
	appKey, err := AppGetAPIKey(ctx, appName)
	if err != nil {
		return nil, err
	}
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint("device")
	data := make(map[string]interface{})
	data["user"] = ctx.Config.UserID()
	data["device_type"] = app.DeviceType
	data["application"] = app.ID
	data["registered_at"] = time.Now().Unix()
	data["uuid"] = uuid
	data["apikey"] = appKey
	body, err := marhsalReader(data)
	if err != nil {
		return nil, err
	}
	b, err := doJSON(ctx, "POST", uri, h, nil, body)
	//fmt.Println(string(b))
	if err != nil {
		return nil, err
	}
	rst := &Device{}
	err = json.Unmarshal(b, rst)
	if err != nil {
		return nil, err
	}
	return rst, nil

}

//DevGetByUUID returns the device with the given uuid.
func DevGetByUUID(ctx *Context, uuid string) (*Device, error) {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint("device")
	params := make(url.Values)
	params.Set("filter", "uuid")
	params.Set("eq", uuid)
	b, err := doJSON(ctx, "GET", uri, h, params, nil)
	if err != nil {
		return nil, err
	}
	var devRes = struct {
		D []*Device `json:"d"`
	}{}
	//fmt.Println(string(b))
	err = json.Unmarshal(b, &devRes)
	if err != nil {
		return nil, err
	}
	if len(devRes.D) > 0 {
		return devRes.D[0], nil
	}
	return nil, ErrDeviceNotFound
}

//DevGetByName returns the device with the given name
func DevGetByName(ctx *Context, name string) (*Device, error) {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint("device")
	params := make(url.Values)
	params.Set("filter", "name")
	params.Set("eq", name)
	b, err := doJSON(ctx, "GET", uri, h, params, nil)
	if err != nil {
		return nil, err
	}
	//fmt.Println(string(b))
	var devRes = struct {
		D []*Device `json:"d"`
	}{}
	err = json.Unmarshal(b, &devRes)
	if err != nil {
		return nil, err
	}
	if len(devRes.D) > 0 {
		//fmt.Println(*devRes.D[0])
		return devRes.D[0], nil
	}
	return nil, ErrDeviceNotFound
}

//DevIsOnline return true if the device with uuid is online and false otherwise.
//Any errors encountered is returned too.
func DevIsOnline(ctx *Context, uuid string) (bool, error) {
	dev, err := DevGetByUUID(ctx, uuid)
	if err != nil {
		return false, err
	}
	return dev.IsOnline, nil
}

//DevGetAllByApp returns all devices that are registered to the application with
//the given appID
func DevGetAllByApp(ctx *Context, appID int64) ([]*Device, error) {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("application(%d)", appID))
	params := make(url.Values)
	params.Set("expand", "device")
	b, err := doJSON(ctx, "GET", uri, h, params, nil)
	if err != nil {
		return nil, err
	}
	//var devRes = struct {
	//D []*Device `json:"d"`
	//}{}
	var devRes = struct {
		D []struct {
			Device []*Device `json:"device"`
		} `json:"d"`
	}{}
	//fmt.Println(string(b))
	err = json.Unmarshal(b, &devRes)
	if err != nil {
		return nil, err
	}
	if len(devRes.D) > 0 {
		return devRes.D[0].Device, nil
	}
	return nil, ErrDeviceNotFound
}

//DevRename renames the device with uuid to nwName
func DevRename(ctx *Context, uuid, newName string) error {
	_, err := DevGetByUUID(ctx, uuid)
	if err != nil {
		return err
	}
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint("device")
	params := make(url.Values)
	params.Set("Filter", "uuid")
	params.Set("eq", uuid)
	data := make(map[string]interface{})
	data["name"] = newName
	body, err := marhsalReader(data)
	if err != nil {
		return err
	}
	b, err := doJSON(ctx, "PATCH", uri, h, params, body)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}

//DevGetApp returns the application in which the device belongs to. This
//function is convenient only when you are interested on other information about
//the application.
//
// If your intention is only to retrieve the applicayion id, then just use this
// instead.
//
//	dev,err:=DevGetByUUID(ctx,<uuid goes here>)
//	if err!=nil{
//		//handle error error
//	}
//	// you can now access the application id like this
//	fmt.Println(dev.Application.ID
func DevGetApp(ctx *Context, uuid string) (*Application, error) {
	dev, err := DevGetByUUID(ctx, uuid)
	if err != nil {
		return nil, err
	}
	return AppGetByID(ctx, dev.Application.ID)
}

//DevEnableURL enables the device url. This allows the device to be accessed
//anywhere using the url which uses resin vpn.
//
// NOTE: It is awskward to retrurn OK rather than the url which was enabled.
func DevEnableURL(ctx *Context, uuid string) error {
	dev, err := DevGetByUUID(ctx, uuid)
	if err != nil {
		return err
	}
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("device(%d)", dev.ID))
	params := make(url.Values)
	params.Set("filter", "uuid")
	params.Set("eq", uuid)
	data := make(map[string]interface{})
	data["is_web_accessible"] = true
	body, err := marhsalReader(data)
	if err != nil {
		return err
	}
	b, err := doJSON(ctx, "PATCH", uri, h, params, body)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}

//DevDisableURL diables the deice url, making it not accessible via the web.
func DevDisableURL(ctx *Context, uuid string) error {
	dev, err := DevGetByUUID(ctx, uuid)
	if err != nil {
		return err
	}
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("device(%d)", dev.ID))
	data := make(map[string]interface{})
	data["is_web_accessible"] = false
	body, err := marhsalReader(data)
	if err != nil {
		return err
	}
	b, err := doJSON(ctx, "PATCH", uri, h, nil, body)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}

//DevDelete deletes the device with the given id
func DevDelete(ctx *Context, id int64) error {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("device(%d)", id))
	b, err := doJSON(ctx, "DELETE", uri, h, nil, nil)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}

//DevNote add note to the device
func DevNote(ctx *Context, id int64, note string) error {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("device(%d)", id))
	data := make(map[string]interface{})
	data["note"] = note
	body, err := marhsalReader(data)
	if err != nil {
		return err
	}
	b, err := doJSON(ctx, "PATCH", uri, h, nil, body)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}

//DevMove moves the device to a different application
func DevMove(ctx *Context, id int64, appID int64) error {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("device(%d)", id))
	data := make(map[string]interface{})
	data["application"] = appID
	body, err := marhsalReader(data)
	if err != nil {
		return err
	}
	b, err := doJSON(ctx, "PATCH", uri, h, nil, body)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}
<commit_msg>Add LogdChannel field to Device<commit_after>package resingo

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"time"

	"github.com/guregu/null"
)

//ErrDeviceNotFound is returned when there is  no device returned from an API call
//for devices.
//
// We assume that, a valid API call will alwayss return with valid results, so
// lack of any matching devices means we didn't find anything.
//
// NOTE: This should have been handled by resin api. Probably with status codes
// and the response body indicating nothing was dound.
var ErrDeviceNotFound = errors.New("resingo: device not found")

//Device represent the information about a resin device
type Device struct {
	ID            int64  `json:"id"`
	Name          string `json:"name"`
	WebAccessible bool   `json:"is_web_accessible"`
	Type          string `json:"device_type"`
	Application   struct {
		ID       int64 `json:"__id"`
		Metadata struct {
			URI string `json:"uri"`
		} `json:"__deferred"`
	} `json:"application"`
	UUID                  string    `json:"uuid"`
	User                  User      `json:"user"`
	Actor                 int64     `json:"actor"`
	IsOnline              bool      `json:"is_online"`
	Commit                string    `json:"commit"`
	Status                string    `json:"status"`
	LastConnectivityEvent null.Time `json:"last_connectivity_event"`
	IP                    string    `json:"ip_address"`
	VPNAddr               string    `json:"vpn_address"`
	PublicAddr            string    `json:"public_address"`
	SuprevisorVersion     string    `json:"supervisor_version"`
	Note                  string    `json:"note"`
	OsVersion             string    `json:"os_version"`
	Location              string    `json:"location"`
	Longitude             string    `json:"longitude"`
	Latitude              string    `json:"latitude"`
	LogsChannel           string    `json:"logs_channel"`
}

//DevGetAll returns all devices that belong to the user who authorized the
//context ctx.
func DevGetAll(ctx *Context) ([]*Device, error) {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint("device")
	b, err := doJSON(ctx, "GET", uri, h, nil, nil)
	if err != nil {
		return nil, err
	}
	var devRes = struct {
		D []*Device `json:"d"`
	}{}
	err = json.Unmarshal(b, &devRes)
	if err != nil {
		return nil, err
	}
	return devRes.D, nil
}

//GenerateUUID generates uuid suitable for resin devices
func GenerateUUID() (string, error) {
	src := make([]byte, 31)
	_, err := rand.Read(src)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(src), nil
}

//DevRegister registers the device with uuid to the the application with name
//appName.
func DevRegister(ctx *Context, appName, uuid string) (*Device, error) {
	app, err := AppGetByName(ctx, appName)
	if err != nil {
		return nil, err
	}
	appKey, err := AppGetAPIKey(ctx, appName)
	if err != nil {
		return nil, err
	}
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint("device")
	data := make(map[string]interface{})
	data["user"] = ctx.Config.UserID()
	data["device_type"] = app.DeviceType
	data["application"] = app.ID
	data["registered_at"] = time.Now().Unix()
	data["uuid"] = uuid
	data["apikey"] = appKey
	body, err := marhsalReader(data)
	if err != nil {
		return nil, err
	}
	b, err := doJSON(ctx, "POST", uri, h, nil, body)
	//fmt.Println(string(b))
	if err != nil {
		return nil, err
	}
	rst := &Device{}
	err = json.Unmarshal(b, rst)
	if err != nil {
		return nil, err
	}
	return rst, nil

}

//DevGetByUUID returns the device with the given uuid.
func DevGetByUUID(ctx *Context, uuid string) (*Device, error) {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint("device")
	params := make(url.Values)
	params.Set("filter", "uuid")
	params.Set("eq", uuid)
	b, err := doJSON(ctx, "GET", uri, h, params, nil)
	if err != nil {
		return nil, err
	}
	var devRes = struct {
		D []*Device `json:"d"`
	}{}
	//fmt.Println(string(b))
	err = json.Unmarshal(b, &devRes)
	if err != nil {
		return nil, err
	}
	if len(devRes.D) > 0 {
		return devRes.D[0], nil
	}
	return nil, ErrDeviceNotFound
}

//DevGetByName returns the device with the given name
func DevGetByName(ctx *Context, name string) (*Device, error) {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint("device")
	params := make(url.Values)
	params.Set("filter", "name")
	params.Set("eq", name)
	b, err := doJSON(ctx, "GET", uri, h, params, nil)
	if err != nil {
		return nil, err
	}
	//fmt.Println(string(b))
	var devRes = struct {
		D []*Device `json:"d"`
	}{}
	err = json.Unmarshal(b, &devRes)
	if err != nil {
		return nil, err
	}
	if len(devRes.D) > 0 {
		//fmt.Println(*devRes.D[0])
		return devRes.D[0], nil
	}
	return nil, ErrDeviceNotFound
}

//DevIsOnline return true if the device with uuid is online and false otherwise.
//Any errors encountered is returned too.
func DevIsOnline(ctx *Context, uuid string) (bool, error) {
	dev, err := DevGetByUUID(ctx, uuid)
	if err != nil {
		return false, err
	}
	return dev.IsOnline, nil
}

//DevGetAllByApp returns all devices that are registered to the application with
//the given appID
func DevGetAllByApp(ctx *Context, appID int64) ([]*Device, error) {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("application(%d)", appID))
	params := make(url.Values)
	params.Set("expand", "device")
	b, err := doJSON(ctx, "GET", uri, h, params, nil)
	if err != nil {
		return nil, err
	}
	//var devRes = struct {
	//D []*Device `json:"d"`
	//}{}
	var devRes = struct {
		D []struct {
			Device []*Device `json:"device"`
		} `json:"d"`
	}{}
	//fmt.Println(string(b))
	err = json.Unmarshal(b, &devRes)
	if err != nil {
		return nil, err
	}
	if len(devRes.D) > 0 {
		return devRes.D[0].Device, nil
	}
	return nil, ErrDeviceNotFound
}

//DevRename renames the device with uuid to nwName
func DevRename(ctx *Context, uuid, newName string) error {
	_, err := DevGetByUUID(ctx, uuid)
	if err != nil {
		return err
	}
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint("device")
	params := make(url.Values)
	params.Set("Filter", "uuid")
	params.Set("eq", uuid)
	data := make(map[string]interface{})
	data["name"] = newName
	body, err := marhsalReader(data)
	if err != nil {
		return err
	}
	b, err := doJSON(ctx, "PATCH", uri, h, params, body)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}

//DevGetApp returns the application in which the device belongs to. This
//function is convenient only when you are interested on other information about
//the application.
//
// If your intention is only to retrieve the applicayion id, then just use this
// instead.
//
//	dev,err:=DevGetByUUID(ctx,<uuid goes here>)
//	if err!=nil{
//		//handle error error
//	}
//	// you can now access the application id like this
//	fmt.Println(dev.Application.ID
func DevGetApp(ctx *Context, uuid string) (*Application, error) {
	dev, err := DevGetByUUID(ctx, uuid)
	if err != nil {
		return nil, err
	}
	return AppGetByID(ctx, dev.Application.ID)
}

//DevEnableURL enables the device url. This allows the device to be accessed
//anywhere using the url which uses resin vpn.
//
// NOTE: It is awskward to retrurn OK rather than the url which was enabled.
func DevEnableURL(ctx *Context, uuid string) error {
	dev, err := DevGetByUUID(ctx, uuid)
	if err != nil {
		return err
	}
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("device(%d)", dev.ID))
	params := make(url.Values)
	params.Set("filter", "uuid")
	params.Set("eq", uuid)
	data := make(map[string]interface{})
	data["is_web_accessible"] = true
	body, err := marhsalReader(data)
	if err != nil {
		return err
	}
	b, err := doJSON(ctx, "PATCH", uri, h, params, body)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}

//DevDisableURL diables the deice url, making it not accessible via the web.
func DevDisableURL(ctx *Context, uuid string) error {
	dev, err := DevGetByUUID(ctx, uuid)
	if err != nil {
		return err
	}
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("device(%d)", dev.ID))
	data := make(map[string]interface{})
	data["is_web_accessible"] = false
	body, err := marhsalReader(data)
	if err != nil {
		return err
	}
	b, err := doJSON(ctx, "PATCH", uri, h, nil, body)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}

//DevDelete deletes the device with the given id
func DevDelete(ctx *Context, id int64) error {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("device(%d)", id))
	b, err := doJSON(ctx, "DELETE", uri, h, nil, nil)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}

//DevNote add note to the device
func DevNote(ctx *Context, id int64, note string) error {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("device(%d)", id))
	data := make(map[string]interface{})
	data["note"] = note
	body, err := marhsalReader(data)
	if err != nil {
		return err
	}
	b, err := doJSON(ctx, "PATCH", uri, h, nil, body)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}

//DevMove moves the device to a different application
func DevMove(ctx *Context, id int64, appID int64) error {
	h := authHeader(ctx.Config.AuthToken)
	uri := ctx.Config.APIEndpoint(fmt.Sprintf("device(%d)", id))
	data := make(map[string]interface{})
	data["application"] = appID
	body, err := marhsalReader(data)
	if err != nil {
		return err
	}
	b, err := doJSON(ctx, "PATCH", uri, h, nil, body)
	if err != nil {
		return err
	}
	if string(b) != "OK" {
		return errors.New("bad response")
	}
	return nil
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/tdewolff/minify"
	"github.com/tdewolff/minify/css"
	"github.com/tdewolff/minify/html"
	"github.com/tdewolff/minify/js"
	jm "github.com/tdewolff/minify/json"

	"strings"

	"io/ioutil"

	"github.com/mundipagg/boleto-api/bank"
	"github.com/mundipagg/boleto-api/boleto"
	"github.com/mundipagg/boleto-api/config"
	"github.com/mundipagg/boleto-api/db"
	"github.com/mundipagg/boleto-api/log"
	"github.com/mundipagg/boleto-api/models"
)

//Regista um boleto em um determinado banco
func registerBoleto(c *gin.Context) {

	if _, hasErr := c.Get("error"); hasErr {
		return
	}
	_boleto, _ := c.Get("boleto")
	_bank, _ := c.Get("bank")
	bol := _boleto.(models.BoletoRequest)
	bank := _bank.(bank.Bank)
	lg := bank.Log()
	lg.Operation = "RegisterBoleto"
	lg.NossoNumero = bol.Title.OurNumber
	lg.Recipient = bol.Recipient.Name
	lg.RequestKey = bol.RequestKey
	lg.BankName = bank.GetBankNameIntegration()

	resp, err := bank.ProcessBoleto(&bol)
	if checkError(c, err, lg) {
		return
	}

	st := http.StatusOK
	if len(resp.Errors) > 0 {

		if resp.StatusCode > 0 {
			st = resp.StatusCode
		} else {
			st = http.StatusBadRequest
		}

	} else {
		mongo, errMongo := db.CreateMongo(lg)

		boView := models.NewBoletoView(bol, resp, bank.GetBankNameIntegration())
		mID, _ := boView.ID.MarshalText()
		resp.ID = string(mID)
		resp.Links = boView.Links

		if errMongo == nil {
			errMongo = mongo.SaveBoleto(boView)
		}

		redis := db.CreateRedis()

		if errMongo != nil {
			b := minifyJSON(boView)

			err = redis.SetBoletoJSON(b, resp.ID, lg)
			if checkError(c, err, lg) {
				return
			}
		}

		bhtml, err := boleto.HTML(boView, "html")
		s := minifyString(bhtml, "text/html")
		redis.SetBoletoHTML(s, resp.ID, lg)

	}
	c.JSON(st, resp)
	c.Set("boletoResponse", resp)
}

func getBoleto(c *gin.Context) {
	c.Status(200)

	id := c.Query("id")
	fmt := c.Query("fmt")

	log := log.CreateLog()
	log.Operation = "GetBoleto"

	redis := db.CreateRedis()

	b := redis.GetBoletoHTMLByID(id, log)

	if b == "" {
		mongo, errMongo := db.CreateMongo(log)
		if checkError(c, errMongo, log) {
			return
		}

		boView, err := mongo.GetBoletoByID(id)
		if checkError(c, err, log) {
			return
		}

		bhtml, err := boleto.HTML(boView, "html")
		b = minifyString(bhtml, "text/html")

		redis.SetBoletoHTML(b, id, log)
	}

	if fmt == "html" {
		c.Header("Content-Type", "text/html; charset=utf-8")
		c.Writer.WriteString(b)
	} else {
		c.Header("Content-Type", "application/pdf")
		bpdf, err := toPdf(b)

		if err != nil {
			c.Header("Content-Type", "application/json")
			checkError(c, models.NewInternalServerError(err.Error(), "internal error"), log)
			c.Abort()
		} else {
			c.Writer.Write(bpdf)
		}

	}

}

func toPdf(page string) ([]byte, error) {
	url := config.Get().PdfAPIURL
	payload := strings.NewReader(page)
	if req, err := http.NewRequest("POST", url, payload); err != nil {
		return nil, err
	} else if res, err := http.DefaultClient.Do(req); err != nil {
		return nil, err
	} else {
		defer res.Body.Close()
		return ioutil.ReadAll(res.Body)
	}
}

func getBoletoByID(c *gin.Context) {
	id := c.Param("id")
	log := log.CreateLog()
	log.Operation = "GetBoletoV1"

	mongo, errDb := db.CreateMongo(log)
	if errDb != nil {
		checkError(c, models.NewInternalServerError("MP500", "Erro interno"), log)
	}
	boleto, err := mongo.GetBoletoByID(id)
	if err != nil {
		checkError(c, models.NewHTTPNotFound("MP404", "Boleto não encontrado"), nil)
		return
	}
	c.JSON(http.StatusOK, boleto)
}

//minifyJSON converte um model BoletoView para um JSON/STRING
func minifyJSON(m models.BoletoView) string {
	j, _ := json.Marshal(m)

	return minifyString(string(j), "application/json")
}

func minifyString(mString, tp string) string {
	m := minify.New()
	m.Add("text/html", &html.Minifier{
		KeepDocumentTags:        true,
		KeepEndTags:             true,
		KeepWhitespace:          false,
		KeepConditionalComments: true,
	})
	m.AddFunc("text/css", css.Minify)
	m.AddFunc("text/javascript", js.Minify)
	m.AddFunc("application/json", jm.Minify)

	s, err := m.String(tp, mString)

	if err != nil {
		return mString
	} else {
		return s
	}
}
<commit_msg>:fire: Corrige variavel não usada<commit_after>package api

import (
	"encoding/json"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/tdewolff/minify"
	"github.com/tdewolff/minify/css"
	"github.com/tdewolff/minify/html"
	"github.com/tdewolff/minify/js"
	jm "github.com/tdewolff/minify/json"

	"strings"

	"io/ioutil"

	"github.com/mundipagg/boleto-api/bank"
	"github.com/mundipagg/boleto-api/boleto"
	"github.com/mundipagg/boleto-api/config"
	"github.com/mundipagg/boleto-api/db"
	"github.com/mundipagg/boleto-api/log"
	"github.com/mundipagg/boleto-api/models"
)

//Regista um boleto em um determinado banco
func registerBoleto(c *gin.Context) {

	if _, hasErr := c.Get("error"); hasErr {
		return
	}
	_boleto, _ := c.Get("boleto")
	_bank, _ := c.Get("bank")
	bol := _boleto.(models.BoletoRequest)
	bank := _bank.(bank.Bank)
	lg := bank.Log()
	lg.Operation = "RegisterBoleto"
	lg.NossoNumero = bol.Title.OurNumber
	lg.Recipient = bol.Recipient.Name
	lg.RequestKey = bol.RequestKey
	lg.BankName = bank.GetBankNameIntegration()

	resp, err := bank.ProcessBoleto(&bol)
	if checkError(c, err, lg) {
		return
	}

	st := http.StatusOK
	if len(resp.Errors) > 0 {

		if resp.StatusCode > 0 {
			st = resp.StatusCode
		} else {
			st = http.StatusBadRequest
		}

	} else {
		mongo, errMongo := db.CreateMongo(lg)

		boView := models.NewBoletoView(bol, resp, bank.GetBankNameIntegration())
		mID, _ := boView.ID.MarshalText()
		resp.ID = string(mID)
		resp.Links = boView.Links

		if errMongo == nil {
			errMongo = mongo.SaveBoleto(boView)
		}

		redis := db.CreateRedis()

		if errMongo != nil {
			b := minifyJSON(boView)

			err = redis.SetBoletoJSON(b, resp.ID, lg)
			if checkError(c, err, lg) {
				return
			}
		}

		bhtml, _ := boleto.HTML(boView, "html")
		s := minifyString(bhtml, "text/html")
		redis.SetBoletoHTML(s, resp.ID, lg)

	}
	c.JSON(st, resp)
	c.Set("boletoResponse", resp)
}

func getBoleto(c *gin.Context) {
	c.Status(200)

	id := c.Query("id")
	fmt := c.Query("fmt")

	log := log.CreateLog()
	log.Operation = "GetBoleto"

	redis := db.CreateRedis()

	b := redis.GetBoletoHTMLByID(id, log)

	if b == "" {
		mongo, errMongo := db.CreateMongo(log)
		if checkError(c, errMongo, log) {
			return
		}

		boView, err := mongo.GetBoletoByID(id)
		if checkError(c, err, log) {
			return
		}

		bhtml, err := boleto.HTML(boView, "html")
		b = minifyString(bhtml, "text/html")

		redis.SetBoletoHTML(b, id, log)
	}

	if fmt == "html" {
		c.Header("Content-Type", "text/html; charset=utf-8")
		c.Writer.WriteString(b)
	} else {
		c.Header("Content-Type", "application/pdf")
		bpdf, err := toPdf(b)

		if err != nil {
			c.Header("Content-Type", "application/json")
			checkError(c, models.NewInternalServerError(err.Error(), "internal error"), log)
			c.Abort()
		} else {
			c.Writer.Write(bpdf)
		}

	}

}

func toPdf(page string) ([]byte, error) {
	url := config.Get().PdfAPIURL
	payload := strings.NewReader(page)
	if req, err := http.NewRequest("POST", url, payload); err != nil {
		return nil, err
	} else if res, err := http.DefaultClient.Do(req); err != nil {
		return nil, err
	} else {
		defer res.Body.Close()
		return ioutil.ReadAll(res.Body)
	}
}

func getBoletoByID(c *gin.Context) {
	id := c.Param("id")
	log := log.CreateLog()
	log.Operation = "GetBoletoV1"

	mongo, errDb := db.CreateMongo(log)
	if errDb != nil {
		checkError(c, models.NewInternalServerError("MP500", "Erro interno"), log)
	}
	boleto, err := mongo.GetBoletoByID(id)
	if err != nil {
		checkError(c, models.NewHTTPNotFound("MP404", "Boleto não encontrado"), nil)
		return
	}
	c.JSON(http.StatusOK, boleto)
}

//minifyJSON converte um model BoletoView para um JSON/STRING
func minifyJSON(m models.BoletoView) string {
	j, _ := json.Marshal(m)

	return minifyString(string(j), "application/json")
}

func minifyString(mString, tp string) string {
	m := minify.New()
	m.Add("text/html", &html.Minifier{
		KeepDocumentTags:        true,
		KeepEndTags:             true,
		KeepWhitespace:          false,
		KeepConditionalComments: true,
	})
	m.AddFunc("text/css", css.Minify)
	m.AddFunc("text/javascript", js.Minify)
	m.AddFunc("application/json", jm.Minify)

	s, err := m.String(tp, mString)

	if err != nil {
		return mString
	} else {
		return s
	}
}
<|endoftext|>


<commit_before>package api

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
)

var (
	errRedirect         = errors.New("redirect")
	defaultHandlerSetup sync.Once
)

// Config is used to configure the creation of the client.
type Config struct {
	// Address is the address of the Vault server. This should be a complete
	// URL such as "http://vault.example.com". If you need a custom SSL
	// cert or want to enable insecure mode, you need to specify a custom
	// HttpClient.
	Address string

	// HttpClient is the HTTP client to use, which will currently always be
	// http.DefaultClient. This is used to control redirect behavior.
	HttpClient *http.Client
}

// DefaultConfig returns a default configuration for the client. It is
// safe to modify the return value of this function.
//
// The default Address is https://127.0.0.1:8200, but this can be overridden by
// setting the `VAULT_ADDR` environment variable.
func DefaultConfig() *Config {
	config := &Config{
		Address:    "https://127.0.0.1:8200",
		HttpClient: http.DefaultClient,
	}

	if addr := os.Getenv("VAULT_ADDR"); addr != "" {
		config.Address = addr
	}

	return config
}

// Client is the client to the Vault API. Create a client with
// NewClient.
type Client struct {
	addr   *url.URL
	config *Config
	token  string
}

// NewClient returns a new client for the given configuration.
//
// If the environment variable `VAULT_TOKEN` is present, the token will be
// automatically added to the client. Otherwise, you must manually call
// `SetToken()`.
func NewClient(c *Config) (*Client, error) {
	u, err := url.Parse(c.Address)
	if err != nil {
		return nil, err
	}

	if c.HttpClient == http.DefaultClient {
		defaultHandlerSetup.Do(func() {
			// Ensure redirects are not automatically followed
			c.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
				return errRedirect
			}
		})
	}

	client := &Client{
		addr:   u,
		config: c,
	}

	if token := os.Getenv("VAULT_TOKEN"); token != "" {
		client.SetToken(token)
	}

	return client, nil
}

// Token returns the access token being used by this client. It will
// return the empty string if there is no token set.
func (c *Client) Token() string {
	return c.token
}

// SetToken sets the token directly. This won't perform any auth
// verification, it simply sets the token properly for future requests.
func (c *Client) SetToken(v string) {
	c.token = v
}

// ClearToken deletes the token if it is set or does nothing otherwise.
func (c *Client) ClearToken() {
	c.token = ""
}

// NewRequest creates a new raw request object to query the Vault server
// configured for this client. This is an advanced method and generally
// doesn't need to be called externally.
func (c *Client) NewRequest(method, path string) *Request {
	req := &Request{
		Method: method,
		URL: &url.URL{
			Scheme: c.addr.Scheme,
			Host:   c.addr.Host,
			Path:   path,
		},
		ClientToken: c.token,
		Params:      make(map[string][]string),
	}

	return req
}

// RawRequest performs the raw request given. This request may be against
// a Vault server not configured with this client. This is an advanced operation
// that generally won't need to be called externally.
func (c *Client) RawRequest(r *Request) (*Response, error) {
	redirectCount := 0
START:
	req, err := r.ToHTTP()
	if err != nil {
		return nil, err
	}

	var result *Response
	resp, err := c.config.HttpClient.Do(req)
	if resp != nil {
		result = &Response{Response: resp}
	}
	if err != nil {
		if urlErr, ok := err.(*url.Error); ok && urlErr.Err == errRedirect {
			err = nil
		} else if strings.Contains(err.Error(), "tls: oversized") {
			err = fmt.Errorf(
				"%s\n\n"+
					"This error usually means that the server is running with TLS disabled\n"+
					"but the client is configured to use TLS. Please either enable TLS\n"+
					"on the server or run the client with -address set to an address\n"+
					"that uses the http protocol:\n\n"+
					"    vault <command> -address http://<address>\n\n"+
					"You can also set the VAULT_ADDR environment variable:\n\n\n"+
					"    VAULT_ADDR=http://<address> vault <command>\n\n"+
					"where <address> is replaced by the actual address to the server.",
				err)
		}
	}
	if err != nil {
		return result, err
	}

	// Check for a redirect, only allowing for a single redirect
	if (resp.StatusCode == 302 || resp.StatusCode == 307) && redirectCount == 0 {
		// Parse the updated location
		respLoc, err := resp.Location()
		if err != nil {
			return result, err
		}

		// Ensure a protocol downgrade doesn't happen
		if req.URL.Scheme == "https" && respLoc.Scheme != "https" {
			return result, fmt.Errorf("redirect would cause protocol downgrade")
		}

		// Update the request
		r.URL = respLoc

		// Reset the request body if any
		if err := r.ResetJSONBody(); err != nil {
			return result, err
		}

		// Retry the request
		redirectCount++
		goto START
	}

	if err := result.Error(); err != nil {
		return result, err
	}

	return result, nil
}
<commit_msg>Change variable name for clarity<commit_after>package api

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
)

var (
	errRedirect            = errors.New("redirect")
	defaultHTTPClientSetup sync.Once
)

// Config is used to configure the creation of the client.
type Config struct {
	// Address is the address of the Vault server. This should be a complete
	// URL such as "http://vault.example.com". If you need a custom SSL
	// cert or want to enable insecure mode, you need to specify a custom
	// HttpClient.
	Address string

	// HttpClient is the HTTP client to use, which will currently always be
	// http.DefaultClient. This is used to control redirect behavior.
	HttpClient *http.Client
}

// DefaultConfig returns a default configuration for the client. It is
// safe to modify the return value of this function.
//
// The default Address is https://127.0.0.1:8200, but this can be overridden by
// setting the `VAULT_ADDR` environment variable.
func DefaultConfig() *Config {
	config := &Config{
		Address:    "https://127.0.0.1:8200",
		HttpClient: http.DefaultClient,
	}

	if addr := os.Getenv("VAULT_ADDR"); addr != "" {
		config.Address = addr
	}

	return config
}

// Client is the client to the Vault API. Create a client with
// NewClient.
type Client struct {
	addr   *url.URL
	config *Config
	token  string
}

// NewClient returns a new client for the given configuration.
//
// If the environment variable `VAULT_TOKEN` is present, the token will be
// automatically added to the client. Otherwise, you must manually call
// `SetToken()`.
func NewClient(c *Config) (*Client, error) {
	u, err := url.Parse(c.Address)
	if err != nil {
		return nil, err
	}

	if c.HttpClient == http.DefaultClient {
		defaultHTTPClientSetup.Do(func() {
			// Ensure redirects are not automatically followed
			c.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
				return errRedirect
			}
		})
	}

	client := &Client{
		addr:   u,
		config: c,
	}

	if token := os.Getenv("VAULT_TOKEN"); token != "" {
		client.SetToken(token)
	}

	return client, nil
}

// Token returns the access token being used by this client. It will
// return the empty string if there is no token set.
func (c *Client) Token() string {
	return c.token
}

// SetToken sets the token directly. This won't perform any auth
// verification, it simply sets the token properly for future requests.
func (c *Client) SetToken(v string) {
	c.token = v
}

// ClearToken deletes the token if it is set or does nothing otherwise.
func (c *Client) ClearToken() {
	c.token = ""
}

// NewRequest creates a new raw request object to query the Vault server
// configured for this client. This is an advanced method and generally
// doesn't need to be called externally.
func (c *Client) NewRequest(method, path string) *Request {
	req := &Request{
		Method: method,
		URL: &url.URL{
			Scheme: c.addr.Scheme,
			Host:   c.addr.Host,
			Path:   path,
		},
		ClientToken: c.token,
		Params:      make(map[string][]string),
	}

	return req
}

// RawRequest performs the raw request given. This request may be against
// a Vault server not configured with this client. This is an advanced operation
// that generally won't need to be called externally.
func (c *Client) RawRequest(r *Request) (*Response, error) {
	redirectCount := 0
START:
	req, err := r.ToHTTP()
	if err != nil {
		return nil, err
	}

	var result *Response
	resp, err := c.config.HttpClient.Do(req)
	if resp != nil {
		result = &Response{Response: resp}
	}
	if err != nil {
		if urlErr, ok := err.(*url.Error); ok && urlErr.Err == errRedirect {
			err = nil
		} else if strings.Contains(err.Error(), "tls: oversized") {
			err = fmt.Errorf(
				"%s\n\n"+
					"This error usually means that the server is running with TLS disabled\n"+
					"but the client is configured to use TLS. Please either enable TLS\n"+
					"on the server or run the client with -address set to an address\n"+
					"that uses the http protocol:\n\n"+
					"    vault <command> -address http://<address>\n\n"+
					"You can also set the VAULT_ADDR environment variable:\n\n\n"+
					"    VAULT_ADDR=http://<address> vault <command>\n\n"+
					"where <address> is replaced by the actual address to the server.",
				err)
		}
	}
	if err != nil {
		return result, err
	}

	// Check for a redirect, only allowing for a single redirect
	if (resp.StatusCode == 302 || resp.StatusCode == 307) && redirectCount == 0 {
		// Parse the updated location
		respLoc, err := resp.Location()
		if err != nil {
			return result, err
		}

		// Ensure a protocol downgrade doesn't happen
		if req.URL.Scheme == "https" && respLoc.Scheme != "https" {
			return result, fmt.Errorf("redirect would cause protocol downgrade")
		}

		// Update the request
		r.URL = respLoc

		// Reset the request body if any
		if err := r.ResetJSONBody(); err != nil {
			return result, err
		}

		// Retry the request
		redirectCount++
		goto START
	}

	if err := result.Error(); err != nil {
		return result, err
	}

	return result, nil
}
<|endoftext|>


<commit_before>package web

import "strings"

const (
	defaultHTTPSuccess int = 200
	defaultHTTPError   int = 400
)

// Controller interface
type Controller interface {
	Index(ctx *Context)
	Create(ctx *Context)
	Detail(ctx *Context)
	Update(ctx *Context)
	Destroy(ctx *Context)
}

// Middleware struct
type Middleware struct {
	Path     string
	Callback Callback
}

// Middlewares Middleware list
type Middlewares []Middleware

func (m Middlewares) exec(path string, ctx *Context) {
	for i := range m {
		if strings.HasPrefix(path, m[i].Path) {
			if m[i].Callback != nil {
				m[i].Callback(ctx)
			}
		}
	}
}

// responseData struct
type responseData struct {
	Success bool        `json:"success"`
	Code    int         `json:"code"`
	Result  interface{} `json:"result"`
	Error   error       `json:"error"`
}

// ValidationError struct
type ValidationError struct {
	Name    string `json:"name"`
	Message string `json:"message"`
}

func (r *ValidationError) Error() string {
	return r.Message
}
<commit_msg>added back Error<commit_after>package web

import "strings"

const (
	defaultHTTPSuccess int = 200
	defaultHTTPError   int = 400
)

// Controller interface
type Controller interface {
	Index(ctx *Context)
	Create(ctx *Context)
	Detail(ctx *Context)
	Update(ctx *Context)
	Destroy(ctx *Context)
}

// Middleware struct
type Middleware struct {
	Path     string
	Callback Callback
}

// Middlewares Middleware list
type Middlewares []Middleware

func (m Middlewares) exec(path string, ctx *Context) {
	for i := range m {
		if strings.HasPrefix(path, m[i].Path) {
			if m[i].Callback != nil {
				m[i].Callback(ctx)
			}
		}
	}
}

// responseData struct
type responseData struct {
	Success bool        `json:"success"`
	Code    int         `json:"code"`
	Result  interface{} `json:"result"`
	Error   error       `json:"error"`
}

// ValidationError struct
type ValidationError struct {
	Name    string `json:"name"`
	Message string `json:"message"`
}

func (r *ValidationError) Error() string {
	return r.Message
}

// Error struct
type Error struct {
	Message string `json:"message"`
}

func (r *Error) Error() string {
	return r.Message
}
<|endoftext|>


<commit_before>package skiplist

import (
	"math/rand"
	"sync"
)

type elementNode struct {
	next []*Element
}

type Element struct {
	elementNode
	key   float64
	value interface{}
}

type SkipList struct {
	elementNode
	maxLevel       int
	length         int
	randSource     rand.Source
	probability    float64
	probTable      []float64
	mutex          sync.RWMutex
	prevNodesCache []*elementNode
}
<commit_msg>Allow access to the key and value in Element<commit_after>package skiplist

import (
	"math/rand"
	"sync"
)

type elementNode struct {
	next []*Element
}

type Element struct {
	elementNode
	key   float64
	value interface{}
}

// Key allows retrieval of the key for a given Element
func (e *Element) Key() float64 {
	return e.key
}

// Value allows retrieval of the value for a given Element
func (e *Element) Value() interface{} {
	return e.value
}

type SkipList struct {
	elementNode
	maxLevel       int
	length         int
	randSource     rand.Source
	probability    float64
	probTable      []float64
	mutex          sync.RWMutex
	prevNodesCache []*elementNode
}
<|endoftext|>


<commit_before>package organization

import (
	"errors"
	"fmt"
	"strings"

	ldap "gopkg.in/ldap.v2"
)

// AddType desgined to add a new dolresType
func (org *Organization) AddType(name, description string, isUnit bool) (string, error) {

	id := generatorID()
	dn := org.dn(id, typeCategory(isUnit))
	aq := ldap.NewAddRequest(dn)

	aq.Attribute(`objectClass`, []string{`doloresType`, `top`})
	aq.Attribute(`cn`, []string{name})
	aq.Attribute(`description`, []string{description})

	return id, org.l.Add(aq)
}

// ModifyType update name or description of doloresType
func (org *Organization) ModifyType(id string, name, description string, isUnit bool) error {

	dn := org.dn(id, typeCategory(isUnit))
	mq := ldap.NewModifyRequest(dn)

	if len(name) != 0 {
		mq.Replace(`cn`, []string{name})
	}
	if len(description) != 0 {
		mq.Replace(`description`, []string{description})
	}

	return org.l.Modify(mq)
}

// DelType by id
func (org *Organization) DelType(id string, isUnit bool) error {

	pids, err := org.PermissionByType(id, isUnit)
	if err != nil {
		return err
	}
	if len(pids) > 0 {
		return errors.New(`has Permission refrence this type,`)
	}

	dn := org.dn(id, typeCategory(isUnit))
	dq := ldap.NewDelRequest(dn, nil)

	return org.l.Del(dq)
}

// Types in ldap server
func (org *Organization) Types(isUnit bool, pageSize uint32, cookie []byte) (*SearchResult, error) {
	sq := &searchRequest{
		org.parentDN(typeCategory(isUnit)),
		`(objectClass=doloresType)`,
		[]string{`id`, `cn`, `description`, `modifyTimestamp`}, nil,
		pageSize,
		cookie}
	return org.search(sq)
}

// TypeByIDs ...
func (org *Organization) TypeByIDs(ids []string) ([]map[string]interface{}, error) {
	filter, err := sqConvertIDsToFilter(ids)
	if err != nil {
		return nil, err
	}
	dn := fmt.Sprintf(`ou=type,%s`, org.subffix)

	sq := ldap.NewSearchRequest(dn,
		ldap.ScopeWholeSubtree,
		ldap.DerefAlways,
		0, 0, false, filter, []string{`id`, `cn`, `description`}, nil)

	sr, err := org.l.Search(sq)
	if err != nil {
		return nil, err
	}

	var result []map[string]interface{}

	for _, e := range sr.Entries {
		isUnit := false
		if strings.Contains(e.DN, `ou=unit`) {
			isUnit = true
		}
		result = append(result, map[string]interface{}{
			`id`:          e.GetAttributeValue(`id`),
			`cn`:          e.GetAttributeValue(`cn`),
			`description`: e.GetAttributeValue(`description`),
			`isUnit`:      isUnit,
		})
	}

	if result == nil {
		return nil, errors.New(`not found`)
	}
	return result, nil
}

// TypeByID ...
func (org *Organization) TypeByID(id string) (map[string]interface{}, error) {

	types, err := org.TypeByIDs([]string{id})
	if err != nil {
		return nil, err
	}
	if len(types) != 1 {
		return nil, errors.New(`found many types`)
	}

	return types[0], nil
}

// TypeByPermissionID ...
func (org *Organization) TypeByPermissionID(id string) ([]map[string]interface{}, error) {

	p, e := org.PermissionByID(id)
	if e != nil {
		return nil, e
	}
	return org.TypeByIDs(p[`rbacType`].([]string))
}
<commit_msg>Refine `type.go`.<commit_after>package organization

import (
	"errors"
	"fmt"
	"strings"

	ldap "gopkg.in/ldap.v2"
)

// AddType desgined to add a new dolresType
func (org *Organization) AddType(name, description string, isUnit bool) (string, error) {

	id := generatorID()
	dn := org.dn(id, typeCategory(isUnit))
	aq := ldap.NewAddRequest(dn)

	aq.Attribute(`objectClass`, []string{`doloresType`, `top`})
	aq.Attribute(`cn`, []string{name})
	aq.Attribute(`description`, []string{description})

	return id, org.l.Add(aq)
}

// ModifyType update name or description of doloresType
func (org *Organization) ModifyType(id string, name, description string, isUnit bool) error {

	dn := org.dn(id, typeCategory(isUnit))
	mq := ldap.NewModifyRequest(dn)

	if len(name) != 0 {
		mq.Replace(`cn`, []string{name})
	}
	if len(description) != 0 {
		mq.Replace(`description`, []string{description})
	}

	return org.l.Modify(mq)
}

// DelType by id
func (org *Organization) DelType(id string, isUnit bool) error {

	pIDs, e := org.PermissionByType(id, isUnit)
	if e != nil {
		return e
	}
	if len(pIDs) != 0 {
		return fmt.Errorf(`有权限规则包含此类型，请先修改权限规则 count: %d`, len(pIDs))
	}

	// 通过Type 找人
	mIDs, e := org.MemberIDsByTypeIDs([]string{id})
	if e != nil {
		return e
	}
	if len(mIDs) != 0 {
		return fmt.Errorf(`有员工属于此类型，请先修改员工所属类型 count: %d`, len(pIDs))
	}

	dn := org.dn(id, typeCategory(isUnit))
	dq := ldap.NewDelRequest(dn, nil)

	return org.l.Del(dq)
}

// Types in ldap server
func (org *Organization) Types(isUnit bool, pageSize uint32, cookie []byte) (*SearchResult, error) {
	sq := &searchRequest{
		org.parentDN(typeCategory(isUnit)),
		`(objectClass=doloresType)`,
		[]string{`id`, `cn`, `description`, `modifyTimestamp`}, nil,
		pageSize,
		cookie}
	return org.search(sq)
}

// TypeByIDs ...
func (org *Organization) TypeByIDs(ids []string) ([]map[string]interface{}, error) {
	filter, err := sqConvertIDsToFilter(ids)
	if err != nil {
		return nil, err
	}
	dn := fmt.Sprintf(`ou=type,%s`, org.subffix)

	sq := ldap.NewSearchRequest(dn,
		ldap.ScopeWholeSubtree,
		ldap.DerefAlways,
		0, 0, false, filter, []string{`id`, `cn`, `description`}, nil)

	sr, err := org.l.Search(sq)
	if err != nil {
		return nil, err
	}

	var result []map[string]interface{}

	for _, e := range sr.Entries {
		isUnit := false
		if strings.Contains(e.DN, `ou=unit`) {
			isUnit = true
		}
		result = append(result, map[string]interface{}{
			`id`:          e.GetAttributeValue(`id`),
			`cn`:          e.GetAttributeValue(`cn`),
			`description`: e.GetAttributeValue(`description`),
			`isUnit`:      isUnit,
		})
	}

	if result == nil {
		return nil, errors.New(`not found`)
	}
	return result, nil
}

// TypeByID ...
func (org *Organization) TypeByID(id string) (map[string]interface{}, error) {

	types, err := org.TypeByIDs([]string{id})
	if err != nil {
		return nil, err
	}
	if len(types) != 1 {
		return nil, errors.New(`found many types`)
	}

	return types[0], nil
}

// TypeByPermissionID ...
func (org *Organization) TypeByPermissionID(id string) ([]map[string]interface{}, error) {

	p, e := org.PermissionByID(id)
	if e != nil {
		return nil, e
	}
	return org.TypeByIDs(p[`rbacType`].([]string))
}
<|endoftext|>


<commit_before>package beam

import (
	"fmt"
	"net"
	"os"
	"syscall"
)

// Send sends a new message on conn with data and f as payload and
// attachment, respectively.
func Send(conn *net.UnixConn, data []byte, f *os.File) error {
	var fds []int
	if f != nil {
		fds = append(fds, int(f.Fd()))
	}
	return sendUnix(conn, data, fds...)
}

// Receive waits for a new message on conn, and receives its payload
// and attachment, or an error if any.
//
// If more than 1 file descriptor is sent in the message, they are all
// closed except for the first, which is the attachment.
// It is legal for a message to have no attachment or an empty payload.
func Receive(conn *net.UnixConn) ([]byte, *os.File, error) {
	for {
		data, fds, err := receiveUnix(conn)
		if err != nil {
			return nil, nil, fmt.Errorf("receive: %v", err)
		}
		var f *os.File
		if len(fds) > 1 {
			for _, fd := range fds {
				syscall.Close(fd)
			}
		}
		if len(fds) >= 1 {
			f = os.NewFile(uintptr(fds[0]), "")
		}
		return data, f, nil
	}
	panic("impossibru")
	return nil, nil, nil
}

// SendPipe creates a new unix socket pair, sends one end as the attachment
// to a beam message with the payload `data`, and returns the other end.
//
// This is a common pattern to open a new service endpoint.
// For example, a service wishing to advertise its presence to clients might
// open an endpoint with:
//
//  endpoint, _ := SendPipe(conn, []byte("sql"))
//  defer endpoint.Close()
//  for {
//  	conn, _ := endpoint.Receive()
//	go func() {
//		Handle(conn)
//		conn.Close()
//	}()
//  }
//
// Note that beam does not distinguish between clients and servers in the logical
// sense: any program wishing to establishing a communication with another program
// may use SendPipe() to create an endpoint.
// For example, here is how an application might use it to connect to a database client.
//
//  endpoint, _ := SendPipe(conn, []byte("userdb"))
//  defer endpoint.Close()
//  conn, _ := endpoint.Receive()
//  defer conn.Close()
//  db := NewDBClient(conn)
//
// In this example note that we only need the first connection out of the endpoint,
// but we could open new ones to retry after a broken connection.
// Note that, because the underlying service transport is abstracted away, this
// allows for arbitrarily complex service discovery and retry logic to take place,
// without complicating application code.
//
func SendPipe(conn *net.UnixConn, data []byte) (endpoint *net.UnixConn, err error) {
	local, remote, err := SocketPair()
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			local.Close()
			remote.Close()
		}
	}()
	endpoint, err = FdConn(int(local.Fd()))
	if err != nil {
		return nil, err
	}
	if err := Send(conn, data, remote); err != nil {
		return nil, err
	}
	return endpoint, nil
}

func receiveUnix(conn *net.UnixConn) ([]byte, []int, error) {
	buf := make([]byte, 4096)
	oob := make([]byte, 4096)
	bufn, oobn, _, _, err := conn.ReadMsgUnix(buf, oob)
	if err != nil {
		return nil, nil, err
	}
	return buf[:bufn], extractFds(oob[:oobn]), nil
}

func sendUnix(conn *net.UnixConn, data []byte, fds ...int) error {
	_, _, err := conn.WriteMsgUnix(data, syscall.UnixRights(fds...), nil)
	if err == nil {
		for _, fd := range fds {
			fmt.Printf("Closing sent fd %v\n", fd)
			syscall.Close(fd)
		}
	}
	return err
}

func extractFds(oob []byte) (fds []int) {
	scms, err := syscall.ParseSocketControlMessage(oob)
	if err != nil {
		return
	}
	for _, scm := range scms {
		gotFds, err := syscall.ParseUnixRights(&scm)
		if err != nil {
			continue
		}
		fds = append(fds, gotFds...)
	}
	return
}

func socketpair() ([2]int, error) {
	return syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.FD_CLOEXEC, 0)
}

// SocketPair is a convenience wrapper around the socketpair(2) syscall.
// It returns a unix socket of type SOCK_STREAM in the form of 2 file descriptors
// not bound to the underlying filesystem.
// Messages sent on one end are received on the other, and vice-versa.
// It is the caller's responsibility to close both ends.
func SocketPair() (*os.File, *os.File, error) {
	pair, err := socketpair()
	if err != nil {
		return nil, nil, err
	}
	return os.NewFile(uintptr(pair[0]), ""), os.NewFile(uintptr(pair[1]), ""), nil
}

func USocketPair() (*net.UnixConn, *net.UnixConn, error) {
	a, b, err := SocketPair()
	if err != nil {
		return nil, nil, err
	}
	fmt.Printf("SocketPair() = %v, %v\n", a.Fd(), b.Fd())
	uA, err := FdConn(int(a.Fd()))
	if err != nil {
		a.Close()
		b.Close()
		return nil, nil, err
	}
	uB, err := FdConn(int(b.Fd()))
	if err != nil {
		a.Close()
		b.Close()
		return nil, nil, err
	}
	return uA, uB, nil
}

// FdConn wraps a file descriptor in a standard *net.UnixConn object, or
// returns an error if the file descriptor does not point to a unix socket.
func FdConn(fd int) (*net.UnixConn, error) {
	f := os.NewFile(uintptr(fd), fmt.Sprintf("%d", fd))
	conn, err := net.FileConn(f)
	if err != nil {
		return nil, err
	}
	f.Close()
	uconn, ok := conn.(*net.UnixConn)
	if !ok {
		conn.Close()
		return nil, fmt.Errorf("%d: not a unix connection", fd)
	}
	return uconn, nil
}
<commit_msg>Beam: Send: pass the underlying error unchanged to allow io.EOF detection<commit_after>package beam

import (
	"fmt"
	"net"
	"os"
	"syscall"
)

// Send sends a new message on conn with data and f as payload and
// attachment, respectively.
func Send(conn *net.UnixConn, data []byte, f *os.File) error {
	var fds []int
	if f != nil {
		fds = append(fds, int(f.Fd()))
	}
	return sendUnix(conn, data, fds...)
}

// Receive waits for a new message on conn, and receives its payload
// and attachment, or an error if any.
//
// If more than 1 file descriptor is sent in the message, they are all
// closed except for the first, which is the attachment.
// It is legal for a message to have no attachment or an empty payload.
func Receive(conn *net.UnixConn) ([]byte, *os.File, error) {
	for {
		data, fds, err := receiveUnix(conn)
		if err != nil {
			return nil, nil, err
		}
		var f *os.File
		if len(fds) > 1 {
			for _, fd := range fds {
				syscall.Close(fd)
			}
		}
		if len(fds) >= 1 {
			f = os.NewFile(uintptr(fds[0]), "")
		}
		return data, f, nil
	}
	panic("impossibru")
	return nil, nil, nil
}

// SendPipe creates a new unix socket pair, sends one end as the attachment
// to a beam message with the payload `data`, and returns the other end.
//
// This is a common pattern to open a new service endpoint.
// For example, a service wishing to advertise its presence to clients might
// open an endpoint with:
//
//  endpoint, _ := SendPipe(conn, []byte("sql"))
//  defer endpoint.Close()
//  for {
//  	conn, _ := endpoint.Receive()
//	go func() {
//		Handle(conn)
//		conn.Close()
//	}()
//  }
//
// Note that beam does not distinguish between clients and servers in the logical
// sense: any program wishing to establishing a communication with another program
// may use SendPipe() to create an endpoint.
// For example, here is how an application might use it to connect to a database client.
//
//  endpoint, _ := SendPipe(conn, []byte("userdb"))
//  defer endpoint.Close()
//  conn, _ := endpoint.Receive()
//  defer conn.Close()
//  db := NewDBClient(conn)
//
// In this example note that we only need the first connection out of the endpoint,
// but we could open new ones to retry after a broken connection.
// Note that, because the underlying service transport is abstracted away, this
// allows for arbitrarily complex service discovery and retry logic to take place,
// without complicating application code.
//
func SendPipe(conn *net.UnixConn, data []byte) (endpoint *net.UnixConn, err error) {
	local, remote, err := SocketPair()
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			local.Close()
			remote.Close()
		}
	}()
	endpoint, err = FdConn(int(local.Fd()))
	if err != nil {
		return nil, err
	}
	if err := Send(conn, data, remote); err != nil {
		return nil, err
	}
	return endpoint, nil
}

func receiveUnix(conn *net.UnixConn) ([]byte, []int, error) {
	buf := make([]byte, 4096)
	oob := make([]byte, 4096)
	bufn, oobn, _, _, err := conn.ReadMsgUnix(buf, oob)
	if err != nil {
		return nil, nil, err
	}
	return buf[:bufn], extractFds(oob[:oobn]), nil
}

func sendUnix(conn *net.UnixConn, data []byte, fds ...int) error {
	_, _, err := conn.WriteMsgUnix(data, syscall.UnixRights(fds...), nil)
	if err == nil {
		for _, fd := range fds {
			fmt.Printf("Closing sent fd %v\n", fd)
			syscall.Close(fd)
		}
	}
	return err
}

func extractFds(oob []byte) (fds []int) {
	scms, err := syscall.ParseSocketControlMessage(oob)
	if err != nil {
		return
	}
	for _, scm := range scms {
		gotFds, err := syscall.ParseUnixRights(&scm)
		if err != nil {
			continue
		}
		fds = append(fds, gotFds...)
	}
	return
}

func socketpair() ([2]int, error) {
	return syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.FD_CLOEXEC, 0)
}

// SocketPair is a convenience wrapper around the socketpair(2) syscall.
// It returns a unix socket of type SOCK_STREAM in the form of 2 file descriptors
// not bound to the underlying filesystem.
// Messages sent on one end are received on the other, and vice-versa.
// It is the caller's responsibility to close both ends.
func SocketPair() (*os.File, *os.File, error) {
	pair, err := socketpair()
	if err != nil {
		return nil, nil, err
	}
	return os.NewFile(uintptr(pair[0]), ""), os.NewFile(uintptr(pair[1]), ""), nil
}

func USocketPair() (*net.UnixConn, *net.UnixConn, error) {
	a, b, err := SocketPair()
	if err != nil {
		return nil, nil, err
	}
	fmt.Printf("SocketPair() = %v, %v\n", a.Fd(), b.Fd())
	uA, err := FdConn(int(a.Fd()))
	if err != nil {
		a.Close()
		b.Close()
		return nil, nil, err
	}
	uB, err := FdConn(int(b.Fd()))
	if err != nil {
		a.Close()
		b.Close()
		return nil, nil, err
	}
	return uA, uB, nil
}

// FdConn wraps a file descriptor in a standard *net.UnixConn object, or
// returns an error if the file descriptor does not point to a unix socket.
func FdConn(fd int) (*net.UnixConn, error) {
	f := os.NewFile(uintptr(fd), fmt.Sprintf("%d", fd))
	conn, err := net.FileConn(f)
	if err != nil {
		return nil, err
	}
	f.Close()
	uconn, ok := conn.(*net.UnixConn)
	if !ok {
		conn.Close()
		return nil, fmt.Errorf("%d: not a unix connection", fd)
	}
	return uconn, nil
}
<|endoftext|>


<commit_before>package quimby

import (
	"encoding/json"
	"errors"

	"github.com/boltdb/bolt"

	"golang.org/x/crypto/bcrypt"
)

type User struct {
	Username       string `json:"username"`
	Password       string `json:"password,omitempty"`
	HashedPassword []byte `json:"hashed_password,omitempty"`
	TFA            string `json:"tfa,omitempty"`
	TFAData        []byte `json:"tfa_data,omitempty"`
	Permission     string `json:"permission"`
	db             *bolt.DB
	tfa            TFAer
}

type UserOpt func(*User)

func UserDB(db *bolt.DB) UserOpt {
	return func(u *User) {
		u.db = db
	}
}

func UserPassword(pw string) UserOpt {
	return func(u *User) {
		u.Password = pw
	}
}

func UserPermission(perm string) UserOpt {
	return func(u *User) {
		u.Permission = perm
	}
}

func UserTFA(tfa TFAer) UserOpt {
	return func(u *User) {
		u.tfa = tfa
	}
}

func NewUser(username string, opts ...UserOpt) *User {
	u := &User{Username: username}
	for _, o := range opts {
		o(u)
	}
	return u
}

func GetUsers(db *bolt.DB) ([]User, error) {
	users := []User{}

	err := db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("users"))
		c := b.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			var u User
			if err := json.Unmarshal(v, &u); err != nil {
				return err
			}
			u.HashedPassword = []byte{}
			u.TFAData = []byte{}
			u.TFA = ""
			u.db = db
			users = append(users, u)
		}
		return nil
	})
	return users, err
}

func (u *User) SetDB(db *bolt.DB) {
	u.db = db
}

func (u *User) SetTFA(tfa TFAer) {
	u.tfa = tfa
}

func (u *User) Fetch() error {
	return u.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("users"))
		v := b.Get([]byte(u.Username))
		if len(v) == 0 {
			return NotFound
		}
		return json.Unmarshal(v, u)
	})
}

//Is authorized if the username is in the db
func (u *User) IsAuthorized(perm string) bool {
	if u.Permission == "" {
		if err := u.Fetch(); err != nil {
			return false
		}
	}
	return u.Permission == perm && perm != ""
}

func (u *User) UpdateTFA() ([]byte, error) {
	if u.tfa == nil {
		return nil, errors.New("can't save user, no tfa")
	}

	var qr []byte
	var err error
	u.TFAData, qr, err = u.tfa.Get(u.Username)
	return qr, err
}

func (u *User) Save() ([]byte, error) {
	if u.tfa == nil {
		return nil, errors.New("can't save user, no tfa")
	}
	if len(u.HashedPassword) == 0 && len(u.Password) < 8 {
		return nil, errors.New("password is too short")
	}
	u.hashPassword()
	var qr []byte

	if !u.Exists() {
		var err error
		u.TFAData, qr, err = u.tfa.Get(u.Username)
		if err != nil {
			return nil, err
		}
	}

	return qr, u.db.Update(func(tx *bolt.Tx) error {
		d, _ := json.Marshal(u)
		b := tx.Bucket([]byte("users"))
		return b.Put([]byte(u.Username), d)
	})
}

func (u *User) Exists() bool {
	err := u.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("users"))
		v := b.Get([]byte(u.Username))
		if len(v) == 0 {
			return NotFound
		}
		return json.Unmarshal(v, u)
	})
	if err == NotFound {
		return false
	}
	return err == nil
}

func (u *User) Delete() error {
	return u.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("users"))
		return b.Delete([]byte(u.Username))
	})
}

func (u *User) CheckPassword() (bool, error) {
	pw := u.Password
	if len(u.HashedPassword) == 0 || len(u.TFAData) == 0 {
		if err := u.Fetch(); err != nil {
			LG.Println(err)
			return false, err
		}
	}

	if err := u.tfa.Check(u.TFAData, u.Username); err != nil {
		LG.Println(err)
		return false, nil
	}
	return bcrypt.CompareHashAndPassword(u.HashedPassword, []byte(pw)) == nil, nil
}

func (u *User) hashPassword() {
	if len(u.Password) == 0 {
		return
	}
	u.HashedPassword, _ = bcrypt.GenerateFromPassword(
		[]byte(u.Password),
		bcrypt.DefaultCost,
	)
	u.Password = ""
}
<commit_msg>fixed tfa bug<commit_after>package quimby

import (
	"encoding/json"
	"errors"

	"github.com/boltdb/bolt"

	"golang.org/x/crypto/bcrypt"
)

type User struct {
	Username       string `json:"username"`
	Password       string `json:"password,omitempty"`
	HashedPassword []byte `json:"hashed_password,omitempty"`
	TFA            string `json:"tfa,omitempty"`
	TFAData        []byte `json:"tfa_data,omitempty"`
	Permission     string `json:"permission"`
	db             *bolt.DB
	tfa            TFAer
}

type UserOpt func(*User)

func UserDB(db *bolt.DB) UserOpt {
	return func(u *User) {
		u.db = db
	}
}

func UserPassword(pw string) UserOpt {
	return func(u *User) {
		u.Password = pw
	}
}

func UserPermission(perm string) UserOpt {
	return func(u *User) {
		u.Permission = perm
	}
}

func UserTFA(tfa TFAer) UserOpt {
	return func(u *User) {
		u.tfa = tfa
	}
}

func NewUser(username string, opts ...UserOpt) *User {
	u := &User{Username: username}
	for _, o := range opts {
		o(u)
	}
	return u
}

func GetUsers(db *bolt.DB) ([]User, error) {
	users := []User{}

	err := db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("users"))
		c := b.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			var u User
			if err := json.Unmarshal(v, &u); err != nil {
				return err
			}
			u.HashedPassword = []byte{}
			u.TFAData = []byte{}
			u.TFA = ""
			u.db = db
			users = append(users, u)
		}
		return nil
	})
	return users, err
}

func (u *User) SetDB(db *bolt.DB) {
	u.db = db
}

func (u *User) SetTFA(tfa TFAer) {
	u.tfa = tfa
}

func (u *User) Fetch() error {
	return u.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("users"))
		v := b.Get([]byte(u.Username))
		if len(v) == 0 {
			return NotFound
		}
		return json.Unmarshal(v, u)
	})
}

//Is authorized if the username is in the db
func (u *User) IsAuthorized(perm string) bool {
	if u.Permission == "" {
		if err := u.Fetch(); err != nil {
			return false
		}
	}
	return u.Permission == perm && perm != ""
}

func (u *User) UpdateTFA() ([]byte, error) {
	if u.tfa == nil {
		return nil, errors.New("can't save user, no tfa")
	}

	var qr []byte
	var err error
	u.TFAData, qr, err = u.tfa.Get(u.Username)
	return qr, err
}

func (u *User) Save() ([]byte, error) {
	if u.tfa == nil {
		return nil, errors.New("can't save user, no tfa")
	}
	if len(u.HashedPassword) == 0 && len(u.Password) < 8 {
		return nil, errors.New("password is too short")
	}
	u.hashPassword()
	var qr []byte

	if !u.Exists() {
		var err error
		u.TFAData, qr, err = u.tfa.Get(u.Username)
		if err != nil {
			return nil, err
		}
	}

	return qr, u.db.Update(func(tx *bolt.Tx) error {
		d, _ := json.Marshal(u)
		b := tx.Bucket([]byte("users"))
		return b.Put([]byte(u.Username), d)
	})
}

func (u *User) Exists() bool {
	err := u.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("users"))
		v := b.Get([]byte(u.Username))
		if len(v) == 0 {
			return NotFound
		}
		return json.Unmarshal(v, u)
	})
	if err == NotFound {
		return false
	}
	return err == nil
}

func (u *User) Delete() error {
	return u.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("users"))
		return b.Delete([]byte(u.Username))
	})
}

func (u *User) CheckPassword() (bool, error) {
	pw := u.Password
	if len(u.HashedPassword) == 0 || len(u.TFAData) == 0 {
		if err := u.Fetch(); err != nil {
			LG.Println(err)
			return false, err
		}
	}

	if err := u.tfa.Check(u.TFAData, u.TFA); err != nil {
		LG.Println("tfa error", err)
		return false, nil
	}
	return bcrypt.CompareHashAndPassword(u.HashedPassword, []byte(pw)) == nil, nil
}

func (u *User) hashPassword() {
	if len(u.Password) == 0 {
		return
	}
	u.HashedPassword, _ = bcrypt.GenerateFromPassword(
		[]byte(u.Password),
		bcrypt.DefaultCost,
	)
	u.Password = ""
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/garyburd/go-websocket/websocket"
	"github.com/ugorji/go/codec"
	"io/ioutil"
	"strconv"
	"time"
)

const (
	MAX_USERS_PER_LOCATION = 20
	MAX_NICKNAME_LENGTH    = 20
	SEND_CHANNEL_SIZE      = 256
	// Time allowed to write a message to the client.
	WRITE_WAIT = 5 * time.Second
	// Time allowed to read the next message from the client.
	READ_WAIT = 20 * time.Second
	// Send pings to client with this period. Must be less than READ_WAIT.
	PING_PERIOD = READ_WAIT / 2
	// Maximum message size allowed from client.
	// must be at least 4 * MAX_CHAT_MESSAGE_LENGTH (in script.js)
	MAX_MESSAGE_SIZE = 1024
)

var (
	userIdGenerator chan int
	msgpackHandle   codec.MsgpackHandle
)

type User struct {
	Socket      *websocket.Conn
	sendData    chan<- *[]byte
	recv        <-chan *[]interface{}
	UserId      int
	Nickname    string
	MouseIsDown bool
	PositionX   int
	PositionY   int
	ColorRed    int
	ColorGreen  int
	ColorBlue   int
	UsePen      bool
	kick        chan string
}

func NewUser(ws *websocket.Conn) *User {
	user := new(User)
	user.Socket = ws
	user.UserId = <-userIdGenerator
	user.Nickname = strconv.Itoa(user.UserId)
	user.UsePen = true
	user.sendData = user.sender()
	user.recv = user.receiver()
	user.kick = make(chan string)
	return user
}

func init() {
	userIdGenerator = make(chan int)
	go func() {
		i := 1
		for {
			userIdGenerator <- i
			i += 1
		}
	}()
}

func encodeEvent(event *[]interface{}) (*[]byte, error) {
	var result []byte
	var err = codec.NewEncoderBytes(&result, &msgpackHandle).Encode(event)
	if err != nil {
		Log.Printf("Failed to encode event %v\n", event)
	}
	return &result, err
}

func (user *User) Kick(description string) {
	select {
	case user.kick <- description:
	default:
		// SocketHandler has already returned, avoid blocking
	}
}

func (user *User) Error(description string) {
	user.SendEvent(&[]interface{}{EventTypeError, description})
	user.Kick(description)
}

func (user *User) SendEvent(event *[]interface{}) {
	data, err := encodeEvent(event)
	if err != nil {
		return
	}
	user.SendData(data)
}

func (user *User) SendData(data *[]byte) {
	select {
	case user.sendData <- data:
	default:
		Log.Printf("Buffer full for user %v: kicking.\n", user.UserId)
		user.Kick("Buffer full")
	}
}

func (user *User) mouseMove(x int, y int, duration int) {
	user.PositionX = x
	user.PositionY = y
}

func (user *User) mouseUp() {
	user.MouseIsDown = false
}

func (user *User) mouseDown() {
	user.MouseIsDown = true
}

func (user *User) changeTool(use_pen bool) {
	user.UsePen = use_pen
}

func (user *User) changeColor(red, green, blue int) {
	user.ColorRed = red
	user.ColorGreen = green
	user.ColorBlue = blue
}

func (user *User) changeNickname(nickname string) {
	user.Nickname = nickname
}

func write(ws *websocket.Conn, opCode int, payload []byte) error {
	ws.SetWriteDeadline(time.Now().Add(WRITE_WAIT))
	return ws.WriteMessage(opCode, payload)
}

func (user *User) sender() chan<- *[]byte {
	ch := make(chan *[]byte, SEND_CHANNEL_SIZE)
	go func() {
		ticker := time.NewTicker(PING_PERIOD)
		defer func() {
			ticker.Stop()
		}()
		for {
			select {
			case data, ok := <-ch:
				if !ok {
					write(user.Socket, websocket.OpClose, []byte{})
					return
				}
				if err := write(user.Socket, websocket.OpBinary, *data); err != nil {
					user.Kick(err.Error())
					return
				}
			case <-ticker.C:
				if err := write(user.Socket, websocket.OpPing, []byte{}); err != nil {
					user.Kick(err.Error())
					return
				}
			}
		}
	}()
	return ch
}

func (user *User) receiver() <-chan *[]interface{} {
	// receives and decodes messages from users
	ch := make(chan *[]interface{})
	go func() {
	    defer close(ch)
		user.Socket.SetReadLimit(MAX_MESSAGE_SIZE)
		user.Socket.SetReadDeadline(time.Now().Add(READ_WAIT))
		for {
			op, r, err := user.Socket.NextReader()
			if err != nil {
				user.Kick(err.Error())
				return
			}
			switch op {
			case websocket.OpPong:
				user.Socket.SetReadDeadline(time.Now().Add(READ_WAIT))
			case websocket.OpBinary:
				data, err := ioutil.ReadAll(r)
				if err != nil {
					user.Kick(err.Error())
					return
				}
				var event []interface{}
				err = codec.NewDecoderBytes(data, &msgpackHandle).Decode(&event)
				if err != nil {
					user.Kick(err.Error())
					return
				}
				ch <- &event
			default:
				user.Kick(fmt.Sprintf("bad message type: %v", op))
				return
			}
		}
	}()
	return ch
}

func (user *User) SocketHandler(location *Location) {
	defer func() {
		if location != nil {
			location.Quit <- user
			Log.Printf("user %v left\n", user.UserId)
		}
	}()
	for {
		select {
		case event, ok := <-user.recv:
			if !ok {
				return
			}
			if location != nil {
				location.Message <- &UserAndEvent{user, event}
			}
		case err_msg := <-user.kick:
			Log.Printf("user %v kicked for '%v'\n", user.UserId, err_msg)
			return
		}
	}
}
<commit_msg>don't check if user.sendData is closed as nobody closes it<commit_after>package main

import (
	"fmt"
	"github.com/garyburd/go-websocket/websocket"
	"github.com/ugorji/go/codec"
	"io/ioutil"
	"strconv"
	"time"
)

const (
	MAX_USERS_PER_LOCATION = 20
	MAX_NICKNAME_LENGTH    = 20
	SEND_CHANNEL_SIZE      = 256
	// Time allowed to write a message to the client.
	WRITE_WAIT = 5 * time.Second
	// Time allowed to read the next message from the client.
	READ_WAIT = 20 * time.Second
	// Send pings to client with this period. Must be less than READ_WAIT.
	PING_PERIOD = READ_WAIT / 2
	// Maximum message size allowed from client.
	// must be at least 4 * MAX_CHAT_MESSAGE_LENGTH (in script.js)
	MAX_MESSAGE_SIZE = 1024
)

var (
	userIdGenerator chan int
	msgpackHandle   codec.MsgpackHandle
)

type User struct {
	Socket      *websocket.Conn
	sendData    chan<- *[]byte
	recv        <-chan *[]interface{}
	UserId      int
	Nickname    string
	MouseIsDown bool
	PositionX   int
	PositionY   int
	ColorRed    int
	ColorGreen  int
	ColorBlue   int
	UsePen      bool
	kick        chan string
}

func NewUser(ws *websocket.Conn) *User {
	user := new(User)
	user.Socket = ws
	user.UserId = <-userIdGenerator
	user.Nickname = strconv.Itoa(user.UserId)
	user.UsePen = true
	user.sendData = user.sender()
	user.recv = user.receiver()
	user.kick = make(chan string)
	return user
}

func init() {
	userIdGenerator = make(chan int)
	go func() {
		i := 1
		for {
			userIdGenerator <- i
			i += 1
		}
	}()
}

func encodeEvent(event *[]interface{}) (*[]byte, error) {
	var result []byte
	var err = codec.NewEncoderBytes(&result, &msgpackHandle).Encode(event)
	if err != nil {
		Log.Printf("Failed to encode event %v\n", event)
	}
	return &result, err
}

func (user *User) Kick(description string) {
	select {
	case user.kick <- description:
	default:
		// SocketHandler has already returned, avoid blocking
	}
}

func (user *User) Error(description string) {
	user.SendEvent(&[]interface{}{EventTypeError, description})
	user.Kick(description)
}

func (user *User) SendEvent(event *[]interface{}) {
	data, err := encodeEvent(event)
	if err != nil {
		return
	}
	user.SendData(data)
}

func (user *User) SendData(data *[]byte) {
	select {
	case user.sendData <- data:
	default:
		Log.Printf("Buffer full for user %v: kicking.\n", user.UserId)
		user.Kick("Buffer full")
	}
}

func (user *User) mouseMove(x int, y int, duration int) {
	user.PositionX = x
	user.PositionY = y
}

func (user *User) mouseUp() {
	user.MouseIsDown = false
}

func (user *User) mouseDown() {
	user.MouseIsDown = true
}

func (user *User) changeTool(use_pen bool) {
	user.UsePen = use_pen
}

func (user *User) changeColor(red, green, blue int) {
	user.ColorRed = red
	user.ColorGreen = green
	user.ColorBlue = blue
}

func (user *User) changeNickname(nickname string) {
	user.Nickname = nickname
}

func write(ws *websocket.Conn, opCode int, payload []byte) error {
	ws.SetWriteDeadline(time.Now().Add(WRITE_WAIT))
	return ws.WriteMessage(opCode, payload)
}

func (user *User) sender() chan<- *[]byte {
	ch := make(chan *[]byte, SEND_CHANNEL_SIZE)
	go func() {
		ticker := time.NewTicker(PING_PERIOD)
		defer func() {
			ticker.Stop()
		}()
		for {
			select {
			case data := <-ch:
				if err := write(user.Socket, websocket.OpBinary, *data); err != nil {
					user.Kick(err.Error())
					return
				}
			case <-ticker.C:
				if err := write(user.Socket, websocket.OpPing, []byte{}); err != nil {
					user.Kick(err.Error())
					return
				}
			}
		}
	}()
	return ch
}

func (user *User) receiver() <-chan *[]interface{} {
	// receives and decodes messages from users
	ch := make(chan *[]interface{})
	go func() {
	    defer close(ch)
		user.Socket.SetReadLimit(MAX_MESSAGE_SIZE)
		user.Socket.SetReadDeadline(time.Now().Add(READ_WAIT))
		for {
			op, r, err := user.Socket.NextReader()
			if err != nil {
				user.Kick(err.Error())
				return
			}
			switch op {
			case websocket.OpPong:
				user.Socket.SetReadDeadline(time.Now().Add(READ_WAIT))
			case websocket.OpBinary:
				data, err := ioutil.ReadAll(r)
				if err != nil {
					user.Kick(err.Error())
					return
				}
				var event []interface{}
				err = codec.NewDecoderBytes(data, &msgpackHandle).Decode(&event)
				if err != nil {
					user.Kick(err.Error())
					return
				}
				ch <- &event
			default:
				user.Kick(fmt.Sprintf("bad message type: %v", op))
				return
			}
		}
	}()
	return ch
}

func (user *User) SocketHandler(location *Location) {
	defer func() {
		if location != nil {
			location.Quit <- user
			Log.Printf("user %v left\n", user.UserId)
		}
	}()
	for {
		select {
		case event, ok := <-user.recv:
			if !ok {
				return
			}
			if location != nil {
				location.Message <- &UserAndEvent{user, event}
			}
		case err_msg := <-user.kick:
			Log.Printf("user %v kicked for '%v'\n", user.UserId, err_msg)
			return
		}
	}
}
<|endoftext|>


<commit_before>package dinero

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

	cache "github.com/patrickmn/go-cache"
)

const (
	packageVersion = "0.3.0"
	backendURL     = "http://openexchangerates.org"
	userAgent      = "dinero/" + packageVersion
)

// Client holds a connection to the OXR API.
type Client struct {
	client     *http.Client
	AppID      string
	UserAgent  string
	BackendURL *url.URL

	// Services used for communicating with the API.
	Rates *RatesService
	Cache *CacheService
}

type service struct {
	client *Client
}

// Response is a OXR response. This wraps the standard http.Response
// returned from the OXR API.
type Response struct {
	*http.Response
	ErrorCode int64
	Message   string
}

// An ErrorResponse reports the error caused by an API request
type ErrorResponse struct {
	*http.Response
	ErrorCode   int64  `json:"status"`
	Message     string `json:"message"`
	Description string `json:"description"`
}

func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%d %v", r.Response.StatusCode, r.Description)
}

// NewClient creates a new Client with the appropriate connection details and
// services used for communicating with the API.
func NewClient(appID, baseCurrency string) *Client {
	// Init new http.Client.
	httpClient := http.DefaultClient

	// Parse BE URL.
	baseURL, _ := url.Parse(backendURL)

	c := &Client{
		client:     httpClient,
		BackendURL: baseURL,
		UserAgent:  userAgent,
		AppID:      appID,
	}

	// Init a new store.
	store := cache.New(5*time.Minute, 10*time.Minute)

	// Init services.
	c.Rates = NewRatesService(c, baseCurrency)
	c.Cache = NewCacheService(c, store)

	return c
}

// NewRequest creates an API request. A relative URL can be provided in urlPath,
// which will be resolved to the BackendURL of the Client.
func (c *Client) NewRequest(method, urlPath string, body interface{}) (*http.Request, error) {
	// Parse our URL.
	rel, err := url.Parse(
		fmt.Sprintf("%s&app_id=%s", urlPath, c.AppID),
	)
	if err != nil {
		return nil, err
	}

	// Resolve to absolute URI.
	u := c.BackendURL.ResolveReference(rel)

	buf := new(bytes.Buffer)
	if body != nil {
		err = json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	// Create the request.
	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	// Add our libraries UA.
	req.Header.Add("User-Agent", c.UserAgent)

	return req, nil
}

// Do sends an API request and returns the API response. The API response is
// JSON decoded and stored in 'v', or returned as an error if an API (if found).
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer func() {
		if rerr := resp.Body.Close(); err == nil {
			err = rerr
		}
	}()

	// Wrap our response.
	response := &Response{Response: resp}

	// Check for any errors that may have occurred.
	err = CheckResponse(resp)
	if err != nil {
		return response, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			_, err = io.Copy(w, resp.Body)
			if err != nil {
				return nil, err
			}
		} else {
			err = json.NewDecoder(resp.Body).Decode(&v)
			if err != nil {
				return nil, err
			}
		}

	}

	return response, err
}

// CheckResponse checks the API response for errors. A response is considered an
// error if it has a status code outside the 200 range. API error responses map
// to ErrorResponse.
func CheckResponse(r *http.Response) error {
	if c := r.StatusCode; c >= 200 && c <= 299 {
		return nil
	}

	errorResponse := &ErrorResponse{Response: r}

	data, err := ioutil.ReadAll(r.Body)
	if err == nil && len(data) > 0 {
		err := json.Unmarshal(data, errorResponse)
		if err != nil {
			return err
		}
	}
	return errorResponse
}
<commit_msg>Added custommisable cache expiry and updated README<commit_after>package dinero

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

	cache "github.com/patrickmn/go-cache"
)

const (
	packageVersion = "0.3.0"
	backendURL     = "http://openexchangerates.org"
	userAgent      = "dinero/" + packageVersion
)

// Client holds a connection to the OXR API.
type Client struct {
	// client is the HTTP client the package will use for requests.
	client *http.Client
	// AppID is the Open Exchange Rates application ID.
	AppID string
	// UserAgent is the UA for this package that all requests will use.
	UserAgent string
	// BackendURL is the base API endpoint at OXR.
	BackendURL *url.URL

	// Services used for communicating with the API.
	Rates *RatesService
	Cache *CacheService
}

// NewClient creates a new Client with the appropriate connection details and
// services used for communicating with the API.
func NewClient(appID, baseCurrency string, expiry time.Duration) *Client {
	// Init new http.Client.
	httpClient := http.DefaultClient

	// Parse BE URL.
	baseURL, _ := url.Parse(backendURL)

	c := &Client{
		client:     httpClient,
		BackendURL: baseURL,
		UserAgent:  userAgent,
		AppID:      appID,
	}

	// Init a new store.
	store := cache.New(expiry, 10*time.Minute)

	// Init services.
	c.Rates = NewRatesService(c, baseCurrency)
	c.Cache = NewCacheService(c, store)

	return c
}

// NewRequest creates an API request. A relative URL can be provided in urlPath,
// which will be resolved to the BackendURL of the Client.
func (c *Client) NewRequest(method, urlPath string, body interface{}) (*http.Request, error) {
	// Parse our URL.
	rel, err := url.Parse(
		fmt.Sprintf("%s&app_id=%s", urlPath, c.AppID),
	)
	if err != nil {
		return nil, err
	}

	// Resolve to absolute URI.
	u := c.BackendURL.ResolveReference(rel)

	buf := new(bytes.Buffer)
	if body != nil {
		err = json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	// Create the request.
	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	// Add our libraries UA.
	req.Header.Add("User-Agent", c.UserAgent)

	return req, nil
}

// Do sends an API request and returns the API response. The API response is
// JSON decoded and stored in 'v', or returned as an error if an API (if found).
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer func() {
		if rerr := resp.Body.Close(); err == nil {
			err = rerr
		}
	}()

	// Wrap our response.
	response := &Response{Response: resp}

	// Check for any errors that may have occurred.
	err = CheckResponse(resp)
	if err != nil {
		return response, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			_, err = io.Copy(w, resp.Body)
			if err != nil {
				return nil, err
			}
		} else {
			err = json.NewDecoder(resp.Body).Decode(&v)
			if err != nil {
				return nil, err
			}
		}

	}

	return response, err
}

// Response is a OXR response. This wraps the standard http.Response
// returned from the OXR API.
type Response struct {
	*http.Response
	ErrorCode int64
	Message   string
}

// An ErrorResponse reports the error caused by an API request
type ErrorResponse struct {
	*http.Response
	ErrorCode   int64  `json:"status"`
	Message     string `json:"message"`
	Description string `json:"description"`
}

func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%d %v", r.Response.StatusCode, r.Description)
}

// CheckResponse checks the API response for errors. A response is considered an
// error if it has a status code outside the 200 range. API error responses map
// to ErrorResponse.
func CheckResponse(r *http.Response) error {
	if c := r.StatusCode; c >= 200 && c <= 299 {
		return nil
	}

	errorResponse := &ErrorResponse{Response: r}

	data, err := ioutil.ReadAll(r.Body)
	if err == nil && len(data) > 0 {
		err := json.Unmarshal(data, errorResponse)
		if err != nil {
			return err
		}
	}
	return errorResponse
}
<|endoftext|>