<commit_before>package cgroups

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"

	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
)

// New returns a new control via the cgroup cgroups interface
func New(hierarchy Hierarchy, path Path, resources *specs.LinuxResources) (Cgroup, error) {
	subsystems, err := hierarchy()
	if err != nil {
		return nil, err
	}
	for _, s := range subsystems {
		if err := initializeSubsystem(s, path, resources); err != nil {
			return nil, err
		}
	}
	return &cgroup{
		path:       path,
		subsystems: subsystems,
	}, nil
}

// Load will load an existing cgroup and allow it to be controlled
func Load(hierarchy Hierarchy, path Path) (Cgroup, error) {
	subsystems, err := hierarchy()
	if err != nil {
		return nil, err
	}
	// check the the subsystems still exist
	for _, s := range pathers(subsystems) {
		p, err := path(s.Name())
		if err != nil {
			if os.IsNotExist(errors.Cause(err)) {
				return nil, ErrCgroupDeleted
			}
			return nil, err
		}
		if _, err := os.Lstat(s.Path(p)); err != nil {
			if os.IsNotExist(err) {
				return nil, ErrCgroupDeleted
			}
			return nil, err
		}
	}
	return &cgroup{
		path:       path,
		subsystems: subsystems,
	}, nil
}

type cgroup struct {
	path Path

	subsystems []Subsystem
	mu         sync.Mutex
	err        error
}

// New returns a new sub cgroup
func (c *cgroup) New(name string, resources *specs.LinuxResources) (Cgroup, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return nil, c.err
	}
	path := subPath(c.path, name)
	for _, s := range c.subsystems {
		if err := initializeSubsystem(s, path, resources); err != nil {
			return nil, err
		}
	}
	return &cgroup{
		path:       path,
		subsystems: c.subsystems,
	}, nil
}

// Subsystems returns all the subsystems that are currently being
// consumed by the group
func (c *cgroup) Subsystems() []Subsystem {
	return c.subsystems
}

// Add moves the provided process into the new cgroup
func (c *cgroup) Add(process Process) error {
	if process.Pid <= 0 {
		return ErrInvalidPid
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	return c.add(process)
}

func (c *cgroup) add(process Process) error {
	for _, s := range pathers(c.subsystems) {
		p, err := c.path(s.Name())
		if err != nil {
			return err
		}
		if err := ioutil.WriteFile(
			filepath.Join(s.Path(p), cgroupProcs),
			[]byte(strconv.Itoa(process.Pid)),
			defaultFilePerm,
		); err != nil {
			return err
		}
	}
	return nil
}

// Delete will remove the control group from each of the subsystems registered
func (c *cgroup) Delete() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	var errors []string
	for _, s := range c.subsystems {
		if d, ok := s.(deleter); ok {
			sp, err := c.path(s.Name())
			if err != nil {
				return err
			}
			if err := d.Delete(sp); err != nil {
				errors = append(errors, string(s.Name()))
			}
			continue
		}
		if p, ok := s.(pather); ok {
			sp, err := c.path(s.Name())
			if err != nil {
				return err
			}
			path := p.Path(sp)
			if err := remove(path); err != nil {
				errors = append(errors, path)
			}
		}
	}
	if len(errors) > 0 {
		return fmt.Errorf("cgroups: unable to remove paths %s", strings.Join(errors, ", "))
	}
	c.err = ErrCgroupDeleted
	return nil
}

// Stat returns the current metrics for the cgroup
func (c *cgroup) Stat(handlers ...ErrorHandler) (*Metrics, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return nil, c.err
	}
	if len(handlers) == 0 {
		handlers = append(handlers, errPassthrough)
	}
	var (
		stats = &Metrics{
			CPU: &CPUStat{
				Throttling: &Throttle{},
				Usage:      &CPUUsage{},
			},
		}
		wg   = &sync.WaitGroup{}
		errs = make(chan error, len(c.subsystems))
	)
	for _, s := range c.subsystems {
		if ss, ok := s.(stater); ok {
			sp, err := c.path(s.Name())
			if err != nil {
				return nil, err
			}
			wg.Add(1)
			go func() {
				defer wg.Done()
				if err := ss.Stat(sp, stats); err != nil {
					for _, eh := range handlers {
						if herr := eh(err); herr != nil {
							errs <- herr
						}
					}
				}
			}()
		}
	}
	wg.Wait()
	close(errs)
	for err := range errs {
		return nil, err
	}
	return stats, nil
}

// Update updates the cgroup with the new resource values provided
//
// Be prepared to handle EBUSY when trying to update a cgroup with
// live processes and other operations like Stats being performed at the
// same time
func (c *cgroup) Update(resources *specs.LinuxResources) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	for _, s := range c.subsystems {
		if u, ok := s.(updater); ok {
			sp, err := c.path(s.Name())
			if err != nil {
				return err
			}
			if err := u.Update(sp, resources); err != nil {
				return err
			}
		}
	}
	return nil
}

// Processes returns the processes running inside the cgroup along
// with the subsystem used, pid, and path
func (c *cgroup) Processes(subsystem Name, recursive bool) ([]Process, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return nil, c.err
	}
	return c.processes(subsystem, recursive)
}

func (c *cgroup) processes(subsystem Name, recursive bool) ([]Process, error) {
	s := c.getSubsystem(subsystem)
	sp, err := c.path(subsystem)
	if err != nil {
		return nil, err
	}
	path := s.(pather).Path(sp)
	var processes []Process
	err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !recursive && info.IsDir() {
			if p == path {
				return nil
			}
			return filepath.SkipDir
		}
		dir, name := filepath.Split(p)
		if name != cgroupProcs {
			return nil
		}
		procs, err := readPids(dir, subsystem)
		if err != nil {
			return err
		}
		processes = append(processes, procs...)
		return nil
	})
	return processes, err
}

// Freeze freezes the entire cgroup and all the processes inside it
func (c *cgroup) Freeze() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	s := c.getSubsystem(Freezer)
	if s == nil {
		return ErrFreezerNotSupported
	}
	sp, err := c.path(Freezer)
	if err != nil {
		return err
	}
	return s.(*freezerController).Freeze(sp)
}

// Thaw thaws out the cgroup and all the processes inside it
func (c *cgroup) Thaw() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	s := c.getSubsystem(Freezer)
	if s == nil {
		return ErrFreezerNotSupported
	}
	sp, err := c.path(Freezer)
	if err != nil {
		return err
	}
	return s.(*freezerController).Thaw(sp)
}

// OOMEventFD returns the memory cgroup's out of memory event fd that triggers
// when processes inside the cgroup receive an oom event
func (c *cgroup) OOMEventFD() (uintptr, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return 0, c.err
	}
	s := c.getSubsystem(Memory)
	if s == nil {
		return 0, ErrMemoryNotSupported
	}
	sp, err := c.path(Memory)
	if err != nil {
		return 0, err
	}
	return s.(*memoryController).OOMEventFD(sp)
}

// State returns the state of the cgroup and its processes
func (c *cgroup) State() State {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.checkExists()
	if c.err != nil && c.err == ErrCgroupDeleted {
		return Deleted
	}
	s := c.getSubsystem(Freezer)
	if s == nil {
		return Thawed
	}
	sp, err := c.path(Freezer)
	if err != nil {
		return Unknown
	}
	state, err := s.(*freezerController).state(sp)
	if err != nil {
		return Unknown
	}
	return state
}

// MoveTo does a recursive move subsystem by subsystem of all the processes
// inside the group
func (c *cgroup) MoveTo(destination Cgroup) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	for _, s := range c.subsystems {
		processes, err := c.processes(s.Name(), true)
		if err != nil {
			return err
		}
		for _, p := range processes {
			if err := destination.Add(p); err != nil {
				return err
			}
		}
	}
	return nil
}

func (c *cgroup) getSubsystem(n Name) Subsystem {
	for _, s := range c.subsystems {
		if s.Name() == n {
			return s
		}
	}
	return nil
}

func (c *cgroup) checkExists() {
	for _, s := range pathers(c.subsystems) {
		p, err := c.path(s.Name())
		if err != nil {
			return
		}
		if _, err := os.Lstat(s.Path(p)); err != nil {
			if os.IsNotExist(err) {
				c.err = ErrCgroupDeleted
				return
			}
		}
	}
}
<commit_msg>Update doc for OOMEventFD<commit_after>package cgroups

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"

	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
)

// New returns a new control via the cgroup cgroups interface
func New(hierarchy Hierarchy, path Path, resources *specs.LinuxResources) (Cgroup, error) {
	subsystems, err := hierarchy()
	if err != nil {
		return nil, err
	}
	for _, s := range subsystems {
		if err := initializeSubsystem(s, path, resources); err != nil {
			return nil, err
		}
	}
	return &cgroup{
		path:       path,
		subsystems: subsystems,
	}, nil
}

// Load will load an existing cgroup and allow it to be controlled
func Load(hierarchy Hierarchy, path Path) (Cgroup, error) {
	subsystems, err := hierarchy()
	if err != nil {
		return nil, err
	}
	// check the the subsystems still exist
	for _, s := range pathers(subsystems) {
		p, err := path(s.Name())
		if err != nil {
			if os.IsNotExist(errors.Cause(err)) {
				return nil, ErrCgroupDeleted
			}
			return nil, err
		}
		if _, err := os.Lstat(s.Path(p)); err != nil {
			if os.IsNotExist(err) {
				return nil, ErrCgroupDeleted
			}
			return nil, err
		}
	}
	return &cgroup{
		path:       path,
		subsystems: subsystems,
	}, nil
}

type cgroup struct {
	path Path

	subsystems []Subsystem
	mu         sync.Mutex
	err        error
}

// New returns a new sub cgroup
func (c *cgroup) New(name string, resources *specs.LinuxResources) (Cgroup, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return nil, c.err
	}
	path := subPath(c.path, name)
	for _, s := range c.subsystems {
		if err := initializeSubsystem(s, path, resources); err != nil {
			return nil, err
		}
	}
	return &cgroup{
		path:       path,
		subsystems: c.subsystems,
	}, nil
}

// Subsystems returns all the subsystems that are currently being
// consumed by the group
func (c *cgroup) Subsystems() []Subsystem {
	return c.subsystems
}

// Add moves the provided process into the new cgroup
func (c *cgroup) Add(process Process) error {
	if process.Pid <= 0 {
		return ErrInvalidPid
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	return c.add(process)
}

func (c *cgroup) add(process Process) error {
	for _, s := range pathers(c.subsystems) {
		p, err := c.path(s.Name())
		if err != nil {
			return err
		}
		if err := ioutil.WriteFile(
			filepath.Join(s.Path(p), cgroupProcs),
			[]byte(strconv.Itoa(process.Pid)),
			defaultFilePerm,
		); err != nil {
			return err
		}
	}
	return nil
}

// Delete will remove the control group from each of the subsystems registered
func (c *cgroup) Delete() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	var errors []string
	for _, s := range c.subsystems {
		if d, ok := s.(deleter); ok {
			sp, err := c.path(s.Name())
			if err != nil {
				return err
			}
			if err := d.Delete(sp); err != nil {
				errors = append(errors, string(s.Name()))
			}
			continue
		}
		if p, ok := s.(pather); ok {
			sp, err := c.path(s.Name())
			if err != nil {
				return err
			}
			path := p.Path(sp)
			if err := remove(path); err != nil {
				errors = append(errors, path)
			}
		}
	}
	if len(errors) > 0 {
		return fmt.Errorf("cgroups: unable to remove paths %s", strings.Join(errors, ", "))
	}
	c.err = ErrCgroupDeleted
	return nil
}

// Stat returns the current metrics for the cgroup
func (c *cgroup) Stat(handlers ...ErrorHandler) (*Metrics, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return nil, c.err
	}
	if len(handlers) == 0 {
		handlers = append(handlers, errPassthrough)
	}
	var (
		stats = &Metrics{
			CPU: &CPUStat{
				Throttling: &Throttle{},
				Usage:      &CPUUsage{},
			},
		}
		wg   = &sync.WaitGroup{}
		errs = make(chan error, len(c.subsystems))
	)
	for _, s := range c.subsystems {
		if ss, ok := s.(stater); ok {
			sp, err := c.path(s.Name())
			if err != nil {
				return nil, err
			}
			wg.Add(1)
			go func() {
				defer wg.Done()
				if err := ss.Stat(sp, stats); err != nil {
					for _, eh := range handlers {
						if herr := eh(err); herr != nil {
							errs <- herr
						}
					}
				}
			}()
		}
	}
	wg.Wait()
	close(errs)
	for err := range errs {
		return nil, err
	}
	return stats, nil
}

// Update updates the cgroup with the new resource values provided
//
// Be prepared to handle EBUSY when trying to update a cgroup with
// live processes and other operations like Stats being performed at the
// same time
func (c *cgroup) Update(resources *specs.LinuxResources) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	for _, s := range c.subsystems {
		if u, ok := s.(updater); ok {
			sp, err := c.path(s.Name())
			if err != nil {
				return err
			}
			if err := u.Update(sp, resources); err != nil {
				return err
			}
		}
	}
	return nil
}

// Processes returns the processes running inside the cgroup along
// with the subsystem used, pid, and path
func (c *cgroup) Processes(subsystem Name, recursive bool) ([]Process, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return nil, c.err
	}
	return c.processes(subsystem, recursive)
}

func (c *cgroup) processes(subsystem Name, recursive bool) ([]Process, error) {
	s := c.getSubsystem(subsystem)
	sp, err := c.path(subsystem)
	if err != nil {
		return nil, err
	}
	path := s.(pather).Path(sp)
	var processes []Process
	err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !recursive && info.IsDir() {
			if p == path {
				return nil
			}
			return filepath.SkipDir
		}
		dir, name := filepath.Split(p)
		if name != cgroupProcs {
			return nil
		}
		procs, err := readPids(dir, subsystem)
		if err != nil {
			return err
		}
		processes = append(processes, procs...)
		return nil
	})
	return processes, err
}

// Freeze freezes the entire cgroup and all the processes inside it
func (c *cgroup) Freeze() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	s := c.getSubsystem(Freezer)
	if s == nil {
		return ErrFreezerNotSupported
	}
	sp, err := c.path(Freezer)
	if err != nil {
		return err
	}
	return s.(*freezerController).Freeze(sp)
}

// Thaw thaws out the cgroup and all the processes inside it
func (c *cgroup) Thaw() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	s := c.getSubsystem(Freezer)
	if s == nil {
		return ErrFreezerNotSupported
	}
	sp, err := c.path(Freezer)
	if err != nil {
		return err
	}
	return s.(*freezerController).Thaw(sp)
}

// OOMEventFD returns the memory cgroup's out of memory event fd that triggers
// when processes inside the cgroup receive an oom event. Returns
// ErrMemoryNotSupported if memory cgroups is not supported.
func (c *cgroup) OOMEventFD() (uintptr, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return 0, c.err
	}
	s := c.getSubsystem(Memory)
	if s == nil {
		return 0, ErrMemoryNotSupported
	}
	sp, err := c.path(Memory)
	if err != nil {
		return 0, err
	}
	return s.(*memoryController).OOMEventFD(sp)
}

// State returns the state of the cgroup and its processes
func (c *cgroup) State() State {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.checkExists()
	if c.err != nil && c.err == ErrCgroupDeleted {
		return Deleted
	}
	s := c.getSubsystem(Freezer)
	if s == nil {
		return Thawed
	}
	sp, err := c.path(Freezer)
	if err != nil {
		return Unknown
	}
	state, err := s.(*freezerController).state(sp)
	if err != nil {
		return Unknown
	}
	return state
}

// MoveTo does a recursive move subsystem by subsystem of all the processes
// inside the group
func (c *cgroup) MoveTo(destination Cgroup) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err != nil {
		return c.err
	}
	for _, s := range c.subsystems {
		processes, err := c.processes(s.Name(), true)
		if err != nil {
			return err
		}
		for _, p := range processes {
			if err := destination.Add(p); err != nil {
				return err
			}
		}
	}
	return nil
}

func (c *cgroup) getSubsystem(n Name) Subsystem {
	for _, s := range c.subsystems {
		if s.Name() == n {
			return s
		}
	}
	return nil
}

func (c *cgroup) checkExists() {
	for _, s := range pathers(c.subsystems) {
		p, err := c.path(s.Name())
		if err != nil {
			return
		}
		if _, err := os.Lstat(s.Path(p)); err != nil {
			if os.IsNotExist(err) {
				c.err = ErrCgroupDeleted
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package bitbucket

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/bitbucket"
	"golang.org/x/oauth2/clientcredentials"
)

const DEFAULT_PAGE_LENGTH = 10
const DEFAULT_LIMIT_PAGES = 0
const DEFAULT_MAX_DEPTH = 1
const DEFAULT_BITBUCKET_API_BASE_URL = "https://api.bitbucket.org/2.0"

func apiBaseUrl() (*url.URL, error) {
	ev := os.Getenv("BITBUCKET_API_BASE_URL")
	if ev == "" {
		ev = DEFAULT_BITBUCKET_API_BASE_URL
	}

	return url.Parse(ev)
}

type Client struct {
	Auth         *auth
	Users        users
	User         user
	Teams        teams
	Repositories *Repositories
	Workspaces   *Workspace
	Pagelen      int
	MaxDepth     int
	// LimitPages limits the number of pages for a request
	//	default value as 0 -- disable limits
	LimitPages int
	// DisableAutoPaging allows you to disable the default behavior of automatically requesting
	// all the pages for a paginated response.
	DisableAutoPaging bool
	apiBaseURL        *url.URL

	HttpClient *http.Client
}

type auth struct {
	appID, secret  string
	user, password string
	token          oauth2.Token
	bearerToken    string
}

type Response struct {
	Size     int           `json:"size"`
	Page     int           `json:"page"`
	Pagelen  int           `json:"pagelen"`
	Next     string        `json:"next"`
	Previous string        `json:"previous"`
	Values   []interface{} `json:"values"`
}

// Uses the Client Credentials Grant oauth2 flow to authenticate to Bitbucket
func NewOAuthClientCredentials(i, s string) *Client {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &clientcredentials.Config{
		ClientID:     i,
		ClientSecret: s,
		TokenURL:     bitbucket.Endpoint.TokenURL,
	}

	tok, err := conf.Token(ctx)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a)

}

func NewOAuth(i, s string) *Client {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &oauth2.Config{
		ClientID:     i,
		ClientSecret: s,
		Endpoint:     bitbucket.Endpoint,
	}

	// Redirect user to consent page to ask for permission
	// for the scopes specified above.
	url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
	fmt.Printf("Visit the URL for the auth dialog:\n%v", url)

	// Use the authorization code that is pushed to the redirect
	// URL. Exchange will do the handshake to retrieve the
	// initial access token. The HTTP Client returned by
	// conf.Client will refresh the token as necessary.
	var code string
	fmt.Printf("Enter the code in the return URL: ")
	if _, err := fmt.Scan(&code); err != nil {
		log.Fatal(err)
	}
	tok, err := conf.Exchange(ctx, code)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a)
}

// NewOAuthWithCode finishes the OAuth handshake with a given code
// and returns a *Client
func NewOAuthWithCode(i, s, c string) (*Client, string) {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &oauth2.Config{
		ClientID:     i,
		ClientSecret: s,
		Endpoint:     bitbucket.Endpoint,
	}

	tok, err := conf.Exchange(ctx, c)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a), tok.AccessToken
}

func NewOAuthbearerToken(t string) *Client {
	a := &auth{bearerToken: t}
	return injectClient(a)
}

func NewBasicAuth(u, p string) *Client {
	a := &auth{user: u, password: p}
	return injectClient(a)
}

func injectClient(a *auth) *Client {
	bitbucketUrl, err := apiBaseUrl()
	if err != nil {
		log.Fatalf("invalid bitbucket url")
	}
	c := &Client{Auth: a, Pagelen: DEFAULT_PAGE_LENGTH, MaxDepth: DEFAULT_MAX_DEPTH,
		apiBaseURL: bitbucketUrl, LimitPages: DEFAULT_LIMIT_PAGES}
	c.Repositories = &Repositories{
		c:                  c,
		PullRequests:       &PullRequests{c: c},
		Pipelines:          &Pipelines{c: c},
		Repository:         &Repository{c: c},
		Issues:             &Issues{c: c},
		Commits:            &Commits{c: c},
		Diff:               &Diff{c: c},
		BranchRestrictions: &BranchRestrictions{c: c},
		Webhooks:           &Webhooks{c: c},
		Downloads:          &Downloads{c: c},
		DeployKeys:         &DeployKeys{c: c},
	}
	c.Users = &Users{c: c}
	c.User = &User{c: c}
	c.Teams = &Teams{c: c}
	c.Workspaces = &Workspace{c: c, Repositories: c.Repositories, Permissions: &Permission{c: c}}
	c.HttpClient = new(http.Client)
	return c
}

func (c *Client) GetOAuthToken() oauth2.Token {
	return c.Auth.token
}

func (c *Client) GetApiBaseURL() string {
	return fmt.Sprintf("%s%s", c.GetApiHostnameURL(), c.apiBaseURL.Path)
}

func (c *Client) GetApiHostnameURL() string {
	return fmt.Sprintf("%s://%s", c.apiBaseURL.Scheme, c.apiBaseURL.Host)
}

func (c *Client) SetApiBaseURL(urlStr url.URL) {
	c.apiBaseURL = &urlStr
}

func (c *Client) executeRaw(method string, urlStr string, text string) (io.ReadCloser, error) {
	body := strings.NewReader(text)

	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	if text != "" {
		req.Header.Set("Content-Type", "application/json")
	}

	c.authenticateRequest(req)
	return c.doRawRequest(req, false)
}

func (c *Client) execute(method string, urlStr string, text string) (interface{}, error) {
	body := strings.NewReader(text)
	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	if text != "" {
		req.Header.Set("Content-Type", "application/json")
	}

	c.authenticateRequest(req)
	result, err := c.doRequest(req, false)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (c *Client) executePaginated(method string, urlStr string, text string) (interface{}, error) {
	if c.Pagelen != DEFAULT_PAGE_LENGTH {
		urlObj, err := url.Parse(urlStr)
		if err != nil {
			return nil, err
		}
		q := urlObj.Query()
		q.Set("pagelen", strconv.Itoa(c.Pagelen))
		urlObj.RawQuery = q.Encode()
		urlStr = urlObj.String()
	}

	body := strings.NewReader(text)
	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	if text != "" {
		req.Header.Set("Content-Type", "application/json")
	}

	c.authenticateRequest(req)
	result, err := c.doPaginatedRequest(req, false)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (c *Client) executeFileUpload(method string, urlStr string, filePath string, fileName string, fieldname string, params map[string]string) (interface{}, error) {
	fileReader, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer fileReader.Close()

	// Prepare a form that you will submit to that URL.
	var b bytes.Buffer
	w := multipart.NewWriter(&b)

	var fw io.Writer
	if fw, err = w.CreateFormFile(fieldname, fileName); err != nil {
		return nil, err
	}

	if _, err = io.Copy(fw, fileReader); err != nil {
		return nil, err
	}

	for key, value := range params {
		err = w.WriteField(key, value)
		if err != nil {
			return nil, err
		}
	}

	// Don't forget to close the multipart writer.
	// If you don't close it, your request will be missing the terminating boundary.
	w.Close()

	// Now that you have a form, you can submit it to your handler.
	req, err := http.NewRequest(method, urlStr, &b)
	if err != nil {
		return nil, err
	}
	// Don't forget to set the content type, this will contain the boundary.
	req.Header.Set("Content-Type", w.FormDataContentType())

	c.authenticateRequest(req)
	return c.doRequest(req, true)

}

func (c *Client) authenticateRequest(req *http.Request) {
	if c.Auth.bearerToken != "" {
		req.Header.Set("Authorization", "Bearer "+c.Auth.bearerToken)
	}

	if c.Auth.user != "" && c.Auth.password != "" {
		req.SetBasicAuth(c.Auth.user, c.Auth.password)
	} else if c.Auth.token.Valid() {
		c.Auth.token.SetAuthHeader(req)
	}
}

func (c *Client) doRequest(req *http.Request, emptyResponse bool) (interface{}, error) {
	resBody, err := c.doRawRequest(req, emptyResponse)
	if err != nil {
		return nil, err
	}
	if emptyResponse || resBody == nil {
		return nil, nil
	}

	defer resBody.Close()

	responseBytes, err := ioutil.ReadAll(resBody)
	if err != nil {
		return resBody, err
	}

	var result interface{}
	if err := json.Unmarshal(responseBytes, &result); err != nil {
		log.Println("Could not unmarshal JSON payload, returning raw response")
		return responseBytes, nil
	}
	return result, nil
}

func (c *Client) doPaginatedRequest(req *http.Request, emptyResponse bool) (interface{}, error) {
	resBody, err := c.doRawRequest(req, emptyResponse)
	if err != nil {
		return nil, err
	}
	if emptyResponse || resBody == nil {
		return nil, nil
	}

	defer resBody.Close()

	responseBytes, err := ioutil.ReadAll(resBody)
	if err != nil {
		return resBody, err
	}

	responsePaginated := &Response{}
	var curPage int

	err = json.Unmarshal(responseBytes, responsePaginated)
	if err == nil && len(responsePaginated.Values) > 0 {
		var values []interface{}
		for {
			curPage++
			values = append(values, responsePaginated.Values...)
			if c.DisableAutoPaging || len(responsePaginated.Next) == 0 ||
				(curPage >= c.LimitPages && c.LimitPages != 0) {
				break
			}
			newReq, err := http.NewRequest(req.Method, responsePaginated.Next, nil)
			if err != nil {
				return resBody, err
			}
			c.authenticateRequest(newReq)
			resp, err := c.doRawRequest(newReq, false)
			if err != nil {
				return resBody, err
			}

			responsePaginated = &Response{}
			json.NewDecoder(resp).Decode(responsePaginated)
		}
		responsePaginated.Values = values
		responseBytes, err = json.Marshal(responsePaginated)
		if err != nil {
			return resBody, err
		}
	}

	var result interface{}
	if err := json.Unmarshal(responseBytes, &result); err != nil {
		log.Println("Could not unmarshal JSON payload, returning raw response")
		return resBody, err
	}
	return result, nil
}

func (c *Client) doRawRequest(req *http.Request, emptyResponse bool) (io.ReadCloser, error) {
	resp, err := c.HttpClient.Do(req)
	if err != nil {
		return nil, err
	}

	if unexpectedHttpStatusCode(resp.StatusCode) {
		defer resp.Body.Close()

		out := &UnexpectedResponseStatusError{Status: resp.Status}

		body, err := io.ReadAll(resp.Body)
		if err != nil {
			out.Body = []byte(fmt.Sprintf("could not read the response body: %v", err))
		} else {
			out.Body = body
		}

		return nil, out
	}

	if emptyResponse || resp.StatusCode == http.StatusNoContent {
		resp.Body.Close()
		return nil, nil
	}

	if resp.Body == nil {
		return nil, fmt.Errorf("response body is nil")
	}

	return resp.Body, nil
}

func unexpectedHttpStatusCode(statusCode int) bool {
	switch statusCode {
	case http.StatusOK,
		http.StatusCreated,
		http.StatusNoContent,
		http.StatusAccepted:
		return false
	default:
		return true
	}
}

func (c *Client) requestUrl(template string, args ...interface{}) string {

	if len(args) == 1 && args[0] == "" {
		return c.GetApiBaseURL() + template
	}
	return c.GetApiBaseURL() + fmt.Sprintf(template, args...)
}

func (c *Client) addMaxDepthParam(params *url.Values, customMaxDepth *int) {
	maxDepth := c.MaxDepth
	if customMaxDepth != nil && *customMaxDepth > 0 {
		maxDepth = *customMaxDepth
	}

	if maxDepth != DEFAULT_MAX_DEPTH {
		params.Set("max_depth", strconv.Itoa(maxDepth))
	}
}
<commit_msg>Don't print on errors. (#209)<commit_after>package bitbucket

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/bitbucket"
	"golang.org/x/oauth2/clientcredentials"
)

const DEFAULT_PAGE_LENGTH = 10
const DEFAULT_LIMIT_PAGES = 0
const DEFAULT_MAX_DEPTH = 1
const DEFAULT_BITBUCKET_API_BASE_URL = "https://api.bitbucket.org/2.0"

func apiBaseUrl() (*url.URL, error) {
	ev := os.Getenv("BITBUCKET_API_BASE_URL")
	if ev == "" {
		ev = DEFAULT_BITBUCKET_API_BASE_URL
	}

	return url.Parse(ev)
}

type Client struct {
	Auth         *auth
	Users        users
	User         user
	Teams        teams
	Repositories *Repositories
	Workspaces   *Workspace
	Pagelen      int
	MaxDepth     int
	// LimitPages limits the number of pages for a request
	//	default value as 0 -- disable limits
	LimitPages int
	// DisableAutoPaging allows you to disable the default behavior of automatically requesting
	// all the pages for a paginated response.
	DisableAutoPaging bool
	apiBaseURL        *url.URL

	HttpClient *http.Client
}

type auth struct {
	appID, secret  string
	user, password string
	token          oauth2.Token
	bearerToken    string
}

type Response struct {
	Size     int           `json:"size"`
	Page     int           `json:"page"`
	Pagelen  int           `json:"pagelen"`
	Next     string        `json:"next"`
	Previous string        `json:"previous"`
	Values   []interface{} `json:"values"`
}

// Uses the Client Credentials Grant oauth2 flow to authenticate to Bitbucket
func NewOAuthClientCredentials(i, s string) *Client {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &clientcredentials.Config{
		ClientID:     i,
		ClientSecret: s,
		TokenURL:     bitbucket.Endpoint.TokenURL,
	}

	tok, err := conf.Token(ctx)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a)

}

func NewOAuth(i, s string) *Client {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &oauth2.Config{
		ClientID:     i,
		ClientSecret: s,
		Endpoint:     bitbucket.Endpoint,
	}

	// Redirect user to consent page to ask for permission
	// for the scopes specified above.
	url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
	fmt.Printf("Visit the URL for the auth dialog:\n%v", url)

	// Use the authorization code that is pushed to the redirect
	// URL. Exchange will do the handshake to retrieve the
	// initial access token. The HTTP Client returned by
	// conf.Client will refresh the token as necessary.
	var code string
	fmt.Printf("Enter the code in the return URL: ")
	if _, err := fmt.Scan(&code); err != nil {
		log.Fatal(err)
	}
	tok, err := conf.Exchange(ctx, code)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a)
}

// NewOAuthWithCode finishes the OAuth handshake with a given code
// and returns a *Client
func NewOAuthWithCode(i, s, c string) (*Client, string) {
	a := &auth{appID: i, secret: s}
	ctx := context.Background()
	conf := &oauth2.Config{
		ClientID:     i,
		ClientSecret: s,
		Endpoint:     bitbucket.Endpoint,
	}

	tok, err := conf.Exchange(ctx, c)
	if err != nil {
		log.Fatal(err)
	}
	a.token = *tok
	return injectClient(a), tok.AccessToken
}

func NewOAuthbearerToken(t string) *Client {
	a := &auth{bearerToken: t}
	return injectClient(a)
}

func NewBasicAuth(u, p string) *Client {
	a := &auth{user: u, password: p}
	return injectClient(a)
}

func injectClient(a *auth) *Client {
	bitbucketUrl, err := apiBaseUrl()
	if err != nil {
		log.Fatalf("invalid bitbucket url")
	}
	c := &Client{Auth: a, Pagelen: DEFAULT_PAGE_LENGTH, MaxDepth: DEFAULT_MAX_DEPTH,
		apiBaseURL: bitbucketUrl, LimitPages: DEFAULT_LIMIT_PAGES}
	c.Repositories = &Repositories{
		c:                  c,
		PullRequests:       &PullRequests{c: c},
		Pipelines:          &Pipelines{c: c},
		Repository:         &Repository{c: c},
		Issues:             &Issues{c: c},
		Commits:            &Commits{c: c},
		Diff:               &Diff{c: c},
		BranchRestrictions: &BranchRestrictions{c: c},
		Webhooks:           &Webhooks{c: c},
		Downloads:          &Downloads{c: c},
		DeployKeys:         &DeployKeys{c: c},
	}
	c.Users = &Users{c: c}
	c.User = &User{c: c}
	c.Teams = &Teams{c: c}
	c.Workspaces = &Workspace{c: c, Repositories: c.Repositories, Permissions: &Permission{c: c}}
	c.HttpClient = new(http.Client)
	return c
}

func (c *Client) GetOAuthToken() oauth2.Token {
	return c.Auth.token
}

func (c *Client) GetApiBaseURL() string {
	return fmt.Sprintf("%s%s", c.GetApiHostnameURL(), c.apiBaseURL.Path)
}

func (c *Client) GetApiHostnameURL() string {
	return fmt.Sprintf("%s://%s", c.apiBaseURL.Scheme, c.apiBaseURL.Host)
}

func (c *Client) SetApiBaseURL(urlStr url.URL) {
	c.apiBaseURL = &urlStr
}

func (c *Client) executeRaw(method string, urlStr string, text string) (io.ReadCloser, error) {
	body := strings.NewReader(text)

	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	if text != "" {
		req.Header.Set("Content-Type", "application/json")
	}

	c.authenticateRequest(req)
	return c.doRawRequest(req, false)
}

func (c *Client) execute(method string, urlStr string, text string) (interface{}, error) {
	body := strings.NewReader(text)
	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	if text != "" {
		req.Header.Set("Content-Type", "application/json")
	}

	c.authenticateRequest(req)
	result, err := c.doRequest(req, false)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (c *Client) executePaginated(method string, urlStr string, text string) (interface{}, error) {
	if c.Pagelen != DEFAULT_PAGE_LENGTH {
		urlObj, err := url.Parse(urlStr)
		if err != nil {
			return nil, err
		}
		q := urlObj.Query()
		q.Set("pagelen", strconv.Itoa(c.Pagelen))
		urlObj.RawQuery = q.Encode()
		urlStr = urlObj.String()
	}

	body := strings.NewReader(text)
	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	if text != "" {
		req.Header.Set("Content-Type", "application/json")
	}

	c.authenticateRequest(req)
	result, err := c.doPaginatedRequest(req, false)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (c *Client) executeFileUpload(method string, urlStr string, filePath string, fileName string, fieldname string, params map[string]string) (interface{}, error) {
	fileReader, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer fileReader.Close()

	// Prepare a form that you will submit to that URL.
	var b bytes.Buffer
	w := multipart.NewWriter(&b)

	var fw io.Writer
	if fw, err = w.CreateFormFile(fieldname, fileName); err != nil {
		return nil, err
	}

	if _, err = io.Copy(fw, fileReader); err != nil {
		return nil, err
	}

	for key, value := range params {
		err = w.WriteField(key, value)
		if err != nil {
			return nil, err
		}
	}

	// Don't forget to close the multipart writer.
	// If you don't close it, your request will be missing the terminating boundary.
	w.Close()

	// Now that you have a form, you can submit it to your handler.
	req, err := http.NewRequest(method, urlStr, &b)
	if err != nil {
		return nil, err
	}
	// Don't forget to set the content type, this will contain the boundary.
	req.Header.Set("Content-Type", w.FormDataContentType())

	c.authenticateRequest(req)
	return c.doRequest(req, true)

}

func (c *Client) authenticateRequest(req *http.Request) {
	if c.Auth.bearerToken != "" {
		req.Header.Set("Authorization", "Bearer "+c.Auth.bearerToken)
	}

	if c.Auth.user != "" && c.Auth.password != "" {
		req.SetBasicAuth(c.Auth.user, c.Auth.password)
	} else if c.Auth.token.Valid() {
		c.Auth.token.SetAuthHeader(req)
	}
}

func (c *Client) doRequest(req *http.Request, emptyResponse bool) (interface{}, error) {
	resBody, err := c.doRawRequest(req, emptyResponse)
	if err != nil {
		return nil, err
	}
	if emptyResponse || resBody == nil {
		return nil, nil
	}

	defer resBody.Close()

	responseBytes, err := ioutil.ReadAll(resBody)
	if err != nil {
		return resBody, err
	}

	var result interface{}
	if err := json.Unmarshal(responseBytes, &result); err != nil {
		return responseBytes, err
	}
	return result, nil
}

func (c *Client) doPaginatedRequest(req *http.Request, emptyResponse bool) (interface{}, error) {
	resBody, err := c.doRawRequest(req, emptyResponse)
	if err != nil {
		return nil, err
	}
	if emptyResponse || resBody == nil {
		return nil, nil
	}

	defer resBody.Close()

	responseBytes, err := ioutil.ReadAll(resBody)
	if err != nil {
		return resBody, err
	}

	responsePaginated := &Response{}
	var curPage int

	err = json.Unmarshal(responseBytes, responsePaginated)
	if err == nil && len(responsePaginated.Values) > 0 {
		var values []interface{}
		for {
			curPage++
			values = append(values, responsePaginated.Values...)
			if c.DisableAutoPaging || len(responsePaginated.Next) == 0 ||
				(curPage >= c.LimitPages && c.LimitPages != 0) {
				break
			}
			newReq, err := http.NewRequest(req.Method, responsePaginated.Next, nil)
			if err != nil {
				return resBody, err
			}
			c.authenticateRequest(newReq)
			resp, err := c.doRawRequest(newReq, false)
			if err != nil {
				return resBody, err
			}

			responsePaginated = &Response{}
			json.NewDecoder(resp).Decode(responsePaginated)
		}
		responsePaginated.Values = values
		responseBytes, err = json.Marshal(responsePaginated)
		if err != nil {
			return resBody, err
		}
	}

	var result interface{}
	if err := json.Unmarshal(responseBytes, &result); err != nil {
		return resBody, err
	}
	return result, nil
}

func (c *Client) doRawRequest(req *http.Request, emptyResponse bool) (io.ReadCloser, error) {
	resp, err := c.HttpClient.Do(req)
	if err != nil {
		return nil, err
	}

	if unexpectedHttpStatusCode(resp.StatusCode) {
		defer resp.Body.Close()

		out := &UnexpectedResponseStatusError{Status: resp.Status}

		body, err := io.ReadAll(resp.Body)
		if err != nil {
			out.Body = []byte(fmt.Sprintf("could not read the response body: %v", err))
		} else {
			out.Body = body
		}

		return nil, out
	}

	if emptyResponse || resp.StatusCode == http.StatusNoContent {
		resp.Body.Close()
		return nil, nil
	}

	if resp.Body == nil {
		return nil, fmt.Errorf("response body is nil")
	}

	return resp.Body, nil
}

func unexpectedHttpStatusCode(statusCode int) bool {
	switch statusCode {
	case http.StatusOK,
		http.StatusCreated,
		http.StatusNoContent,
		http.StatusAccepted:
		return false
	default:
		return true
	}
}

func (c *Client) requestUrl(template string, args ...interface{}) string {

	if len(args) == 1 && args[0] == "" {
		return c.GetApiBaseURL() + template
	}
	return c.GetApiBaseURL() + fmt.Sprintf(template, args...)
}

func (c *Client) addMaxDepthParam(params *url.Values, customMaxDepth *int) {
	maxDepth := c.MaxDepth
	if customMaxDepth != nil && *customMaxDepth > 0 {
		maxDepth = *customMaxDepth
	}

	if maxDepth != DEFAULT_MAX_DEPTH {
		params.Set("max_depth", strconv.Itoa(maxDepth))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"

	"github.com/Shopify/sarama"
)

// custom offset predefined
const (
	lastOneHours    float64 = 0.01042
	lastFourHours   float64 = 0.041647
	lastTwelveHours float64 = 0.125
	lastOneDays     float64 = 0.25
	lastTwoDays     float64 = 0.5
	lastThreeDays   float64 = 0.75
	lastFourDays    float64 = 1.0
)

// CustomClient Adds an additional custom functionality surrounding offsets to the sarama client
type CustomClient struct {
	sarama.Client

	topic     string
	partition int32
}

// NewClient creates a new custom client
func (client CustomClient) NewClient(srcBrokers []string, topic string, partition int32) CustomClient {
	c, err := sarama.NewClient(srcBrokers, nil)
	if err != nil {
		log.Fatalln("ERROR:", err)
	}

	return CustomClient{c, topic, partition}
}

// GetNumPartitions gets the number of partitions for the topic
func (client CustomClient) GetNumPartitions() int {
	var list []int32
	list, err := client.Partitions(client.topic)
	if err != nil {
		log.Fatalln("Unable to get number of partitions: ", err)
	}
	return len(list)
}

// GetCustomOffset takes a fraction of the total data stored in kafka and gets a relative offset
func (client CustomClient) GetCustomOffset(fraction float64) int {

	newestOffset, err := client.GetOffset(client.topic, client.partition, sarama.OffsetNewest)
	if err != nil {
		log.Fatalln("ERROR:", err)
	}

	oldestOffset, err := client.GetOffset(client.topic, client.partition, sarama.OffsetOldest)
	if err != nil {
		log.Fatalln("ERROR:", err)
	}

	diff := newestOffset - oldestOffset

	fractionalOffset := float64(diff) * fraction

	return int(fractionalOffset)
}
<commit_msg>Don't attach NewClient to a custom client instance<commit_after>package main

import (
	"log"

	"github.com/Shopify/sarama"
)

// custom offset predefined
const (
	lastOneHours    float64 = 0.01042
	lastFourHours   float64 = 0.041647
	lastTwelveHours float64 = 0.125
	lastOneDays     float64 = 0.25
	lastTwoDays     float64 = 0.5
	lastThreeDays   float64 = 0.75
	lastFourDays    float64 = 1.0
)

// CustomClient Adds an additional custom functionality surrounding offsets to the sarama client
type CustomClient struct {
	sarama.Client

	topic     string
	partition int32
}

// NewClient creates a new custom client
func NewClient(srcBrokers []string, topic string, partition int32) CustomClient {
	c, err := sarama.NewClient(srcBrokers, nil)
	if err != nil {
		log.Fatalln("ERROR:", err)
	}

	return CustomClient{c, topic, partition}
}

// GetNumPartitions gets the number of partitions for the topic
func (client CustomClient) GetNumPartitions() int {
	var list []int32
	list, err := client.Partitions(client.topic)
	if err != nil {
		log.Fatalln("Unable to get number of partitions: ", err)
	}
	return len(list)
}

// GetCustomOffset takes a fraction of the total data stored in kafka and gets a relative offset
func (client CustomClient) GetCustomOffset(fraction float64) int {

	newestOffset, err := client.GetOffset(client.topic, client.partition, sarama.OffsetNewest)
	if err != nil {
		log.Fatalln("ERROR:", err)
	}

	oldestOffset, err := client.GetOffset(client.topic, client.partition, sarama.OffsetOldest)
	if err != nil {
		log.Fatalln("ERROR:", err)
	}

	diff := newestOffset - oldestOffset

	fractionalOffset := float64(diff) * fraction

	return int(fractionalOffset)
}
<|endoftext|>


<commit_before>package wsevent

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"net/http"
	"sync"
	"sync/atomic"
	"time"

	"github.com/dgrijalva/jwt-go"
	ws "github.com/gorilla/websocket"
)

//Client represents a server-side client
type Client struct {
	ID      string        //Session ID
	Request *http.Request //http Request when connection was upgraded
	Token   *jwt.Token    //if any

	writeMu *sync.Mutex
	conn    *ws.Conn
	server  *Server
	closed  *int32
}

type request struct {
	ID   string          `json:"id"`
	Data json.RawMessage `json:"data"`

	next *request
}

type reply struct {
	ID   string      `json:"id"`
	Data interface{} `json:"data"`

	next *reply
}

func genID(r *http.Request) string {
	buff := make([]byte, 5)
	rand.Read(buff)
	b := bytes.NewBuffer(buff)
	b.WriteString(r.RemoteAddr)

	return base64.URLEncoding.EncodeToString(b.Bytes())
}

func (s *Server) NewClientWithID(upgrader ws.Upgrader, w http.ResponseWriter, r *http.Request, id string) (*Client, error) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return nil, err
	}

	client := &Client{
		ID:      id,
		Request: r,

		writeMu: new(sync.Mutex),
		conn:    conn,
		server:  s,
		closed:  new(int32),
	}

	go client.listener(s)

	return client, nil
}

func (s *Server) NewClient(upgrader ws.Upgrader, w http.ResponseWriter, r *http.Request) (*Client, error) {
	return s.NewClientWithID(upgrader, w, r, genID(r))
}

//A thread-safe variant of WriteMessage
func (c *Client) Emit(data string) {
	c.writeMu.Lock()
	c.conn.WriteMessage(ws.TextMessage, []byte(data))
	c.writeMu.Unlock()
}

type emitJS struct {
	Id   int         `json:"id"`
	Data interface{} `json:"data"`
}

//A thread-safe variant of EmitJSON
func (c *Client) EmitJSON(v interface{}) error {
	js := emitJS{}
	js.Id = -1
	js.Data = v

	bytes, err := json.Marshal(js)
	if err != nil {
		return err
	}

	c.Emit(string(bytes))
	return nil
}

func (c *Client) Close() {
	c.conn.Close()
}

func (c *Client) cleanup(s *Server) {
	if atomic.LoadInt32(c.closed) == 1 {
		return
	}

	atomic.StoreInt32(c.closed, 1)
	c.conn.Close()

	s.joinedRoomsMu.RLock()
	for _, room := range s.joinedRooms[c.ID] {
		//log.Println(room)
		s.roomsMu.Lock()
		for i, client := range s.rooms[room] {
			if client.ID == c.ID {
				clients := s.rooms[room]
				clients[i] = clients[len(clients)-1]
				clients[len(clients)-1] = nil
				s.rooms[room] = clients[:len(clients)-1]
				if len(s.rooms[room]) == 0 {
					delete(s.rooms, room)
				}
				break
			}
		}

		s.roomsMu.Unlock()
	}
	s.joinedRoomsMu.RUnlock()

	s.joinedRoomsMu.Lock()
	delete(s.joinedRooms, c.ID)
	s.joinedRoomsMu.Unlock()

	if s.OnDisconnect != nil {
		s.OnDisconnect(c.ID, c.Token)
	}
}

func (c *Client) listener(s *Server) {
	tick := time.NewTicker(time.Millisecond * 10)
	for {
		<-tick.C
		_, data, err := c.conn.ReadMessage()
		if atomic.LoadInt32(s.closed) == 1 {
			return
		}

		if err != nil {
			c.cleanup(s)
			tick.Stop()
			return
		}

		req := s.getRequest()

		if err := json.Unmarshal(data, &req); err != nil {
			continue
		}

		callName := s.codec.ReadName(req.Data)

		s.handlersLock.RLock()
		f, ok := s.handlers[callName]
		s.handlersLock.RUnlock()

		var defaultHandler bool

		if !ok {
			if s.defaultHandler == nil {
				continue
			}
			f = s.defaultHandler
			defaultHandler = true
		}

		s.Requests.Add(1)
		reply := s.getReply()
		reply.ID = req.ID
		if defaultHandler {
			reply.Data, err = s.call(c, f, []byte("{}"))
		} else {
			reply.Data, err = s.call(c, f, req.Data)
		}
		if err != nil {
			reply.Data = s.codec.Error(err)
		}
		s.Requests.Done()

		go func() {
			bytes, _ := json.Marshal(reply)

			c.Emit(string(bytes))
			s.freeRequest(req)
			s.freeReply(reply)
		}()

	}
}
<commit_msg>Delegate requests to separate goroutines<commit_after>package wsevent

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"net/http"
	"sync"
	"sync/atomic"
	"time"

	"github.com/dgrijalva/jwt-go"
	ws "github.com/gorilla/websocket"
)

//Client represents a server-side client
type Client struct {
	ID      string        //Session ID
	Request *http.Request //http Request when connection was upgraded
	Token   *jwt.Token    //if any

	writeMu *sync.Mutex
	conn    *ws.Conn
	server  *Server
	closed  *int32
}

type request struct {
	ID   string          `json:"id"`
	Data json.RawMessage `json:"data"`

	next *request
}

type reply struct {
	ID   string      `json:"id"`
	Data interface{} `json:"data"`

	next *reply
}

func genID(r *http.Request) string {
	buff := make([]byte, 5)
	rand.Read(buff)
	b := bytes.NewBuffer(buff)
	b.WriteString(r.RemoteAddr)

	return base64.URLEncoding.EncodeToString(b.Bytes())
}

func (s *Server) NewClientWithID(upgrader ws.Upgrader, w http.ResponseWriter, r *http.Request, id string) (*Client, error) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return nil, err
	}

	client := &Client{
		ID:      id,
		Request: r,

		writeMu: new(sync.Mutex),
		conn:    conn,
		server:  s,
		closed:  new(int32),
	}

	go client.listener(s)

	return client, nil
}

func (s *Server) NewClient(upgrader ws.Upgrader, w http.ResponseWriter, r *http.Request) (*Client, error) {
	return s.NewClientWithID(upgrader, w, r, genID(r))
}

//A thread-safe variant of WriteMessage
func (c *Client) Emit(data string) {
	c.writeMu.Lock()
	c.conn.WriteMessage(ws.TextMessage, []byte(data))
	c.writeMu.Unlock()
}

type emitJS struct {
	Id   int         `json:"id"`
	Data interface{} `json:"data"`
}

//A thread-safe variant of EmitJSON
func (c *Client) EmitJSON(v interface{}) error {
	js := emitJS{}
	js.Id = -1
	js.Data = v

	bytes, err := json.Marshal(js)
	if err != nil {
		return err
	}

	c.Emit(string(bytes))
	return nil
}

func (c *Client) Close() {
	c.conn.Close()
}

func (c *Client) cleanup(s *Server) {
	if atomic.LoadInt32(c.closed) == 1 {
		return
	}

	atomic.StoreInt32(c.closed, 1)
	c.conn.Close()

	s.joinedRoomsMu.RLock()
	for _, room := range s.joinedRooms[c.ID] {
		//log.Println(room)
		s.roomsMu.Lock()
		for i, client := range s.rooms[room] {
			if client.ID == c.ID {
				clients := s.rooms[room]
				clients[i] = clients[len(clients)-1]
				clients[len(clients)-1] = nil
				s.rooms[room] = clients[:len(clients)-1]
				if len(s.rooms[room]) == 0 {
					delete(s.rooms, room)
				}
				break
			}
		}

		s.roomsMu.Unlock()
	}
	s.joinedRoomsMu.RUnlock()

	s.joinedRoomsMu.Lock()
	delete(s.joinedRooms, c.ID)
	s.joinedRoomsMu.Unlock()

	if s.OnDisconnect != nil {
		s.OnDisconnect(c.ID, c.Token)
	}
}

func (c *Client) listener(s *Server) {
	tick := time.NewTicker(time.Millisecond * 10)
	for {
		<-tick.C
		_, data, err := c.conn.ReadMessage()
		if atomic.LoadInt32(s.closed) == 1 {
			return
		}

		if err != nil {
			c.cleanup(s)
			tick.Stop()
			return
		}

		req := s.getRequest()

		if err := json.Unmarshal(data, &req); err != nil {
			continue
		}

		callName := s.codec.ReadName(req.Data)

		s.handlersLock.RLock()
		f, ok := s.handlers[callName]
		s.handlersLock.RUnlock()

		var defaultHandler bool

		if !ok {
			if s.defaultHandler == nil {
				continue
			}
			f = s.defaultHandler
			defaultHandler = true
		}

		s.Requests.Add(1)
		go func() {
			reply := s.getReply()
			reply.ID = req.ID
			if defaultHandler {
				reply.Data, err = s.call(c, f, []byte("{}"))
			} else {
				reply.Data, err = s.call(c, f, req.Data)
			}
			if err != nil {
				reply.Data = s.codec.Error(err)
			}
			s.Requests.Done()

			bytes, _ := json.Marshal(reply)

			c.Emit(string(bytes))
			s.freeRequest(req)
			s.freeReply(reply)
		}()

	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
	"time"

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

const MSG_BUFFER int = 10

const HELP_TEXT string = `-> Available commands:
   /about
   /exit
   /help
   /list
   /nick $NAME
   /whois $NAME
`

const ABOUT_TEXT string = `-> ssh-chat is made by @shazow.

   It is a custom ssh server built in Go to serve a chat experience
   instead of a shell.

   Source: https://github.com/shazow/ssh-chat

   For more, visit shazow.net or follow at twitter.com/shazow
`

type Client struct {
	Server        *Server
	Conn          *ssh.ServerConn
	Msg           chan string
	Name          string
	Color         string
	Op            bool
	ready         chan struct{}
	term          *terminal.Terminal
	termWidth     int
	termHeight    int
	silencedUntil time.Time
}

func NewClient(server *Server, conn *ssh.ServerConn) *Client {
	return &Client{
		Server: server,
		Conn:   conn,
		Name:   conn.User(),
		Color:  RandomColor(),
		Msg:    make(chan string, MSG_BUFFER),
		ready:  make(chan struct{}, 1),
	}
}

func (c *Client) ColoredName() string {
    return ColorString(c.Color, c.Name)
}

func (c *Client) Write(msg string) {
	c.term.Write([]byte(msg + "\r\n"))
}

func (c *Client) WriteLines(msg []string) {
	for _, line := range msg {
		c.Write(line)
	}
}

func (c *Client) IsSilenced() bool {
	return c.silencedUntil.After(time.Now())
}

func (c *Client) Silence(d time.Duration) {
	c.silencedUntil = time.Now().Add(d)
}

func (c *Client) Resize(width int, height int) error {
	err := c.term.SetSize(width, height)
	if err != nil {
		logger.Errorf("Resize failed: %dx%d", width, height)
		return err
	}
	c.termWidth, c.termHeight = width, height
	return nil
}

func (c *Client) Rename(name string) {
	c.Name = name
	c.term.SetPrompt(fmt.Sprintf("[%s] ", c.ColoredName()))
}

func (c *Client) Fingerprint() string {
	return c.Conn.Permissions.Extensions["fingerprint"]
}

func (c *Client) handleShell(channel ssh.Channel) {
	defer channel.Close()

	// FIXME: This shouldn't live here, need to restructure the call chaining.
	c.Server.Add(c)
	go func() {
		// Block until done, then remove.
		c.Conn.Wait()
		c.Server.Remove(c)
	}()

	go func() {
		for msg := range c.Msg {
			c.Write(msg)
		}
	}()

	for {
		line, err := c.term.ReadLine()
		if err != nil {
			break
		}

		parts := strings.SplitN(line, " ", 3)
		isCmd := strings.HasPrefix(parts[0], "/")

		if isCmd {
			// TODO: Factor this out.
			switch parts[0] {
			case "/test-colors": // Shh, this command is a secret!
				c.Write(ColorString("32", "Lorem ipsum dolor sit amet,"))
				c.Write("consectetur " + ColorString("31;1", "adipiscing") + " elit.")
			case "/exit":
				channel.Close()
			case "/help":
				c.WriteLines(strings.Split(HELP_TEXT, "\n"))
			case "/about":
				c.WriteLines(strings.Split(ABOUT_TEXT, "\n"))
			case "/me":
				me := strings.TrimLeft(line, "/me")
				if me == "" {
					me = " is at a loss for words."
				}
				msg := fmt.Sprintf("** %s%s", c.ColoredName(), me)
				if c.IsSilenced() || len(msg) > 1000 {
					c.Msg <- fmt.Sprintf("-> Message rejected.")
				} else {
					c.Server.Broadcast(msg, nil)
				}
			case "/nick":
				if len(parts) == 2 {
					c.Server.Rename(c, parts[1])
				} else {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /nick $NAME")
				}
			case "/whois":
				if len(parts) == 2 {
					client := c.Server.Who(parts[1])
					if client != nil {
						version := RE_STRIP_TEXT.ReplaceAllString(string(client.Conn.ClientVersion()), "")
						if len(version) > 100 {
							version = "Evil Jerk with a superlong string"
						}
						c.Msg <- fmt.Sprintf("-> %s is %s via %s", client.ColoredName(), client.Fingerprint(), version)
					} else {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					}
				} else {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /whois $NAME")
				}
			case "/list":
				names := c.Server.List(nil)
				c.Msg <- fmt.Sprintf("-> %d connected: %s", len(names), strings.Join(names, ", "))
			case "/ban":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) != 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /ban $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.Write(fmt.Sprintf("-> Banned by %s.", c.ColoredName()))
						c.Server.Ban(fingerprint, nil)
						client.Conn.Close()
						c.Server.Broadcast(fmt.Sprintf("* %s was banned by %s", parts[1], c.ColoredName()), nil)
					}
				}
			case "/op":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) != 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /op $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.Write(fmt.Sprintf("-> Made op by %s.", c.ColoredName()))
						c.Server.Op(fingerprint)
					}
				}
			case "/silence":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) < 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /silence $NAME")
				} else {
					duration := time.Duration(5) * time.Minute
					if len(parts) >= 3 {
						parsedDuration, err := time.ParseDuration(parts[2])
						if err == nil {
							duration = parsedDuration
						}
					}
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						client.Silence(duration)
						client.Write(fmt.Sprintf("-> Silenced for %s by %s.", duration, c.ColoredName()))
					}
				}
			default:
				c.Msg <- fmt.Sprintf("-> Invalid command: %s", line)
			}
			continue
		}

		msg := fmt.Sprintf("%s: %s", c.ColoredName(), line)
		if c.IsSilenced() || len(msg) > 1000 {
			c.Msg <- fmt.Sprintf("-> Message rejected.")
			continue
		}
		c.Server.Broadcast(msg, c)
	}

}

func (c *Client) handleChannels(channels <-chan ssh.NewChannel) {
	prompt := fmt.Sprintf("[%s] ", c.ColoredName())

	hasShell := false

	for ch := range channels {
		if t := ch.ChannelType(); t != "session" {
			ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}

		channel, requests, err := ch.Accept()
		if err != nil {
			logger.Errorf("Could not accept channel: %v", err)
			continue
		}
		defer channel.Close()

		c.term = terminal.NewTerminal(channel, prompt)
		for req := range requests {
			var width, height int
			var ok bool

			switch req.Type {
			case "shell":
				if c.term != nil && !hasShell {
					go c.handleShell(channel)
					ok = true
					hasShell = true
				}
			case "pty-req":
				width, height, ok = parsePtyRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			case "window-change":
				width, height, ok = parseWinchRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			}

			if req.WantReply {
				req.Reply(ok, nil)
			}
		}
	}
}
<commit_msg>Autocomplete nick support<commit_after>package main

import (
	"fmt"
	"strings"
	"time"

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

const MSG_BUFFER int = 10

const HELP_TEXT string = `-> Available commands:
   /about
   /exit
   /help
   /list
   /nick $NAME
   /whois $NAME
`

const ABOUT_TEXT string = `-> ssh-chat is made by @shazow.

   It is a custom ssh server built in Go to serve a chat experience
   instead of a shell.

   Source: https://github.com/shazow/ssh-chat

   For more, visit shazow.net or follow at twitter.com/shazow
`

var autoCompleteFunc func(line string, pos int, key rune) (newLine string, newPos int, ok bool) = nil

type Client struct {
	Server        *Server
	Conn          *ssh.ServerConn
	Msg           chan string
	Name          string
	Color         string
	Op            bool
	ready         chan struct{}
	term          *terminal.Terminal
	termWidth     int
	termHeight    int
	silencedUntil time.Time
}

func NewClient(server *Server, conn *ssh.ServerConn) *Client {
	if autoCompleteFunc == nil {
		autoCompleteFunc = createAutoCompleteFunc(server)
	}
	return &Client{
		Server: server,
		Conn:   conn,
		Name:   conn.User(),
		Color:  RandomColor(),
		Msg:    make(chan string, MSG_BUFFER),
		ready:  make(chan struct{}, 1),
	}
}

func (c *Client) ColoredName() string {
	return ColorString(c.Color, c.Name)
}

func (c *Client) Write(msg string) {
	c.term.Write([]byte(msg + "\r\n"))
}

func (c *Client) WriteLines(msg []string) {
	for _, line := range msg {
		c.Write(line)
	}
}

func (c *Client) IsSilenced() bool {
	return c.silencedUntil.After(time.Now())
}

func (c *Client) Silence(d time.Duration) {
	c.silencedUntil = time.Now().Add(d)
}

func (c *Client) Resize(width int, height int) error {
	err := c.term.SetSize(width, height)
	if err != nil {
		logger.Errorf("Resize failed: %dx%d", width, height)
		return err
	}
	c.termWidth, c.termHeight = width, height
	return nil
}

func (c *Client) Rename(name string) {
	c.Name = name
	c.term.SetPrompt(fmt.Sprintf("[%s] ", c.ColoredName()))
}

func (c *Client) Fingerprint() string {
	return c.Conn.Permissions.Extensions["fingerprint"]
}

func (c *Client) handleShell(channel ssh.Channel) {
	defer channel.Close()

	// FIXME: This shouldn't live here, need to restructure the call chaining.
	c.Server.Add(c)
	go func() {
		// Block until done, then remove.
		c.Conn.Wait()
		c.Server.Remove(c)
	}()

	go func() {
		for msg := range c.Msg {
			c.Write(msg)
		}
	}()

	for {
		line, err := c.term.ReadLine()
		if err != nil {
			break
		}

		parts := strings.SplitN(line, " ", 3)
		isCmd := strings.HasPrefix(parts[0], "/")

		if isCmd {
			// TODO: Factor this out.
			switch parts[0] {
			case "/test-colors": // Shh, this command is a secret!
				c.Write(ColorString("32", "Lorem ipsum dolor sit amet,"))
				c.Write("consectetur " + ColorString("31;1", "adipiscing") + " elit.")
			case "/exit":
				channel.Close()
			case "/help":
				c.WriteLines(strings.Split(HELP_TEXT, "\n"))
			case "/about":
				c.WriteLines(strings.Split(ABOUT_TEXT, "\n"))
			case "/me":
				me := strings.TrimLeft(line, "/me")
				if me == "" {
					me = " is at a loss for words."
				}
				msg := fmt.Sprintf("** %s%s", c.ColoredName(), me)
				if c.IsSilenced() || len(msg) > 1000 {
					c.Msg <- fmt.Sprintf("-> Message rejected.")
				} else {
					c.Server.Broadcast(msg, nil)
				}
			case "/nick":
				if len(parts) == 2 {
					c.Server.Rename(c, parts[1])
				} else {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /nick $NAME")
				}
			case "/whois":
				if len(parts) == 2 {
					client := c.Server.Who(parts[1])
					if client != nil {
						version := RE_STRIP_TEXT.ReplaceAllString(string(client.Conn.ClientVersion()), "")
						if len(version) > 100 {
							version = "Evil Jerk with a superlong string"
						}
						c.Msg <- fmt.Sprintf("-> %s is %s via %s", client.ColoredName(), client.Fingerprint(), version)
					} else {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					}
				} else {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /whois $NAME")
				}
			case "/list":
				names := c.Server.List(nil)
				c.Msg <- fmt.Sprintf("-> %d connected: %s", len(names), strings.Join(names, ", "))
			case "/ban":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) != 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /ban $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.Write(fmt.Sprintf("-> Banned by %s.", c.ColoredName()))
						c.Server.Ban(fingerprint, nil)
						client.Conn.Close()
						c.Server.Broadcast(fmt.Sprintf("* %s was banned by %s", parts[1], c.ColoredName()), nil)
					}
				}
			case "/op":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) != 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /op $NAME")
				} else {
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						fingerprint := client.Fingerprint()
						client.Write(fmt.Sprintf("-> Made op by %s.", c.ColoredName()))
						c.Server.Op(fingerprint)
					}
				}
			case "/silence":
				if !c.Server.IsOp(c) {
					c.Msg <- fmt.Sprintf("-> You're not an admin.")
				} else if len(parts) < 2 {
					c.Msg <- fmt.Sprintf("-> Missing $NAME from: /silence $NAME")
				} else {
					duration := time.Duration(5) * time.Minute
					if len(parts) >= 3 {
						parsedDuration, err := time.ParseDuration(parts[2])
						if err == nil {
							duration = parsedDuration
						}
					}
					client := c.Server.Who(parts[1])
					if client == nil {
						c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
					} else {
						client.Silence(duration)
						client.Write(fmt.Sprintf("-> Silenced for %s by %s.", duration, c.ColoredName()))
					}
				}
			default:
				c.Msg <- fmt.Sprintf("-> Invalid command: %s", line)
			}
			continue
		}

		msg := fmt.Sprintf("%s: %s", c.ColoredName(), line)
		if c.IsSilenced() || len(msg) > 1000 {
			c.Msg <- fmt.Sprintf("-> Message rejected.")
			continue
		}
		c.Server.Broadcast(msg, c)
	}

}

func (c *Client) handleChannels(channels <-chan ssh.NewChannel) {
	prompt := fmt.Sprintf("[%s] ", c.ColoredName())

	hasShell := false

	for ch := range channels {
		if t := ch.ChannelType(); t != "session" {
			ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
			continue
		}

		channel, requests, err := ch.Accept()
		if err != nil {
			logger.Errorf("Could not accept channel: %v", err)
			continue
		}
		defer channel.Close()

		c.term = terminal.NewTerminal(channel, prompt)
		c.term.AutoCompleteCallback = autoCompleteFunc

		for req := range requests {
			var width, height int
			var ok bool

			switch req.Type {
			case "shell":
				if c.term != nil && !hasShell {
					go c.handleShell(channel)
					ok = true
					hasShell = true
				}
			case "pty-req":
				width, height, ok = parsePtyRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			case "window-change":
				width, height, ok = parseWinchRequest(req.Payload)
				if ok {
					err := c.Resize(width, height)
					ok = err == nil
				}
			}

			if req.WantReply {
				req.Reply(ok, nil)
			}
		}
	}
}

func createAutoCompleteFunc(server *Server) func(string, int, rune) (string, int, bool) {
	return func(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
		if key == 9 {
			shortLine := strings.Split(line[:pos], " ")
			partialNick := shortLine[len(shortLine)-1]

			nicks := server.List(&partialNick)
			if len(nicks) > 0 {
				nick := nicks[len(nicks)-1]
				posPartialNick := pos - len(partialNick)

				newLine = strings.Replace(line[posPartialNick:],
					partialNick, nick, 1)
				newLine = line[:posPartialNick] + newLine
				newPos = pos + (len(nick) - len(partialNick))
				ok = true
				fmt.Println(newLine)
			}
		} else {
			ok = false
		}
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/mailgun/pelican-protocol"
)

func main() {
	fmt.Printf("cli.go starting.\n")

	my_known_hosts_file := "my.known.hosts"
	h := pelican.NewKnownHosts(my_known_hosts_file)
	defer h.Close()

	fmt.Printf("cli.go done with NewKnownHosts().\n")
	_, err := h.SshMakeNewAcct(pelican.GetNewAcctPrivateKey(), "localhost", 2200)
	panicOn(err)

	/*
		out, err := h.SshConnect("root", "./id_rsa", "localhost", 2200, "pwd")
		if err != nil {
			panic(err)
		}
		fmt.Printf("out = '%s'\n", string(out))
	*/
}
<commit_msg>fix client<commit_after>package main

import (
	"fmt"

	"github.com/mailgun/pelican-protocol"
)

func main() {
	fmt.Printf("cli.go starting.\n")

	my_known_hosts_file := "my.known.hosts"
	h := pelican.NewKnownHosts(my_known_hosts_file)
	defer h.Close()

	fmt.Printf("cli.go done with NewKnownHosts().\n")
	_, err := h.SshMakeNewAcct(pelican.GetOriginatorPrivateKey(), "localhost", 2200)
	panicOn(err)

	/*
		out, err := h.SshConnect("root", "./id_rsa", "localhost", 2200, "pwd")
		if err != nil {
			panic(err)
		}
		fmt.Printf("out = '%s'\n", string(out))
	*/
}
<|endoftext|>


<commit_before>package eneru

import (
	"bytes"
	"encoding/json"
	"log"
	"net/http"
	"net/http/httputil"
)

//go:generate mockery --name Client

const (
	GET    = "GET"
	POST   = "POST"
	PUT    = "PUT"
	DELETE = "DELETE"
	HEAD   = "HEAD"
)

type Client struct {
	url        string
	debug      bool
	pretty     bool
	httpClient *http.Client
}

func NewClient(url string) (*Client, error) {
	c := &Client{
		url:        addTailingSlash(url),
		httpClient: http.DefaultClient,
	}

	if err := c.ping(); err != nil {
		return nil, err
	}

	return c, nil
}

func (c *Client) CreateIndex(index string) *CreateIndexReq {
	return NewCreateIndex(c, index)
}

func (c *Client) ExistIndex(index string) *ExistIndexReq {
	return NewExistIndex(c, index)
}

func (c *Client) Count() *CountReq {
	return NewCount(c)
}

func (c *Client) Delete(index string) *DeleteReq {
	return NewDelete(c, index)
}

func (c *Client) Index(index string) *IndexReq {
	return NewIndex(c, index)
}

func (c *Client) Update(index, ty string) *UpdateReq {
	return NewUpdate(c, index, ty)
}

func (c *Client) Search() *SearchReq {
	return NewSearch(c)
}

func (c *Client) UpdatePartial(index, ty string) *UpdatePartialReq {
	return NewUpdatePartial(c, index, ty)
}

func (c *Client) Debug(debug bool) {
	c.debug = debug
}

func (c *Client) Pretty(pretty bool) {
	c.pretty = pretty
}

func (c *Client) ping() error {
	resp, err := c.Request(HEAD, "/", nil, nil)
	if err != nil {
		return err
	}

	if resp.StatusCode != 200 {
		return ErrUnableConnect
	}

	return nil
}

func (c *Client) Request(method, path string, query *Query, body *bytes.Buffer) (*http.Response, error) {
	c.doPretty(query, body)

	r := c.buildRequest(method, path, query, body)
	if c.debug {
		c.dumpRequest(r)
	}

	resp, err := c.httpClient.Do(r)
	if err != nil {
		return nil, err
	}

	if c.debug {
		c.dumpResponse(resp)
	}

	if body != nil {
		bufPool.Put(body)
	}

	if err := c.checkResponse(resp); err != nil {
		return resp, err
	}

	return resp, err
}

func (c *Client) buildRequest(method, path string, query *Query, body *bytes.Buffer) *http.Request {
	var r *http.Request
	if body == nil {
		r, _ = http.NewRequest(method, buildUrl(c.url, path, query), nil)
	} else {
		r, _ = http.NewRequest(method, buildUrl(c.url, path, query), body)
	}

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

	return r
}

func (c *Client) doPretty(query *Query, body *bytes.Buffer) {
	if c.pretty {
		if query == nil {
			query = NewQuery()
		}
		query.Add("pretty", "true")

		if body != nil {
			data := make([]byte, body.Len())
			copy(data, body.Bytes())
			body.Truncate(0)
			json.Indent(body, data, "", "    ")
		}
	}
}

// dumpRequest dumps the given HTTP request.
func (c *Client) dumpRequest(r *http.Request) {
	out, err := httputil.DumpRequestOut(r, true)
	if err == nil {
		log.Printf("%s\n\n", string(out))
	}
}

// dumpResponse dumps the given HTTP response.
func (c *Client) dumpResponse(resp *http.Response) {
	out, err := httputil.DumpResponse(resp, true)
	if err == nil {
		log.Printf("%s\n\n", string(out))
	}
}

func (c *Client) checkResponse(resp *http.Response) error {
	if resp.StatusCode == 500 {
		return newErrResp(resp.Body)
	}

	return nil
}
<commit_msg>update<commit_after>package eneru

import (
	"bytes"
	"encoding/json"
	"log"
	"net/http"
	"net/http/httputil"
)

//go:generate mockery --name Client

const (
	GET    = "GET"
	POST   = "POST"
	PUT    = "PUT"
	DELETE = "DELETE"
	HEAD   = "HEAD"
)

type Client struct {
	url        string
	debug      bool
	pretty     bool
	httpClient *http.Client
}

func NewClient(url string) (*Client, error) {
	c := &Client{
		url:        addTailingSlash(url),
		httpClient: http.DefaultClient,
	}

	if err := c.ping(); err != nil {
		return nil, err
	}

	return c, nil
}

func (c *Client) CreateIndex(index string) *CreateIndexReq {
	return NewCreateIndex(c, index)
}

func (c *Client) ExistIndex(index string) *ExistIndexReq {
	return NewExistIndex(c, index)
}

func (c *Client) Count() *CountReq {
	return NewCount(c)
}

func (c *Client) Delete(index string) *DeleteReq {
	return NewDelete(c, index)
}

func (c *Client) Index(index string) *IndexReq {
	return NewIndex(c, index)
}

func (c *Client) Update(index, ty string) *UpdateReq {
	return NewUpdate(c, index, ty)
}

func (c *Client) Search() *SearchReq {
	return NewSearch(c)
}

func (c *Client) UpdatePartial(index, ty string) *UpdatePartialReq {
	return NewUpdatePartial(c, index, ty)
}

func (c *Client) Debug(debug bool) {
	c.debug = debug
}

func (c *Client) Pretty(pretty bool) {
	c.pretty = pretty
}

func (c *Client) ping() error {
	resp, err := c.Request(GET, "", nil, nil)
	if err != nil {
		return err
	}

	if resp.StatusCode != 200 {
		return ErrUnableConnect
	}

	return nil
}

func (c *Client) Request(method, path string, query *Query, body *bytes.Buffer) (*http.Response, error) {
	c.doPretty(query, body)

	r := c.buildRequest(method, path, query, body)
	if c.debug {
		c.dumpRequest(r)
	}

	resp, err := c.httpClient.Do(r)
	if err != nil {
		return nil, err
	}

	if c.debug {
		c.dumpResponse(resp)
	}

	if body != nil {
		bufPool.Put(body)
	}

	if err := c.checkResponse(resp); err != nil {
		return resp, err
	}

	return resp, err
}

func (c *Client) buildRequest(method, path string, query *Query, body *bytes.Buffer) *http.Request {
	var r *http.Request
	if body == nil {
		r, _ = http.NewRequest(method, buildUrl(c.url, path, query), nil)
	} else {
		r, _ = http.NewRequest(method, buildUrl(c.url, path, query), body)
	}

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

	return r
}

func (c *Client) doPretty(query *Query, body *bytes.Buffer) {
	if c.pretty {
		if query == nil {
			query = NewQuery()
		}
		query.Add("pretty", "true")

		if body != nil {
			data := make([]byte, body.Len())
			copy(data, body.Bytes())
			body.Truncate(0)
			json.Indent(body, data, "", "    ")
		}
	}
}

// dumpRequest dumps the given HTTP request.
func (c *Client) dumpRequest(r *http.Request) {
	out, err := httputil.DumpRequestOut(r, true)
	if err == nil {
		log.Printf("%s\n\n", string(out))
	}
}

// dumpResponse dumps the given HTTP response.
func (c *Client) dumpResponse(resp *http.Response) {
	out, err := httputil.DumpResponse(resp, true)
	if err == nil {
		log.Printf("%s\n\n", string(out))
	}
}

func (c *Client) checkResponse(resp *http.Response) error {
	if resp.StatusCode == 500 {
		return newErrResp(resp.Body)
	}

	return nil
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//
package main

import (
	"errors"
	"github.com/gorilla/websocket"
	"strings"
	"time"
)

// packet is an extensible object type transmitted via websocket as JSON.
type packet struct {
	Type string
	Data map[string]string
}

// newPacket returns an initialized packet with Type set to t
func newPacket(t string) (pack packet) {
	pack.Data = make(map[string]string)
	pack.Type = t
	return
}

// client is an extensible type representing a single websocket client.
type client struct {
	ws            *websocket.Conn
	user          user
	path, address string
}

// recieve reads a single message and returns it.
func (c *client) recieve() (b []byte, e error) {
	t, m, e := c.ws.ReadMessage()
	if t == 1 {
		b = m
	}
	return
}

// listener listens for incoming packets and passes them to the respective handlers.
func (c *client) listener() (e error) {
	for {
		b, e := c.recieve()
		if e != nil {
			return e
		}
		args := getArgs(b)
		if len(args) > 0 && len(args[0]) > 0 {
			if cmd, exists := cmdMap[strings.ToLower(args[0])]; exists {
				e = cmd.Handler(c, args)
			} else {
				e = c.appendMsg("#msg-list", args[0]+": command not found ")
			}
		}
		time.Sleep(time.Second)
	}
	return
}

// appendMsg appends a msg (div.msg) element to selector.
func (c *client) appendMsg(selector, text string) (e error) {
	p := newPacket("appendElement")
	p.Data["Element"] = "div"
	p.Data["Selector"] = selector
	p.Data["Class"] = "msg"
	p.Data["Text"] = text
	p.Data["Scroll"] = "true"
	e = c.ws.WriteJSON(p)
	return
}

func (c *client) appendLink(selector, url, text string) (e error) {
	p := newPacket("appendElement")
	p.Data["Element"] = "a"
	p.Data["Selector"] = selector
	p.Data["Id"] = text
	p.Data["Class"] = "ip-link"
	p.Data["Href"] = url
	p.Data["Text"] = text
	p.Data["Target"] = "_blank"
	p.Data["Scroll"] = "true"
	p.Data["OnClick"] = "removeDecoration"
	e = c.ws.WriteJSON(p)
	return
}

func (c *client) appendBreak(selector string) (e error) {
	p := newPacket("appendElement")
	p.Data["Element"] = "br"
	p.Data["Selector"] = selector
	p.Data["Scroll"] = "true"
	e = c.ws.WriteJSON(p)
	return
}

// focus will set the window focus on selector
func (c *client) focus(selector, value string) (e error) {
	p := newPacket("focus")
	p.Data["Selector"] = selector
	p.Data["Value"] = value
	e = c.ws.WriteJSON(p)
	return
}

// exists will check if selector exists
func (c *client) exists(selector string) (bl bool) {
	p := newPacket("exists")
	p.Data["Selector"] = selector
	e := c.ws.WriteJSON(p)
	if e == nil {
		b, e := c.recieve()
		if e == nil && string(b) == "true" {
			return true
		}
	}
	return false
}

// innerHTML will set the html content of selector
func (c *client) innerHTML(selector, value string) (e error) {
	p := newPacket("innerHTML")
	p.Data["Selector"] = selector
	p.Data["Value"] = value
	e = c.ws.WriteJSON(p)
	return
}

// getHTML returns the innerHTML of selector
func (c *client) getHTML(selector string) (s string, e error) {
	if c.exists(selector) {
		p := newPacket("getHTML")
		p.Data["Selector"] = selector
		e = c.ws.WriteJSON(p)
		if e == nil {
			b, e := c.recieve()
			if e == nil {
				s = string(b)
			}
		}
	} else {
		e = errors.New("element does not exist")
	}
	return
}

// setAttribute sets the specified attribute for selector.
func (c *client) setAttribute(selector, attribute, value string) (e error) {
	p := newPacket("setAttribute")
	p.Data["Selector"] = selector
	p.Data["Attribute"] = attribute
	p.Data["Value"] = value
	e = c.ws.WriteJSON(p)
	return
}

// getAttribute returns the current value of an attribute of selector.
func (c *client) getAttribute(selector, attribute string) (s string, e error) {
	p := newPacket("getAttribute")
	p.Data["Selector"] = selector
	p.Data["Attribute"] = attribute
	e = c.ws.WriteJSON(p)
	if e == nil {
		b, e := c.recieve()
		if e == nil {
			s = string(b)
		}
	}
	return
}

// setProperty sets the specified CSS property of selector.
func (c *client) setProperty(selector, property, value string) (e error) {
	p := newPacket(property)
	p.Data["Selector"] = selector
	p.Data["Value"] = value
	e = c.ws.WriteJSON(p)
	return
}

// getProperty returns the current (computed) value for the specified CSS property of selector.
func (c *client) getProperty(selector, property string) (s string, e error) {
	p := newPacket("getProperty")
	p.Data["Selector"] = selector
	p.Data["Property"] = property
	e = c.ws.WriteJSON(p)
	if e == nil {
		b, e := c.recieve()
		if e == nil {
			s = string(b)
		}
	}
	return
}

// editable sets the editable property of the element
func (c *client) editable(selector, value string) (e error) {
	p := newPacket("editable")
	p.Data["Selector"] = selector
	p.Data["Value"] = value
	e = c.ws.WriteJSON(p)
	return
}

// prompt sends the specified text as a msg and returns user input as a string.
func (c *client) prompt(text string) (s string, e error) {
	if len(text) > 0 {
		e = c.appendMsg("#msg-list", text)
	} else {
		e = c.appendMsg("#msg-list", "Enter some input:")
	}
	b, e := c.recieve()
	if e == nil {
		s = string(b)
	}
	return
}

// promptSecure uses prompt() but changes the selector/input box type to & from password for security.
func (c *client) promptSecure(selector, text string) (s string, e error) {
	attr, e := c.getAttribute(selector, "type")
	if e == nil {
		defer c.setAttribute(selector, "type", attr)
		e = c.setAttribute(selector, "type", "password")
		if e == nil {
			s, e = c.prompt(text)
		}
	}
	return
}
<commit_msg>Minor code re-arrangement.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//
package main

import (
	"errors"
	"github.com/gorilla/websocket"
	"strings"
	"time"
)

// packet is an extensible object type transmitted via websocket as JSON.
type packet struct {
	Type string
	Data map[string]string
}

// newPacket returns an initialized packet with Type set to t
func newPacket(t string) (pack packet) {
	pack.Data = make(map[string]string)
	pack.Type = t
	return
}

// client is an extensible type representing a single websocket client.
type client struct {
	ws            *websocket.Conn
	user          user
	path, address string
}

// recieve reads a single message and returns it.
func (c *client) recieve() (b []byte, e error) {
	t, m, e := c.ws.ReadMessage()
	if t == 1 {
		b = m
	}
	return
}

// listener listens for incoming packets and passes them to the respective handlers.
func (c *client) listener() (e error) {
	for {
		b, e := c.recieve()
		if e != nil {
			return e
		}
		args := getArgs(b)
		if len(args) > 0 && len(args[0]) > 0 {
			if cmd, exists := cmdMap[strings.ToLower(args[0])]; exists {
				e = cmd.Handler(c, args)
			} else {
				e = c.appendMsg("#msg-list", args[0]+": command not found ")
			}
		}
		time.Sleep(time.Second)
	}
	return
}

// prompt sends the specified text as a msg and returns user input as a string.
func (c *client) prompt(text string) (s string, e error) {
	if len(text) > 0 {
		e = c.appendMsg("#msg-list", text)
	} else {
		e = c.appendMsg("#msg-list", "Enter some input:")
	}
	b, e := c.recieve()
	if e == nil {
		s = string(b)
	}
	return
}

// promptSecure uses prompt() but changes the selector/input box type to & from password for security.
func (c *client) promptSecure(selector, text string) (s string, e error) {
	attr, e := c.getAttribute(selector, "type")
	if e == nil {
		defer c.setAttribute(selector, "type", attr)
		e = c.setAttribute(selector, "type", "password")
		if e == nil {
			s, e = c.prompt(text)
		}
	}
	return
}

// appendMsg appends a msg (div.msg) element to selector.
func (c *client) appendMsg(selector, text string) (e error) {
	p := newPacket("appendElement")
	p.Data["Element"] = "div"
	p.Data["Selector"] = selector
	p.Data["Class"] = "msg"
	p.Data["Text"] = text
	p.Data["Scroll"] = "true"
	e = c.ws.WriteJSON(p)
	return
}

func (c *client) appendLink(selector, url, text string) (e error) {
	p := newPacket("appendElement")
	p.Data["Element"] = "a"
	p.Data["Selector"] = selector
	p.Data["Id"] = text
	p.Data["Class"] = "ip-link"
	p.Data["Href"] = url
	p.Data["Text"] = text
	p.Data["Target"] = "_blank"
	p.Data["Scroll"] = "true"
	p.Data["OnClick"] = "removeDecoration"
	e = c.ws.WriteJSON(p)
	return
}

func (c *client) appendBreak(selector string) (e error) {
	p := newPacket("appendElement")
	p.Data["Element"] = "br"
	p.Data["Selector"] = selector
	p.Data["Scroll"] = "true"
	e = c.ws.WriteJSON(p)
	return
}

// focus will set the window focus on selector
func (c *client) focus(selector, value string) (e error) {
	p := newPacket("focus")
	p.Data["Selector"] = selector
	p.Data["Value"] = value
	e = c.ws.WriteJSON(p)
	return
}

// exists will check if selector exists
func (c *client) exists(selector string) (bl bool) {
	p := newPacket("exists")
	p.Data["Selector"] = selector
	e := c.ws.WriteJSON(p)
	if e == nil {
		b, e := c.recieve()
		if e == nil && string(b) == "true" {
			return true
		}
	}
	return false
}

// innerHTML will set the html content of selector
func (c *client) innerHTML(selector, value string) (e error) {
	p := newPacket("innerHTML")
	p.Data["Selector"] = selector
	p.Data["Value"] = value
	e = c.ws.WriteJSON(p)
	return
}

// getHTML returns the innerHTML of selector
func (c *client) getHTML(selector string) (s string, e error) {
	if c.exists(selector) {
		p := newPacket("getHTML")
		p.Data["Selector"] = selector
		e = c.ws.WriteJSON(p)
		if e == nil {
			b, e := c.recieve()
			if e == nil {
				s = string(b)
			}
		}
	} else {
		e = errors.New("element does not exist")
	}
	return
}

// setAttribute sets the specified attribute for selector.
func (c *client) setAttribute(selector, attribute, value string) (e error) {
	p := newPacket("setAttribute")
	p.Data["Selector"] = selector
	p.Data["Attribute"] = attribute
	p.Data["Value"] = value
	e = c.ws.WriteJSON(p)
	return
}

// getAttribute returns the current value of an attribute of selector.
func (c *client) getAttribute(selector, attribute string) (s string, e error) {
	p := newPacket("getAttribute")
	p.Data["Selector"] = selector
	p.Data["Attribute"] = attribute
	e = c.ws.WriteJSON(p)
	if e == nil {
		b, e := c.recieve()
		if e == nil {
			s = string(b)
		}
	}
	return
}

// setProperty sets the specified CSS property of selector.
func (c *client) setProperty(selector, property, value string) (e error) {
	p := newPacket(property)
	p.Data["Selector"] = selector
	p.Data["Value"] = value
	e = c.ws.WriteJSON(p)
	return
}

// getProperty returns the current (computed) value for the specified CSS property of selector.
func (c *client) getProperty(selector, property string) (s string, e error) {
	p := newPacket("getProperty")
	p.Data["Selector"] = selector
	p.Data["Property"] = property
	e = c.ws.WriteJSON(p)
	if e == nil {
		b, e := c.recieve()
		if e == nil {
			s = string(b)
		}
	}
	return
}

// editable sets the editable property of the element
func (c *client) editable(selector, value string) (e error) {
	p := newPacket("editable")
	p.Data["Selector"] = selector
	p.Data["Value"] = value
	e = c.ws.WriteJSON(p)
	return
}
<|endoftext|>


<commit_before>package rdio

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	RDIO_API_ENDPOINT   = "http://api.rdio.com/1/"
	RDIO_OAUTH_ENDPOINT = "http://api.rdio.com/oauth/"
)

type Client struct {
	ConsumerKey    string
	ConsumerSecret string
	Token          string
	TokenSecret    string
	httpClient     *http.Client
}

func (c *Client) Call(method string, params url.Values) (interface{}, error) {
	params["method"] = []string{method}
	body, err := c.SignedPost(RDIO_API_ENDPOINT, params)
	if err != nil {
		return nil, err
	}

	// parse into json
	var f interface{}
	err = json.Unmarshal(body, &f)
	if err != nil {
		return nil, err
	}

	return f, nil
}

func (c *Client) SignedPost(postUrl string, params url.Values) ([]byte, error) {
	// Sign the params
	auth := c.Sign(postUrl, params)
	fmt.Println(auth)

	// Make call
	if c.httpClient == nil {
		c.httpClient = &http.Client{}
	}

	req, err := http.NewRequest("POST", postUrl, strings.NewReader(params.Encode()))
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-type", "application/x-www-form-urlencoded")
	req.Header.Add("Authorization", auth)
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}

	// Make sure we close the body stream no matter what
	defer resp.Body.Close()

	// Read body
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	fmt.Println(resp.Header)
	str := string(body)
	fmt.Println(str)

	// Check status code
	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("Unknown status code: %d", resp.StatusCode)
	case 400:
		return nil, errors.New("Bad Request")
	case 403:
		return nil, errors.New("Developer Inactive")
	case 200:

	}

	// Return
	return body, nil
}

func (c *Client) Sign(signUrl string, params url.Values) string {
	params["oauth_version"] = []string{"1.0"}
	params["oauth_timestamp"] = []string{strconv.FormatInt(time.Now().Unix(), 10)}
	params["oauth_nonce"] = []string{strconv.FormatInt(rand.Int63(), 10)}
	params["oauth_signature_method"] = []string{"HMAC-SHA1"}
	params["oauth_consumer_key"] = []string{c.ConsumerKey}

	// The consumer secret is the first half of the HMAC-SHA1 key
	hmacKey := c.ConsumerSecret + "&"

	if c.Token != "" {
		// Include a token in params
		params["oauth_token"] = []string{c.Token}
		// and the token secret in the HMAC-SHA1 key
		hmacKey += c.TokenSecret
	}

	// sort the params by key
	var keys []string
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	sorted := url.Values{}
	for _, k := range keys {
		sorted.Add(k, params.Get(k))
	}

	// Build the signature base string
	signatureBaseString := []byte("POST&" + url.QueryEscape(signUrl) + "&" + sorted.Encode())

	// Calculate HMAC-SHA1
	mac := hmac.New(sha1.New, []byte(hmacKey))
	mac.Write(signatureBaseString)
	oauthSignature := base64.URLEncoding.EncodeToString(mac.Sum(nil))

	// Build the Authorization header
	authorizationParams := url.Values{}
	authorizationParams.Add("oauth_signature", `"`+oauthSignature+`"`)

	// List of params that must be included in the header, if present
	for _, k := range keys {
		switch k {
		case "oauth_version",
			"oauth_timestamp",
			"oauth_nonce",
			"oauth_signature_method",
			"oauth_signature",
			"oauth_consumer_key",
			"oauth_token":

			authorizationParams.Add(k, `"`+params.Get(k)+`"`)
		}
	}

	return "OAuth " + strings.Replace(strings.Replace(authorizationParams.Encode(), "&", ", ", -1), "%22", `"`, -1)
}

func (c *Client) StartAuth() ([]byte, error) {
	params := url.Values{
		"oauth_callback": []string{"oob"},
	}

	body, err := c.SignedPost(RDIO_OAUTH_ENDPOINT+"request_token/", params)
	if err != nil {
		return nil, err
	}

	return body, nil
}

func (c *Client) CompleteAuth(verifier string) ([]byte, error) {
	params := url.Values{
		"oauth_verifier": []string{"verifier"},
	}

	body, err := c.SignedPost(RDIO_OAUTH_ENDPOINT+"access_token/", params)
	if err != nil {
		return nil, err
	}

	return body, nil
}
<commit_msg>Looks like maybe the signature isn't supposed to be url encoded<commit_after>package rdio

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	RDIO_API_ENDPOINT   = "http://api.rdio.com/1/"
	RDIO_OAUTH_ENDPOINT = "http://api.rdio.com/oauth/"
)

type Client struct {
	ConsumerKey    string
	ConsumerSecret string
	Token          string
	TokenSecret    string
	httpClient     *http.Client
}

func (c *Client) Call(method string, params url.Values) (interface{}, error) {
	params["method"] = []string{method}
	body, err := c.SignedPost(RDIO_API_ENDPOINT, params)
	if err != nil {
		return nil, err
	}

	// parse into json
	var f interface{}
	err = json.Unmarshal(body, &f)
	if err != nil {
		return nil, err
	}

	return f, nil
}

func (c *Client) SignedPost(postUrl string, params url.Values) ([]byte, error) {
	// Sign the params
	auth := c.Sign(postUrl, params)
	fmt.Println(auth)

	// Make call
	if c.httpClient == nil {
		c.httpClient = &http.Client{}
	}

	req, err := http.NewRequest("POST", postUrl, strings.NewReader(params.Encode()))
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-type", "application/x-www-form-urlencoded")
	req.Header.Add("Authorization", auth)
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}

	// Make sure we close the body stream no matter what
	defer resp.Body.Close()

	// Read body
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	fmt.Println(resp.Header)
	str := string(body)
	fmt.Println(str)

	// Check status code
	switch resp.StatusCode {
	default:
		return nil, fmt.Errorf("Unknown status code: %d", resp.StatusCode)
	case 400:
		return nil, errors.New("Bad Request")
	case 403:
		return nil, errors.New("Developer Inactive")
	case 200:

	}

	// Return
	return body, nil
}

func (c *Client) Sign(signUrl string, params url.Values) string {
	params["oauth_version"] = []string{"1.0"}
	params["oauth_timestamp"] = []string{strconv.FormatInt(time.Now().Unix(), 10)}
	params["oauth_nonce"] = []string{strconv.FormatInt(rand.Int63(), 10)}
	params["oauth_signature_method"] = []string{"HMAC-SHA1"}
	params["oauth_consumer_key"] = []string{c.ConsumerKey}

	// The consumer secret is the first half of the HMAC-SHA1 key
	hmacKey := c.ConsumerSecret + "&"

	if c.Token != "" {
		// Include a token in params
		params["oauth_token"] = []string{c.Token}
		// and the token secret in the HMAC-SHA1 key
		hmacKey += c.TokenSecret
	}

	// sort the params by key
	var keys []string
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	sorted := url.Values{}
	for _, k := range keys {
		sorted.Add(k, params.Get(k))
	}

	// Build the signature base string
	signatureBaseString := []byte("POST&" + url.QueryEscape(signUrl) + "&" + sorted.Encode())

	// Calculate HMAC-SHA1
	mac := hmac.New(sha1.New, []byte(hmacKey))
	mac.Write(signatureBaseString)
	oauthSignature := base64.URLEncoding.EncodeToString(mac.Sum(nil))

	// Build the Authorization header
	authorizationParams := url.Values{}
	//authorizationParams.Add("oauth_signature", `"`+oauthSignature+`"`)

	// List of params that must be included in the header, if present
	for _, k := range keys {
		switch k {
		case "oauth_version",
			"oauth_timestamp",
			"oauth_nonce",
			"oauth_signature_method",
			//"oauth_signature",
			"oauth_consumer_key",
			"oauth_token":

			authorizationParams.Add(k, `"`+params.Get(k)+`"`)
		}
	}

	return "OAuth oauth_signature=\"" + oauthSignature + "\", " + strings.Replace(strings.Replace(authorizationParams.Encode(), "&", ", ", -1), "%22", `"`, -1)
}

func (c *Client) StartAuth() ([]byte, error) {
	params := url.Values{
		"oauth_callback": []string{"oob"},
	}

	body, err := c.SignedPost(RDIO_OAUTH_ENDPOINT+"request_token/", params)
	if err != nil {
		return nil, err
	}

	return body, nil
}

func (c *Client) CompleteAuth(verifier string) ([]byte, error) {
	params := url.Values{
		"oauth_verifier": []string{"verifier"},
	}

	body, err := c.SignedPost(RDIO_OAUTH_ENDPOINT+"access_token/", params)
	if err != nil {
		return nil, err
	}

	return body, nil
}
<|endoftext|>