<commit_before>package slack

import (
	"errors"
	"net/url"
)

// UserProfile contains all the information details of a given user
type UserProfile struct {
	FirstName          string `json:"first_name"`
	LastName           string `json:"last_name"`
	RealName           string `json:"real_name"`
	RealNameNormalized string `json:"real_name_normalized"`
	Email              string `json:"email"`
	Skype              string `json:"skype"`
	Phone              string `json:"phone"`
	Image24            string `json:"image_24"`
	Image32            string `json:"image_32"`
	Image48            string `json:"image_48"`
	Image72            string `json:"image_72"`
	Image192           string `json:"image_192"`
	ImageOriginal      string `json:"image_original"`
	Title              string `json:"title"`
}

// User contains all the information of a user
type User struct {
	Id                string      `json:"id"`
	Name              string      `json:"name"`
	Deleted           bool        `json:"deleted"`
	Color             string      `json:"color"`
	Profile           UserProfile `json:"profile"`
	IsBot             bool        `json:"is_bot"`
	IsAdmin           bool        `json:"is_admin"`
	IsOwner           bool        `json:"is_owner"`
	IsPrimaryOwner    bool        `json:"is_primary_owner"`
	IsRestricted      bool        `json:"is_restricted"`
	IsUltraRestricted bool        `json:"is_ultra_restricted"`
	HasFiles          bool        `json:"has_files"`
	Presence          string      `json:"presence"`
}

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

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

func userRequest(path string, values url.Values, debug bool) (*userResponseFull, error) {
	response := &userResponseFull{}
	err := parseResponse(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// GetUserPresence will retrieve the current presence status of given user.
func (api *Slack) GetUserPresence(userId string) (*UserPresence, error) {
	values := url.Values{
		"token": {api.config.token},
		"user":  {userId},
	}
	response, err := userRequest("users.getPresence", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.UserPresence, nil
}

// GetUserInfo will retrive the complete user information
func (api *Slack) GetUserInfo(userId string) (*User, error) {
	values := url.Values{
		"token": {api.config.token},
		"user":  {userId},
	}
	response, err := userRequest("users.info", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.User, nil
}

// GetUsers returns the list of users (with their detailed information)
func (api *Slack) GetUsers() ([]User, error) {
	values := url.Values{
		"token": {api.config.token},
	}
	response, err := userRequest("users.list", values, api.debug)
	if err != nil {
		return nil, err
	}
	return response.Members, nil
}

// SetUserAsActive marks the currently authenticated user as active
func (api *Slack) SetUserAsActive() error {
	values := url.Values{
		"token": {api.config.token},
	}
	_, err := userRequest("users.setActive", values, api.debug)
	if err != nil {
		return err
	}
	return nil
}

// SetUserPresence changes the currently authenticated user presence
func (api *Slack) SetUserPresence(presence string) error {
	values := url.Values{
		"token":    {api.config.token},
		"presence": {presence},
	}
	_, err := userRequest("users.setPresence", values, api.debug)
	if err != nil {
		return err
	}
	return nil

}
<commit_msg>Add has_2fa support<commit_after>package slack

import (
	"errors"
	"net/url"
)

// UserProfile contains all the information details of a given user
type UserProfile struct {
	FirstName          string `json:"first_name"`
	LastName           string `json:"last_name"`
	RealName           string `json:"real_name"`
	RealNameNormalized string `json:"real_name_normalized"`
	Email              string `json:"email"`
	Skype              string `json:"skype"`
	Phone              string `json:"phone"`
	Image24            string `json:"image_24"`
	Image32            string `json:"image_32"`
	Image48            string `json:"image_48"`
	Image72            string `json:"image_72"`
	Image192           string `json:"image_192"`
	ImageOriginal      string `json:"image_original"`
	Title              string `json:"title"`
}

// User contains all the information of a user
type User struct {
	Id                string      `json:"id"`
	Name              string      `json:"name"`
	Deleted           bool        `json:"deleted"`
	Color             string      `json:"color"`
	Profile           UserProfile `json:"profile"`
	IsBot             bool        `json:"is_bot"`
	IsAdmin           bool        `json:"is_admin"`
	IsOwner           bool        `json:"is_owner"`
	IsPrimaryOwner    bool        `json:"is_primary_owner"`
	IsRestricted      bool        `json:"is_restricted"`
	IsUltraRestricted bool        `json:"is_ultra_restricted"`
	Has2FA            bool        `json:"has_2fa"`
	HasFiles          bool        `json:"has_files"`
	Presence          string      `json:"presence"`
}

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

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

func userRequest(path string, values url.Values, debug bool) (*userResponseFull, error) {
	response := &userResponseFull{}
	err := parseResponse(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// GetUserPresence will retrieve the current presence status of given user.
func (api *Slack) GetUserPresence(userId string) (*UserPresence, error) {
	values := url.Values{
		"token": {api.config.token},
		"user":  {userId},
	}
	response, err := userRequest("users.getPresence", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.UserPresence, nil
}

// GetUserInfo will retrive the complete user information
func (api *Slack) GetUserInfo(userId string) (*User, error) {
	values := url.Values{
		"token": {api.config.token},
		"user":  {userId},
	}
	response, err := userRequest("users.info", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.User, nil
}

// GetUsers returns the list of users (with their detailed information)
func (api *Slack) GetUsers() ([]User, error) {
	values := url.Values{
		"token": {api.config.token},
	}
	response, err := userRequest("users.list", values, api.debug)
	if err != nil {
		return nil, err
	}
	return response.Members, nil
}

// SetUserAsActive marks the currently authenticated user as active
func (api *Slack) SetUserAsActive() error {
	values := url.Values{
		"token": {api.config.token},
	}
	_, err := userRequest("users.setActive", values, api.debug)
	if err != nil {
		return err
	}
	return nil
}

// SetUserPresence changes the currently authenticated user presence
func (api *Slack) SetUserPresence(presence string) error {
	values := url.Values{
		"token":    {api.config.token},
		"presence": {presence},
	}
	_, err := userRequest("users.setPresence", values, api.debug)
	if err != nil {
		return err
	}
	return nil

}
<|endoftext|>


<commit_before>package collectors

import (
	"os"

	"github.com/StackExchange/scollector/opentsdb"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: puppet_disabled_func_linux})
}

func puppet_disabled_func_linux() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	disabled := 0
	filename := "/var/lib/puppet/state/agent_disabled.lock"
	if _, err := os.Stat(filename); !os.IsNotExist(err) {
		disabled = 1
	}
	Add(&md, "puppet.disabled", disabled, nil)
	return md
}
<commit_msg>cmd/scollector: Don't send puppet enabled metric unless Puppet seems to be installed<commit_after>package collectors

import (
	"os"
	"time"

	"github.com/StackExchange/scollector/opentsdb"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: puppet_disabled_func_linux, init: puppetInit})
}

var puppetExists bool

const puppetPath = "/var/lib/puppet/"
const puppetDisabled = "/var/lib/puppet/state/agent_disabled.lock"

func puppetInit() {
	update := func() {
		if _, err := os.Stat(puppetPath); err == nil {
			puppetExists = true
		}
	}
	update()
	go func() {
		for _ = range time.Tick(time.Minute * 5) {
			update()
		}
	}()
}

func puppet_disabled_func_linux() opentsdb.MultiDataPoint {
	if !puppetExists {
		return nil
	}
	var md opentsdb.MultiDataPoint
	disabled := 0
	if _, err := os.Stat(puppetDisabled); !os.IsNotExist(err) {
		disabled = 1
	}
	Add(&md, "puppet.disabled", disabled, nil)
	return md
}
<|endoftext|>


<commit_before>package options

import (
	"shadowss/pkg/config"

	"github.com/golang/glog"
	"github.com/spf13/pflag"
)

type ServerOption struct {
	ConfigFile         string
	CpuCoreNum         int
	EnableUDPRelay     bool
	URL                string
	MaxTCPConnPerPort  int
	APIProxyListenPort int
}

func NewServerOption() *ServerOption {
	return &ServerOption{
		ConfigFile:     string(""),
		CpuCoreNum:     1,
		EnableUDPRelay: false,
	}
}

func (s *ServerOption) LoadConfigFile() error {
	glog.V(5).Infof("Parse %s file\r\n", s.ConfigFile)
	err := config.ServerCfg.Parse(s.ConfigFile)
	if err != nil {
		glog.Errorf("error reading %s: %v\n", s.ConfigFile, err)
		return err
	}

	return nil
}

func (s *ServerOption) AddFlags(fs *pflag.FlagSet) {

	fs.StringVar(&s.ConfigFile, "config-file", s.ConfigFile, ""+
		"specify a configure file for server run. ")

	fs.IntVar(&s.CpuCoreNum, "cpu-core-num", s.CpuCoreNum, ""+
		"specify how many cpu core will be alloc for program")

	fs.BoolVar(&s.EnableUDPRelay, "enable-udp-relay", s.EnableUDPRelay, ""+
		"enable udp relay")

	fs.StringVar(&s.URL, "apiserver-url", s.URL, ""+
		"specify a api server url. ")

	fs.IntVar(&s.MaxTCPConnPerPort, "max-tcp-conn-per-port", 300, ""+
		"specify how many tcp connection per port")

	fs.IntVar(&s.APIProxyListenPort, "max-tcp-conn-per-port", 48888, ""+
		"specify a port for api proxy")

}
<commit_msg>fix options panic bug<commit_after>package options

import (
	"shadowss/pkg/config"

	"github.com/golang/glog"
	"github.com/spf13/pflag"
)

type ServerOption struct {
	ConfigFile         string
	CpuCoreNum         int
	EnableUDPRelay     bool
	URL                string
	MaxTCPConnPerPort  int
	APIProxyListenPort int
}

func NewServerOption() *ServerOption {
	return &ServerOption{
		ConfigFile:     string(""),
		CpuCoreNum:     1,
		EnableUDPRelay: false,
	}
}

func (s *ServerOption) LoadConfigFile() error {
	glog.V(5).Infof("Parse %s file\r\n", s.ConfigFile)
	err := config.ServerCfg.Parse(s.ConfigFile)
	if err != nil {
		glog.Errorf("error reading %s: %v\n", s.ConfigFile, err)
		return err
	}

	return nil
}

func (s *ServerOption) AddFlags(fs *pflag.FlagSet) {

	fs.StringVar(&s.ConfigFile, "config-file", s.ConfigFile, ""+
		"specify a configure file for server run. ")

	fs.IntVar(&s.CpuCoreNum, "cpu-core-num", s.CpuCoreNum, ""+
		"specify how many cpu core will be alloc for program")

	fs.BoolVar(&s.EnableUDPRelay, "enable-udp-relay", s.EnableUDPRelay, ""+
		"enable udp relay")

	fs.StringVar(&s.URL, "apiserver-url", s.URL, ""+
		"specify a api server url. ")

	fs.IntVar(&s.MaxTCPConnPerPort, "max-tcp-conn-per-port", 300, ""+
		"specify how many tcp connection per port")

	fs.IntVar(&s.APIProxyListenPort, "api-proxy-port", 48888, ""+
		"specify a port for api proxy")

}
<|endoftext|>


<commit_before>package jiraui

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

	"github.com/Netflix-Skunkworks/go-jira"
	ui "github.com/gizak/termui"
	"github.com/mitchellh/go-wordwrap"
	"gopkg.in/coryb/yaml.v2"
)

func countLabelsFromQuery(query string) map[string]int {
	data, _ := runJiraQuery(query)
	return countLabelsFromQueryData(data)
}

func countLabelsFromQueryData(data interface{}) map[string]int {
	counts := make(map[string]int)
	issues := data.(map[string]interface{})["issues"].([]interface{})
	for _, issue := range issues {
		issueLabels := issue.(map[string]interface{})["fields"].(map[string]interface{})["labels"]
		labels := issueLabels.([]interface{})
		if len(labels) == 0 {
			// "NOT LABELLED" isn't a valid label, so no possible conflict here.
			counts["NOT LABELLED"] = counts["NOT LABELLED"] + 1
		} else {
			for _, v := range labels {
				label := v.(string)
				counts[label] = counts[label] + 1
			}
		}
	}
	return counts
}

func RunExternalCommand(fn func() error) error {
	log.Debugf("ShellOut() called with %q", fn)
	deregisterEventHandlers()
	ui.Clear()
	stty := exec.Command("stty", "-f", "/dev/tty", "echo", "opost")
	_ = stty.Run()
	err := fn() // magic happens
	stty = exec.Command("stty", "-f", "/dev/tty", "-echo", "-opost")
	_ = stty.Run()
	registerEventHandlers()
	if err != nil {
		return err
	}
	return nil
}

func runShell() {
	_ = RunExternalCommand(
		func() error {
			cmd := exec.Command("bash")
			cmd.Stdout, cmd.Stderr, cmd.Stdin = os.Stdout, os.Stderr, os.Stdin
			return cmd.Run()
		})
	changePage()
}

func runJiraCmdEdit(ticketId string) {
	_ = RunExternalCommand(
		func() error {
			opts := getJiraOpts()
			c := jira.New(opts)
			return c.CmdEdit(ticketId)
		})
	switch c := currentPage.(type) {
	case Refresher:
		c.Refresh()
	}
	changePage()
}

func runJiraCmdCreate(project string, summary string) {
	_ = RunExternalCommand(
		func() error {
			opts := getJiraOpts()
			opts["project"] = project
			opts["summary"] = summary
			c := jira.New(opts)
			return c.CmdCreate()
		})
	switch c := currentPage.(type) {
	case Refresher:
		c.Refresh()
	}
	changePage()
}

func runJiraCmdCommentNoEditor(ticketId string, comment string) {
	opts := getJiraOpts()
	opts["comment"] = comment
	c := jira.New(opts)
	c.CmdComment(ticketId)
}

func runJiraCmdAssign(ticketId string, user string) {
	opts := getJiraOpts()
	c := jira.New(opts)
	c.CmdAssign(ticketId, user)
}

func runJiraCmdWatch(ticketId string, watcher string, remove bool) {
	opts := getJiraOpts()
	c := jira.New(opts)
	if watcher == "" {
		watcher = opts["user"].(string)
	}
	c.CmdWatch(ticketId, watcher, remove)
}

func runJiraCmdVote(ticketId string, up bool) {
	opts := getJiraOpts()
	c := jira.New(opts)
	c.CmdVote(ticketId, up)
}

func runJiraCmdLabels(ticketId string, action string, labels []string) {
	opts := getJiraOpts()
	c := jira.New(opts)
	err := c.CmdLabels(action, ticketId, labels)
	if err != nil {
		log.Errorf("Error writing labels: %q", err)
	}
}

func runJiraCmdRank(ticketId, targetId string, order jira.RankOrder) {
	opts := getJiraOpts()
	c := jira.New(opts)
	err := c.RankIssue(ticketId, targetId, order)
	if err != nil {
		log.Errorf("Error modifying issue rank: %q", err)
	}
}

func findTicketIdInString(line string) string {
	re := regexp.MustCompile(`[A-Z]{2,12}-[0-9]{1,6}`)
	return strings.TrimSpace(re.FindString(line))
}

func runJiraQuery(query string) (interface{}, error) {
	opts := getJiraOpts()
	opts["query"] = query
	c := jira.New(opts)
	return c.FindIssues()
}

func JiraQueryAsStrings(query string, templateName string) []string {
	opts := getJiraOpts()
	opts["query"] = query
	c := jira.New(opts)
	data, _ := c.FindIssues()
	buf := new(bytes.Buffer)
	if templateName == "" {
		templateName = "jira_ui_list"
	}
	template := c.GetTemplate(templateName)
	if template == "" {
		template = default_list_template
	}
	jira.RunTemplate(template, data, buf)
	return strings.Split(strings.TrimSpace(buf.String()), "\n")
}

func FetchJiraTicket(id string) (interface{}, error) {
	opts := getJiraOpts()
	c := jira.New(opts)
	return c.ViewIssue(id)
}

func JiraTicketAsStrings(data interface{}, templateName string) []string {
	opts := getJiraOpts()
	c := jira.New(opts)
	buf := new(bytes.Buffer)
	template := c.GetTemplate(templateName)
	log.Debugf("JiraTicketsAsStrings: template = %q", template)
	if template == "" {
		template = strings.Replace(default_view_template, "ENDPOINT", opts["endpoint"].(string), 1)
	}
	jira.RunTemplate(template, data, buf)
	return strings.Split(strings.TrimSpace(buf.String()), "\n")
}

func HelpTextAsStrings(data interface{}, templateName string) []string {
	opts := getJiraOpts()
	c := jira.New(opts)
	buf := new(bytes.Buffer)
	template := c.GetTemplate(templateName)
	if template == "" {
		template = default_help_template
	}
	log.Debugf("HelpTextAsStrings: template = %q", template)
	jira.RunTemplate(template, data, buf)
	return strings.Split(strings.TrimSpace(buf.String()), "\n")
}

func WrapText(lines []string, maxWidth uint) []string {
	out := make([]string, 0)
	insideNoformatBlock := false
	insideCodeBlock := false
	for _, line := range lines {
		if matched, _ := regexp.MatchString(`^\s+\{code`, line); matched {
			insideCodeBlock = !insideCodeBlock
		} else if strings.TrimSpace(line) == "{noformat}" {
			insideNoformatBlock = !insideNoformatBlock
		}
		if maxWidth == 0 || uint(len(line)) < maxWidth || insideCodeBlock || insideNoformatBlock {
			out = append(out, line)
			continue
		}
		if matched, _ := regexp.MatchString(`^[a-z_]+:\s`, line); matched {
			// don't futz with single line field+value.
			// If they are too long, that's their fault.
			out = append(out, line)
			continue
		}
		// wrap text, but preserve indenting
		re := regexp.MustCompile(`^\s*`)
		indenting := re.FindString(line)
		wrappedLines := strings.Split(wordwrap.WrapString(line, maxWidth-uint(len(indenting))), "\n")
		indentedWrappedLines := make([]string, len(wrappedLines))
		for i, wl := range wrappedLines {
			if i == 0 {
				// first line already has the indent
				indentedWrappedLines[i] = wl
			} else {
				indentedWrappedLines[i] = indenting + wl
			}
		}
		out = append(out, indentedWrappedLines...)
	}
	return out
}

func parseYaml(file string, v map[string]interface{}) {
	if fh, err := ioutil.ReadFile(file); err == nil {
		log.Debugf("Parsing YAML file: %s", file)
		yaml.Unmarshal(fh, &v)
	}
}

func loadConfigs(opts map[string]interface{}) {
	paths := jira.FindParentPaths(".jira.d/jira-ui-config.yml")
	paths = append(jira.FindParentPaths(".jira.d/config.yml"), paths...)
	paths = append([]string{"/etc/go-jira-ui.yml", "/etc/go-jira.yml"}, paths...)

	// iterate paths in reverse
	for i := len(paths) - 1; i >= 0; i-- {
		file := paths[i]
		if _, err := os.Stat(file); err == nil {
			tmp := make(map[string]interface{})
			parseYaml(file, tmp)
			for k, v := range tmp {
				if _, ok := opts[k]; !ok {
					log.Debugf("Setting %q to %#v from %s", k, v, file)
					opts[k] = v
				}
			}
		}
	}
}

func doLogin(opts map[string]interface{}) error {
	c := jira.New(opts)
	fmt.Printf("Logging in as %s:\n", opts["user"])
	return c.CmdLogin()
}

func ensureLoggedIntoJira() error {
	homeDir := os.Getenv("HOME")
	opts := getJiraOpts()
	testSessionQuery := fmt.Sprintf("reporter = %s", opts["user"])
	if _, err := os.Stat(fmt.Sprintf("%s/.jira.d/cookies.js", homeDir)); err != nil {
		return doLogin(opts)
	} else if data, err := runJiraQuery(testSessionQuery); err != nil {
		return doLogin(opts)
	} else if val, ok := data.(map[string]interface{})["errorMessages"]; ok {
		if len(val.([]interface{})) > 0 {
			return doLogin(opts)
		}
	}
	return nil
}

func getJiraOpts() map[string]interface{} {
	user := os.Getenv("USER")
	home := os.Getenv("HOME")
	defaultQueryFields := "summary,created,updated,priority,status,reporter,assignee,labels"
	defaultSort := "priority asc, created"
	defaultMaxResults := 1000

	opts := make(map[string]interface{})
	defaults := map[string]interface{}{
		"user":        user,
		"endpoint":    os.Getenv("JIRA_ENDPOINT"),
		"queryfields": defaultQueryFields,
		"directory":   fmt.Sprintf("%s/.jira.d/templates", home),
		"sort":        defaultSort,
		"max_results": defaultMaxResults,
		"method":      "GET",
		"quiet":       true,
	}

	for k, v := range cliOpts {
		if _, ok := opts[k]; !ok {
			log.Debugf("Setting %q to %#v from cli options", k, v)
			opts[k] = v
		}
	}

	loadConfigs(opts)
	for k, v := range defaults {
		if _, ok := opts[k]; !ok {
			log.Debugf("Setting %q to %#v from defaults", k, v)
			opts[k] = v
		}
	}
	return opts
}

func lastLineDisplayed(ls *ScrollableList, firstLine int, correction int) int {
	return firstLine + ls.Height - correction
}
<commit_msg>Add an regex check for a too long ticket id<commit_after>package jiraui

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

	"github.com/Netflix-Skunkworks/go-jira"
	ui "github.com/gizak/termui"
	"github.com/mitchellh/go-wordwrap"
	"gopkg.in/coryb/yaml.v2"
)

func countLabelsFromQuery(query string) map[string]int {
	data, _ := runJiraQuery(query)
	return countLabelsFromQueryData(data)
}

func countLabelsFromQueryData(data interface{}) map[string]int {
	counts := make(map[string]int)
	issues := data.(map[string]interface{})["issues"].([]interface{})
	for _, issue := range issues {
		issueLabels := issue.(map[string]interface{})["fields"].(map[string]interface{})["labels"]
		labels := issueLabels.([]interface{})
		if len(labels) == 0 {
			// "NOT LABELLED" isn't a valid label, so no possible conflict here.
			counts["NOT LABELLED"] = counts["NOT LABELLED"] + 1
		} else {
			for _, v := range labels {
				label := v.(string)
				counts[label] = counts[label] + 1
			}
		}
	}
	return counts
}

func RunExternalCommand(fn func() error) error {
	log.Debugf("ShellOut() called with %q", fn)
	deregisterEventHandlers()
	ui.Clear()
	stty := exec.Command("stty", "-f", "/dev/tty", "echo", "opost")
	_ = stty.Run()
	err := fn() // magic happens
	stty = exec.Command("stty", "-f", "/dev/tty", "-echo", "-opost")
	_ = stty.Run()
	registerEventHandlers()
	if err != nil {
		return err
	}
	return nil
}

func runShell() {
	_ = RunExternalCommand(
		func() error {
			cmd := exec.Command("bash")
			cmd.Stdout, cmd.Stderr, cmd.Stdin = os.Stdout, os.Stderr, os.Stdin
			return cmd.Run()
		})
	changePage()
}

func runJiraCmdEdit(ticketId string) {
	_ = RunExternalCommand(
		func() error {
			opts := getJiraOpts()
			c := jira.New(opts)
			return c.CmdEdit(ticketId)
		})
	switch c := currentPage.(type) {
	case Refresher:
		c.Refresh()
	}
	changePage()
}

func runJiraCmdCreate(project string, summary string) {
	_ = RunExternalCommand(
		func() error {
			opts := getJiraOpts()
			opts["project"] = project
			opts["summary"] = summary
			c := jira.New(opts)
			return c.CmdCreate()
		})
	switch c := currentPage.(type) {
	case Refresher:
		c.Refresh()
	}
	changePage()
}

func runJiraCmdCommentNoEditor(ticketId string, comment string) {
	opts := getJiraOpts()
	opts["comment"] = comment
	c := jira.New(opts)
	c.CmdComment(ticketId)
}

func runJiraCmdAssign(ticketId string, user string) {
	opts := getJiraOpts()
	c := jira.New(opts)
	c.CmdAssign(ticketId, user)
}

func runJiraCmdWatch(ticketId string, watcher string, remove bool) {
	opts := getJiraOpts()
	c := jira.New(opts)
	if watcher == "" {
		watcher = opts["user"].(string)
	}
	c.CmdWatch(ticketId, watcher, remove)
}

func runJiraCmdVote(ticketId string, up bool) {
	opts := getJiraOpts()
	c := jira.New(opts)
	c.CmdVote(ticketId, up)
}

func runJiraCmdLabels(ticketId string, action string, labels []string) {
	opts := getJiraOpts()
	c := jira.New(opts)
	err := c.CmdLabels(action, ticketId, labels)
	if err != nil {
		log.Errorf("Error writing labels: %q", err)
	}
}

func runJiraCmdRank(ticketId, targetId string, order jira.RankOrder) {
	opts := getJiraOpts()
	c := jira.New(opts)
	err := c.RankIssue(ticketId, targetId, order)
	if err != nil {
		log.Errorf("Error modifying issue rank: %q", err)
	}
}

func findTicketIdInString(line string) string {
	re := regexp.MustCompile(`[A-Z]{2,12}-[0-9]{1,6}`)
	re_too_long := regexp.MustCompile(`[A-Z]{13}-[0-9]{1,6}`)

	if re_too_long.MatchString(line) {
		return ""
	}

	return strings.TrimSpace(re.FindString(line))
}

func runJiraQuery(query string) (interface{}, error) {
	opts := getJiraOpts()
	opts["query"] = query
	c := jira.New(opts)
	return c.FindIssues()
}

func JiraQueryAsStrings(query string, templateName string) []string {
	opts := getJiraOpts()
	opts["query"] = query
	c := jira.New(opts)
	data, _ := c.FindIssues()
	buf := new(bytes.Buffer)
	if templateName == "" {
		templateName = "jira_ui_list"
	}
	template := c.GetTemplate(templateName)
	if template == "" {
		template = default_list_template
	}
	jira.RunTemplate(template, data, buf)
	return strings.Split(strings.TrimSpace(buf.String()), "\n")
}

func FetchJiraTicket(id string) (interface{}, error) {
	opts := getJiraOpts()
	c := jira.New(opts)
	return c.ViewIssue(id)
}

func JiraTicketAsStrings(data interface{}, templateName string) []string {
	opts := getJiraOpts()
	c := jira.New(opts)
	buf := new(bytes.Buffer)
	template := c.GetTemplate(templateName)
	log.Debugf("JiraTicketsAsStrings: template = %q", template)
	if template == "" {
		template = strings.Replace(default_view_template, "ENDPOINT", opts["endpoint"].(string), 1)
	}
	jira.RunTemplate(template, data, buf)
	return strings.Split(strings.TrimSpace(buf.String()), "\n")
}

func HelpTextAsStrings(data interface{}, templateName string) []string {
	opts := getJiraOpts()
	c := jira.New(opts)
	buf := new(bytes.Buffer)
	template := c.GetTemplate(templateName)
	if template == "" {
		template = default_help_template
	}
	log.Debugf("HelpTextAsStrings: template = %q", template)
	jira.RunTemplate(template, data, buf)
	return strings.Split(strings.TrimSpace(buf.String()), "\n")
}

func WrapText(lines []string, maxWidth uint) []string {
	out := make([]string, 0)
	insideNoformatBlock := false
	insideCodeBlock := false
	for _, line := range lines {
		if matched, _ := regexp.MatchString(`^\s+\{code`, line); matched {
			insideCodeBlock = !insideCodeBlock
		} else if strings.TrimSpace(line) == "{noformat}" {
			insideNoformatBlock = !insideNoformatBlock
		}
		if maxWidth == 0 || uint(len(line)) < maxWidth || insideCodeBlock || insideNoformatBlock {
			out = append(out, line)
			continue
		}
		if matched, _ := regexp.MatchString(`^[a-z_]+:\s`, line); matched {
			// don't futz with single line field+value.
			// If they are too long, that's their fault.
			out = append(out, line)
			continue
		}
		// wrap text, but preserve indenting
		re := regexp.MustCompile(`^\s*`)
		indenting := re.FindString(line)
		wrappedLines := strings.Split(wordwrap.WrapString(line, maxWidth-uint(len(indenting))), "\n")
		indentedWrappedLines := make([]string, len(wrappedLines))
		for i, wl := range wrappedLines {
			if i == 0 {
				// first line already has the indent
				indentedWrappedLines[i] = wl
			} else {
				indentedWrappedLines[i] = indenting + wl
			}
		}
		out = append(out, indentedWrappedLines...)
	}
	return out
}

func parseYaml(file string, v map[string]interface{}) {
	if fh, err := ioutil.ReadFile(file); err == nil {
		log.Debugf("Parsing YAML file: %s", file)
		yaml.Unmarshal(fh, &v)
	}
}

func loadConfigs(opts map[string]interface{}) {
	paths := jira.FindParentPaths(".jira.d/jira-ui-config.yml")
	paths = append(jira.FindParentPaths(".jira.d/config.yml"), paths...)
	paths = append([]string{"/etc/go-jira-ui.yml", "/etc/go-jira.yml"}, paths...)

	// iterate paths in reverse
	for i := len(paths) - 1; i >= 0; i-- {
		file := paths[i]
		if _, err := os.Stat(file); err == nil {
			tmp := make(map[string]interface{})
			parseYaml(file, tmp)
			for k, v := range tmp {
				if _, ok := opts[k]; !ok {
					log.Debugf("Setting %q to %#v from %s", k, v, file)
					opts[k] = v
				}
			}
		}
	}
}

func doLogin(opts map[string]interface{}) error {
	c := jira.New(opts)
	fmt.Printf("Logging in as %s:\n", opts["user"])
	return c.CmdLogin()
}

func ensureLoggedIntoJira() error {
	homeDir := os.Getenv("HOME")
	opts := getJiraOpts()
	testSessionQuery := fmt.Sprintf("reporter = %s", opts["user"])
	if _, err := os.Stat(fmt.Sprintf("%s/.jira.d/cookies.js", homeDir)); err != nil {
		return doLogin(opts)
	} else if data, err := runJiraQuery(testSessionQuery); err != nil {
		return doLogin(opts)
	} else if val, ok := data.(map[string]interface{})["errorMessages"]; ok {
		if len(val.([]interface{})) > 0 {
			return doLogin(opts)
		}
	}
	return nil
}

func getJiraOpts() map[string]interface{} {
	user := os.Getenv("USER")
	home := os.Getenv("HOME")
	defaultQueryFields := "summary,created,updated,priority,status,reporter,assignee,labels"
	defaultSort := "priority asc, created"
	defaultMaxResults := 1000

	opts := make(map[string]interface{})
	defaults := map[string]interface{}{
		"user":        user,
		"endpoint":    os.Getenv("JIRA_ENDPOINT"),
		"queryfields": defaultQueryFields,
		"directory":   fmt.Sprintf("%s/.jira.d/templates", home),
		"sort":        defaultSort,
		"max_results": defaultMaxResults,
		"method":      "GET",
		"quiet":       true,
	}

	for k, v := range cliOpts {
		if _, ok := opts[k]; !ok {
			log.Debugf("Setting %q to %#v from cli options", k, v)
			opts[k] = v
		}
	}

	loadConfigs(opts)
	for k, v := range defaults {
		if _, ok := opts[k]; !ok {
			log.Debugf("Setting %q to %#v from defaults", k, v)
			opts[k] = v
		}
	}
	return opts
}

func lastLineDisplayed(ls *ScrollableList, firstLine int, correction int) int {
	return firstLine + ls.Height - correction
}
<|endoftext|>


<commit_before>//
// Copyright © 2011-2017 Guy M. Allard
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package stompngo

import (
	"fmt"
	"io"
	"strings"
)

/*
	Encode a string per STOMP 1.1+ specifications.
*/
func encode(s string) string {
	r := s
	for _, tr := range codecValues {
		if strings.Index(r, tr.decoded) >= 0 {
			r = strings.Replace(r, tr.decoded, tr.encoded, -1)
		}
	}
	return r
}

/*
	Decode a string per STOMP 1.1+ specifications.
*/
func decode(s string) string {
	r := s
	for _, tr := range codecValues {
		if strings.Index(r, tr.encoded) >= 0 {
			r = strings.Replace(r, tr.encoded, tr.decoded, -1)
		}
	}
	return r
}

/*
	A network helper.  Read from the wire until a 0x00 byte is encountered.
*/
func readUntilNul(c *Connection) ([]uint8, error) {
	c.setReadDeadline()
	b, e := c.rdr.ReadBytes(0)
	if c.checkReadError(e) != nil {
		return b, e
	}
	if len(b) == 1 {
		b = NULLBUFF
	} else {
		b = b[0 : len(b)-1]
	}
	return b, e
}

/*
	A network helper.  Read a full message body with a known length that is
	> 0.  Then read the trailing 'null' byte expected for STOMP frames.
*/
func readBody(c *Connection, l int) ([]uint8, error) {
	b := make([]byte, l)
	c.setReadDeadline()
	n, e := io.ReadFull(c.rdr, b)
	if n < l { // Short read, e is ErrUnexpectedEOF
		c.log("SHORT READ", n, l, e)
		return b[0 : n-1], e
	}
	if c.checkReadError(e) != nil { // Other erors
		return b, e
	}
	c.setReadDeadline()
	_, _ = c.rdr.ReadByte()         // trailing NUL
	if c.checkReadError(e) != nil { // Other erors
		return b, e
	}
	return b, e
}

/*
	Common Header Validation.
*/
func checkHeaders(h Headers, p string) error {
	if h == nil {
		return EHDRNIL
	}
	// Length check
	if e := h.Validate(); e != nil {
		return e
	}
	// Empty key / value check
	for i := 0; i < len(h); i += 2 {
		if h[i] == "" {
			return EHDRMTK
		}
		if p == SPL_10 && h[i+1] == "" {
			return EHDRMTV
		}
	}
	// UTF8 check
	if p != SPL_10 {
		_, e := h.ValidateUTF8()
		if e != nil {
			return e
		}
	}
	return nil
}

/*
	Internal function used by heartbeat initialization.
*/
func max(a, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

/*
   Debug helper.  Get properly formatted destination.
*/
func dumpmd(md MessageData) {
	fmt.Printf("Command: %s\n", md.Message.Command)
	fmt.Println("Headers:")
	for i := 0; i < len(md.Message.Headers); i += 2 {
		fmt.Printf("key:%s\t\tvalue:%s\n",
			md.Message.Headers[i], md.Message.Headers[i+1])
	}
	fmt.Printf("Body: %s\n", string(md.Message.Body))
	if md.Error != nil {
		fmt.Printf("Error: %s\n", md.Error.Error())
	} else {
		fmt.Println("Error: nil")
	}
}
<commit_msg>Handle reading message body when underlying connection unexpectedly closes<commit_after>//
// Copyright © 2011-2017 Guy M. Allard
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package stompngo

import (
	"fmt"
	"io"
	"strings"
)

/*
	Encode a string per STOMP 1.1+ specifications.
*/
func encode(s string) string {
	r := s
	for _, tr := range codecValues {
		if strings.Index(r, tr.decoded) >= 0 {
			r = strings.Replace(r, tr.decoded, tr.encoded, -1)
		}
	}
	return r
}

/*
	Decode a string per STOMP 1.1+ specifications.
*/
func decode(s string) string {
	r := s
	for _, tr := range codecValues {
		if strings.Index(r, tr.encoded) >= 0 {
			r = strings.Replace(r, tr.encoded, tr.decoded, -1)
		}
	}
	return r
}

/*
	A network helper.  Read from the wire until a 0x00 byte is encountered.
*/
func readUntilNul(c *Connection) ([]uint8, error) {
	c.setReadDeadline()
	b, e := c.rdr.ReadBytes(0)
	if c.checkReadError(e) != nil {
		return b, e
	}
	if len(b) == 1 {
		b = NULLBUFF
	} else {
		b = b[0 : len(b)-1]
	}
	return b, e
}

/*
	A network helper.  Read a full message body with a known length that is
	> 0.  Then read the trailing 'null' byte expected for STOMP frames.
*/
func readBody(c *Connection, l int) ([]uint8, error) {
	b := make([]byte, l)
	c.setReadDeadline()
	n, e := io.ReadFull(c.rdr, b)
	if n < l && n != 0 { // Short read, e is ErrUnexpectedEOF
		c.log("SHORT READ", n, l, e)
		return b[0 : n-1], e
	}
	if c.checkReadError(e) != nil { // Other erors
		return b, e
	}
	c.setReadDeadline()
	_, _ = c.rdr.ReadByte()         // trailing NUL
	if c.checkReadError(e) != nil { // Other erors
		return b, e
	}
	return b, e
}

/*
	Common Header Validation.
*/
func checkHeaders(h Headers, p string) error {
	if h == nil {
		return EHDRNIL
	}
	// Length check
	if e := h.Validate(); e != nil {
		return e
	}
	// Empty key / value check
	for i := 0; i < len(h); i += 2 {
		if h[i] == "" {
			return EHDRMTK
		}
		if p == SPL_10 && h[i+1] == "" {
			return EHDRMTV
		}
	}
	// UTF8 check
	if p != SPL_10 {
		_, e := h.ValidateUTF8()
		if e != nil {
			return e
		}
	}
	return nil
}

/*
	Internal function used by heartbeat initialization.
*/
func max(a, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

/*
   Debug helper.  Get properly formatted destination.
*/
func dumpmd(md MessageData) {
	fmt.Printf("Command: %s\n", md.Message.Command)
	fmt.Println("Headers:")
	for i := 0; i < len(md.Message.Headers); i += 2 {
		fmt.Printf("key:%s\t\tvalue:%s\n",
			md.Message.Headers[i], md.Message.Headers[i+1])
	}
	fmt.Printf("Body: %s\n", string(md.Message.Body))
	if md.Error != nil {
		fmt.Printf("Error: %s\n", md.Error.Error())
	} else {
		fmt.Println("Error: nil")
	}
}
<|endoftext|>


<commit_before><commit_msg>pio: check error before deferring f.Close<commit_after><|endoftext|>


<commit_before>package flag_test

import (
	"fmt"
	"os"
	"strings"

	. "code.cloudfoundry.org/cli/command/flag"
	flags "github.com/jessevdk/go-flags"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("EnvironmentVariable", func() {
	var (
		envVar  EnvironmentVariable
		envList []string
	)

	BeforeEach(func() {
		envList = []string{
			"ENVIRONMENTVARIABLE_TEST_ABC",
			"ENVIRONMENTVARIABLE_TEST_FOO_BAR",
			"ENVIRONMENTVARIABLE_TEST_ACKBAR",
			"ENVIRONMENTVARIABLE_TEST_abc",
		}

		var err error
		for _, v := range envList {
			err = os.Setenv(v, "")
			Expect(err).NotTo(HaveOccurred())
		}
	})

	AfterEach(func() {
		var err error
		for _, v := range envList {
			err = os.Unsetenv(v)
			Expect(err).ToNot(HaveOccurred())
		}
	})

	Describe("Complete", func() {
		Context("when the prefix is empty", func() {
			It("returns no matches", func() {
				Expect(envVar.Complete("")).To(BeEmpty())
			})
		})

		Context("when the prefix does not start with $", func() {
			It("returns no matches", func() {
				Expect(envVar.Complete("A$A")).To(BeEmpty())
			})
		})

		Context("when the prefix starts with $", func() {
			Context("when only $ is specified", func() {
				It("returns all environment variables", func() {
					keyValPairs := os.Environ()
					envVars := make([]string, len(keyValPairs))
					for i, keyValPair := range keyValPairs {
						envVars[i] = fmt.Sprintf("$%s", strings.Split(keyValPair, "=")[0])
					}

					matches := envVar.Complete("$")
					Expect(matches).To(HaveLen(len(keyValPairs)))
					for _, v := range envVars {
						Expect(matches).To(ContainElement(flags.Completion{Item: v}))
					}
				})
			})

			Context("when additional characters are specified", func() {
				Context("when there are matching environment variables", func() {
					It("returns the matching environment variables", func() {
						matches := envVar.Complete("$ENVIRONMENTVARIABLE_TEST_A")
						Expect(matches).To(HaveLen(2))
						Expect(matches).To(ConsistOf(
							flags.Completion{Item: "$ENVIRONMENTVARIABLE_TEST_ABC"},
							flags.Completion{Item: "$ENVIRONMENTVARIABLE_TEST_ACKBAR"},
						))
					})

					It("is case sensitive", func() {
						matches := envVar.Complete("$ENVIRONMENTVARIABLE_TEST_a")
						Expect(matches).To(HaveLen(1))
						Expect(matches).To(ConsistOf(
							flags.Completion{Item: "$ENVIRONMENTVARIABLE_TEST_abc"},
						))
					})
				})

				Context("when there are no matching environment variables", func() {
					It("returns no matches", func() {
						Expect(envVar.Complete("$Z")).To(BeEmpty())
					})
				})
			})
		})
	})
})
<commit_msg>Don't run environment_variable_test on windows<commit_after>// +build !windows

package flag_test

import (
	"fmt"
	"os"
	"strings"

	. "code.cloudfoundry.org/cli/command/flag"
	flags "github.com/jessevdk/go-flags"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("EnvironmentVariable", func() {
	var (
		envVar  EnvironmentVariable
		envList []string
	)

	BeforeEach(func() {
		envList = []string{
			"ENVIRONMENTVARIABLE_TEST_ABC",
			"ENVIRONMENTVARIABLE_TEST_FOO_BAR",
			"ENVIRONMENTVARIABLE_TEST_ACKBAR",
			"ENVIRONMENTVARIABLE_TEST_abc",
		}

		var err error
		for _, v := range envList {
			err = os.Setenv(v, "")
			Expect(err).NotTo(HaveOccurred())
		}
	})

	AfterEach(func() {
		var err error
		for _, v := range envList {
			err = os.Unsetenv(v)
			Expect(err).ToNot(HaveOccurred())
		}
	})

	Describe("Complete", func() {
		Context("when the prefix is empty", func() {
			It("returns no matches", func() {
				Expect(envVar.Complete("")).To(BeEmpty())
			})
		})

		Context("when the prefix does not start with $", func() {
			It("returns no matches", func() {
				Expect(envVar.Complete("A$A")).To(BeEmpty())
			})
		})

		Context("when the prefix starts with $", func() {
			Context("when only $ is specified", func() {
				It("returns all environment variables", func() {
					keyValPairs := os.Environ()
					envVars := make([]string, len(keyValPairs))
					for i, keyValPair := range keyValPairs {
						envVars[i] = fmt.Sprintf("$%s", strings.Split(keyValPair, "=")[0])
					}

					matches := envVar.Complete("$")
					Expect(matches).To(HaveLen(len(keyValPairs)))
					for _, v := range envVars {
						Expect(matches).To(ContainElement(flags.Completion{Item: v}))
					}
				})
			})

			Context("when additional characters are specified", func() {
				Context("when there are matching environment variables", func() {
					It("returns the matching environment variables", func() {
						matches := envVar.Complete("$ENVIRONMENTVARIABLE_TEST_A")
						Expect(matches).To(HaveLen(2))
						Expect(matches).To(ConsistOf(
							flags.Completion{Item: "$ENVIRONMENTVARIABLE_TEST_ABC"},
							flags.Completion{Item: "$ENVIRONMENTVARIABLE_TEST_ACKBAR"},
						))
					})

					It("is case sensitive", func() {
						matches := envVar.Complete("$ENVIRONMENTVARIABLE_TEST_a")
						Expect(matches).To(HaveLen(1))
						Expect(matches).To(ConsistOf(
							flags.Completion{Item: "$ENVIRONMENTVARIABLE_TEST_abc"},
						))
					})
				})

				Context("when there are no matching environment variables", func() {
					It("returns no matches", func() {
						Expect(envVar.Complete("$Z")).To(BeEmpty())
					})
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"github.com/disintegration/imaging"
	"github.com/minotar/minecraft"
	"image"
	"image/draw"
	"image/png"
	"io"
)

const (
	HeadX      = 8
	HeadY      = 8
	HeadWidth  = 8
	HeadHeight = 8

	HelmX      = 40
	HelmY      = 8
	HelmWidth  = 8
	HelmHeight = 8

	TorsoX      = 20
	TorsoY      = 20
	TorsoWidth  = 8
	TorsoHeight = 12

	RaX      = 44
	RaY      = 20
	RaWidth  = 4
	RaHeight = 12

	RlX      = 4
	RlY      = 20
	RlWidth  = 4
	RlHeight = 12

	LaX      = 36
	LaY      = 52
	LaWidth  = 4
	LaHeight = 12

	LlX      = 20
	LlY      = 52
	LlWidth  = 4
	LlHeight = 12
)

type mcSkin struct {
	Processed image.Image
	minecraft.Skin
}

func (skin *mcSkin) GetHead() error {
	img, err := cropHead(skin.Image)
	if err != nil {
		return err
	}

	skin.Processed = img
	return nil
}

func (skin *mcSkin) GetHelm() error {
	helm, err := cropHelm(skin.Image)
	if err != nil {
		return err
	}

	skin.Processed = helm
	return nil
}

func (skin *mcSkin) GetBody() error {
	// Check if 1.8 skin (the max Y bound should be 64)
	render18Skin := true
	bounds := skin.Image.Bounds()
	if bounds.Max.Y != 64 {
		render18Skin = false
	}

	helmImg, err := cropHelm(skin.Image)
	if err != nil {
		return err
	}

	torsoImg, err := cropImage(skin.Image, image.Rect(TorsoX, TorsoY, TorsoX+TorsoWidth, TorsoY+TorsoHeight))
	if err != nil {
		return err
	}

	raImg, err := cropImage(skin.Image, image.Rect(RaX, RaY, RaX+RaWidth, RaY+RaHeight))
	if err != nil {
		return err
	}

	rlImg, err := cropImage(skin.Image, image.Rect(RlX, RlY, RlX+RlWidth, RlY+RlHeight))
	if err != nil {
		return err
	}

	var laImg, llImg image.Image

	// If the skin is 1.8 then we will use the left arms and legs, otherwise flip the right ones and use them.
	if render18Skin {
		laImg, err = cropImage(skin.Image, image.Rect(LaX, LaY, LaX+LaWidth, LaY+LaHeight))
		if err != nil {
			return err
		}

		llImg, err = cropImage(skin.Image, image.Rect(LlX, LlY, LlX+LlWidth, LlY+LlHeight))
		if err != nil {
			return err
		}
	} else {
		laImg = imaging.FlipH(raImg)

		llImg = imaging.FlipH(rlImg)
	}

	// Create a blank canvas for us to draw our body on
	bodyImg := image.NewRGBA(image.Rect(0, 0, LaWidth+TorsoWidth+RaWidth, HeadHeight+TorsoHeight+LlHeight))
	// Helm
	draw.Draw(bodyImg, image.Rect(LaWidth, 0, LaWidth+HelmWidth, HelmHeight), helmImg, image.Pt(0, 0), draw.Src)
	// Torso
	draw.Draw(bodyImg, image.Rect(LaWidth, HelmHeight, LaWidth+TorsoWidth, HelmHeight+TorsoHeight), torsoImg, image.Pt(0, 0), draw.Src)
	// Left Arm
	draw.Draw(bodyImg, image.Rect(0, HelmHeight, LaWidth, HelmHeight+LaHeight), laImg, image.Pt(0, 0), draw.Src)
	// Right Arm
	draw.Draw(bodyImg, image.Rect(LaWidth+TorsoWidth, HelmHeight, LaWidth+TorsoWidth+RaWidth, HelmHeight+RaHeight), raImg, image.Pt(0, 0), draw.Src)
	// Left Leg
	draw.Draw(bodyImg, image.Rect(LaWidth, HelmHeight+TorsoHeight, LaWidth+LlWidth, HelmHeight+TorsoHeight+LlHeight), llImg, image.Pt(0, 0), draw.Src)
	// Right Leg
	draw.Draw(bodyImg, image.Rect(LaWidth+LlWidth, HelmHeight+TorsoHeight, LaWidth+LlWidth+RlWidth, HelmHeight+TorsoHeight+RlHeight), rlImg, image.Pt(0, 0), draw.Src)

	skin.Processed = bodyImg
	return nil
}

func (skin *mcSkin) WritePNG(w io.Writer) error {
	return png.Encode(w, skin.Processed)
}

func (skin *mcSkin) Resize(width uint) {
	skin.Processed = imaging.Resize(skin.Processed, int(width), 0, imaging.NearestNeighbor)
}

func cropHead(img image.Image) (image.Image, error) {
	return cropImage(img, image.Rect(HeadX, HeadY, HeadX+HeadWidth, HeadY+HeadHeight))
}

func cropHelm(img image.Image) (image.Image, error) {
	// check if helm is solid colour - if so, it counts as transparent
	isSolidColour := true
	baseColour := img.At(HelmX, HelmY)
	for checkX := HelmX; checkX < HelmX+HelmWidth; checkX++ {
		for checkY := HelmY; checkY < HelmY+HelmHeight; checkY++ {
			checkColour := img.At(checkX, checkY)
			if checkColour != baseColour {
				isSolidColour = false
				break
			}
		}
	}

	headImg, err := cropHead(img)

	if err != nil {
		return nil, err
	}

	if !isSolidColour {
		headImgRGBA := headImg.(*image.RGBA)

		helmImg, err := cropImage(img, image.Rect(HelmX, HelmY, HelmX+HelmWidth, HelmY+HelmHeight))
		if err != nil {
			return nil, err
		}

		sr := helmImg.Bounds()
		draw.Draw(headImgRGBA, sr, helmImg, sr.Min, draw.Over)
	}

	return headImg, nil
}

func cropImage(i image.Image, d image.Rectangle) (image.Image, error) {
	bounds := i.Bounds()
	if bounds.Min.X > d.Min.X || bounds.Min.Y > d.Min.Y || bounds.Max.X < d.Max.X || bounds.Max.Y < d.Max.Y {
		return nil, errors.New("Bounds invalid for crop")
	}

	dims := d.Size()
	outIm := image.NewRGBA(image.Rect(0, 0, dims.X, dims.Y))
	for x := 0; x < dims.X; x++ {
		for y := 0; y < dims.Y; y++ {
			outIm.Set(x, y, i.At(d.Min.X+x, d.Min.Y+y))
		}
	}
	return outIm, nil
}
<commit_msg>Remove check for alpha - treat all images as being alpha<commit_after>package main

import (
	"errors"
	"github.com/disintegration/imaging"
	"github.com/minotar/minecraft"
	"image"
	"image/draw"
	"image/png"
	"io"
)

const (
	HeadX      = 8
	HeadY      = 8
	HeadWidth  = 8
	HeadHeight = 8

	HelmX      = 40
	HelmY      = 8
	HelmWidth  = 8
	HelmHeight = 8

	TorsoX      = 20
	TorsoY      = 20
	TorsoWidth  = 8
	TorsoHeight = 12

	RaX      = 44
	RaY      = 20
	RaWidth  = 4
	RaHeight = 12

	RlX      = 4
	RlY      = 20
	RlWidth  = 4
	RlHeight = 12

	LaX      = 36
	LaY      = 52
	LaWidth  = 4
	LaHeight = 12

	LlX      = 20
	LlY      = 52
	LlWidth  = 4
	LlHeight = 12
)

type mcSkin struct {
	Processed image.Image
	minecraft.Skin
}

func (skin *mcSkin) GetHead() error {
	img, err := cropHead(skin.Image)
	if err != nil {
		return err
	}

	skin.Processed = img
	return nil
}

func (skin *mcSkin) GetHelm() error {
	helm, err := cropHelm(skin.Image)
	if err != nil {
		return err
	}

	skin.Processed = helm
	return nil
}

func (skin *mcSkin) GetBody() error {
	// Check if 1.8 skin (the max Y bound should be 64)
	render18Skin := true
	bounds := skin.Image.Bounds()
	if bounds.Max.Y != 64 {
		render18Skin = false
	}

	helmImg, err := cropHelm(skin.Image)
	if err != nil {
		return err
	}

	torsoImg, err := cropImage(skin.Image, image.Rect(TorsoX, TorsoY, TorsoX+TorsoWidth, TorsoY+TorsoHeight))
	if err != nil {
		return err
	}

	raImg, err := cropImage(skin.Image, image.Rect(RaX, RaY, RaX+RaWidth, RaY+RaHeight))
	if err != nil {
		return err
	}

	rlImg, err := cropImage(skin.Image, image.Rect(RlX, RlY, RlX+RlWidth, RlY+RlHeight))
	if err != nil {
		return err
	}

	var laImg, llImg image.Image

	// If the skin is 1.8 then we will use the left arms and legs, otherwise flip the right ones and use them.
	if render18Skin {
		laImg, err = cropImage(skin.Image, image.Rect(LaX, LaY, LaX+LaWidth, LaY+LaHeight))
		if err != nil {
			return err
		}

		llImg, err = cropImage(skin.Image, image.Rect(LlX, LlY, LlX+LlWidth, LlY+LlHeight))
		if err != nil {
			return err
		}
	} else {
		laImg = imaging.FlipH(raImg)

		llImg = imaging.FlipH(rlImg)
	}

	// Create a blank canvas for us to draw our body on
	bodyImg := image.NewRGBA(image.Rect(0, 0, LaWidth+TorsoWidth+RaWidth, HeadHeight+TorsoHeight+LlHeight))
	// Helm
	draw.Draw(bodyImg, image.Rect(LaWidth, 0, LaWidth+HelmWidth, HelmHeight), helmImg, image.Pt(0, 0), draw.Src)
	// Torso
	draw.Draw(bodyImg, image.Rect(LaWidth, HelmHeight, LaWidth+TorsoWidth, HelmHeight+TorsoHeight), torsoImg, image.Pt(0, 0), draw.Src)
	// Left Arm
	draw.Draw(bodyImg, image.Rect(0, HelmHeight, LaWidth, HelmHeight+LaHeight), laImg, image.Pt(0, 0), draw.Src)
	// Right Arm
	draw.Draw(bodyImg, image.Rect(LaWidth+TorsoWidth, HelmHeight, LaWidth+TorsoWidth+RaWidth, HelmHeight+RaHeight), raImg, image.Pt(0, 0), draw.Src)
	// Left Leg
	draw.Draw(bodyImg, image.Rect(LaWidth, HelmHeight+TorsoHeight, LaWidth+LlWidth, HelmHeight+TorsoHeight+LlHeight), llImg, image.Pt(0, 0), draw.Src)
	// Right Leg
	draw.Draw(bodyImg, image.Rect(LaWidth+LlWidth, HelmHeight+TorsoHeight, LaWidth+LlWidth+RlWidth, HelmHeight+TorsoHeight+RlHeight), rlImg, image.Pt(0, 0), draw.Src)

	skin.Processed = bodyImg
	return nil
}

func (skin *mcSkin) WritePNG(w io.Writer) error {
	return png.Encode(w, skin.Processed)
}

func (skin *mcSkin) Resize(width uint) {
	skin.Processed = imaging.Resize(skin.Processed, int(width), 0, imaging.NearestNeighbor)
}

func cropHead(img image.Image) (image.Image, error) {
	return cropImage(img, image.Rect(HeadX, HeadY, HeadX+HeadWidth, HeadY+HeadHeight))
}

func cropHelm(img image.Image) (image.Image, error) {
	headImg, err := cropHead(img)

	if err != nil {
		return nil, err
	}

	headImgRGBA := headImg.(*image.RGBA)

	helmImg, err := cropImage(img, image.Rect(HelmX, HelmY, HelmX+HelmWidth, HelmY+HelmHeight))
	if err != nil {
		return nil, err
	}

	sr := helmImg.Bounds()
	draw.Draw(headImgRGBA, sr, helmImg, sr.Min, draw.Over)

	return headImg, nil
}

func cropImage(i image.Image, d image.Rectangle) (image.Image, error) {
	bounds := i.Bounds()
	if bounds.Min.X > d.Min.X || bounds.Min.Y > d.Min.Y || bounds.Max.X < d.Max.X || bounds.Max.Y < d.Max.Y {
		return nil, errors.New("Bounds invalid for crop")
	}

	dims := d.Size()
	outIm := image.NewRGBA(image.Rect(0, 0, dims.X, dims.Y))
	for x := 0; x < dims.X; x++ {
		for y := 0; y < dims.Y; y++ {
			outIm.Set(x, y, i.At(d.Min.X+x, d.Min.Y+y))
		}
	}
	return outIm, nil
}
<|endoftext|>


<commit_before>// Boringstreamer looks for mp3 files and broadcasts via http (live streaming.)
//
// $ boringstreamer
//
// or
//
// c:\>boringstreamer.exe
//
// recursively looks for .mp3 files starting from current working directory and broadcasts on port 4444 for at most 42 concurrent http clients.
//
// See -h for details.
//
// Browse to listen (e.g. http://localhost:4444/)
package main

import (
	"bytes"
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/tcolgate/mp3"
)

var (
	addr           = flag.String("addr", ":4444", "listen on address (format: :port or host:port)")
	maxConnections = flag.Int("max", 42, "set maximum number of streaming connections")
	recursively    = flag.Bool("r", true, "recursively look for music starting from path")
	verbose        = flag.Bool("v", false, "display verbose messages")
)

var debugging bool	// controlled by hidden command line argument -debug

// like /dev/null
type nullWriter struct {
}

func (nw nullWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

type streamFrame []byte

// client's event
type broadcastResult struct {
	qid int
	err error
}

// After a start() mux broadcasts audio stream to subscribed clients (ie. to http servers).
// Clients subscribe() and unsubscribe by writing to result chanel.
type mux struct {
	sync.Mutex

	clients map[int]chan streamFrame // set of listener clients to be notified
	result  chan broadcastResult     // clients share broadcast success-failure here
}

// subscribe(ch) adds ch to the set of channels to be received on by the clients when a new audio frame is available.
// Returns uniq client id (qid) for ch and a broadcast result channel for the client.
// Returns -1, nil if too many clients are already listening.
// clients: qid, br := m.subscribe(ch)
func (m *mux) subscribe(ch chan streamFrame) (int, chan broadcastResult) {
	m.Lock()
	defer m.Unlock()
	// search for available qid
	qid := 0
	_, ok := m.clients[qid]
	for ; ok; _, ok = m.clients[qid] {
		if qid >= *maxConnections-1 {
			return -1, nil
		}
		qid++
	}
	m.clients[qid] = ch
	if *verbose {
		fmt.Printf("New connection (qid: %v), streaming to %v connections, at %v\n", qid, len(m.clients), time.Now().Format(time.Stamp))
	}

	return qid, m.result
}

// start() initializes a multiplexer for raw audio streams
// e.g: m := new(mux).start(path)
func (m *mux) start(path string) *mux {
	m.result = make(chan broadcastResult)
	m.clients = make(map[int]chan streamFrame)

	// flow structure: fs -> nextFile -> nextStream -> nextFrame -> subscribed http servers -> browsers
	nextFile := make(chan string)			// next file to be broadcast
	nextStream := make(chan io.Reader)		// next raw audio stream
	nextFrame := make(chan streamFrame)	// next audio frame

	// generate randomized list of files available from path
	rand.Seed(time.Now().Unix()) // minimal randomness
	rescan := make(chan chan string)
	go func() {
		for {
			files := <-rescan
			filepath.Walk(path, func(wpath string, info os.FileInfo, err error) error {
				if err != nil {
					return nil
				}
				if !info.Mode().IsRegular() {
					return nil
				}
				probably := strings.HasSuffix(strings.ToLower(info.Name()), ".mp3") // probably mp3
				if !info.IsDir() && !probably {
					return nil
				}
				files <- wpath // found file

				return nil
			})
			close(files)
			time.Sleep(1 * time.Second) // if no files are found, poll at least with 1Hz
		}
	}()

	// buffer and shuffle
	go func() {
		for {
			files := make(chan string)
			rescan <- files

			shuffled := make([]string, 0) // randomized set of files

			for f := range files {
				select {
				case <- time.After(100*time.Millisecond):	// start playing as soon as possible, but wait at least 0.1 second for shuffling
					nextFile <- f
					if *verbose {
						fmt.Printf("Next: %v\n", f)
					}
				default:
					// shuffle files for random playback
					// (random permutation)
					if len(shuffled) == 0 {
						shuffled = append(shuffled, f)
					} else {
						i := rand.Intn(len(shuffled))
						shuffled = append(shuffled, shuffled[i])
						shuffled[i] = f
					}
				}
			}

			// queue shuffled files
			for _, f := range shuffled {
				nextFile <- f
				if *verbose {
					fmt.Printf("Next: %v\n", f)
				}
			}
		}
	}()

	// open file
	go func() {
		for {
			filename := <-nextFile
			f, err := os.Open(filename)
			if err != nil {
				if debugging {
					log.Printf("Skipped \"%v\", err=%v", filename, err)
				}
				continue
			}
			nextStream <- bufio.NewReaderSize(f, 1024*1024)
			if *verbose {
				fmt.Printf("Now playing: %v\n", filename)
			}
		}
	}()

	// decode stream to frames and delay for frame duration
	go func() {
		nullwriter := new(nullWriter)
		var cumwait time.Duration
		for {
			streamReader := <- nextStream
			d := mp3.NewDecoder(streamReader)
			var f mp3.Frame
			for {
				t0 := time.Now()
				tmp := log.Prefix()
				if !debugging {
					log.SetOutput(nullwriter) // hack to silence mp3 debug/log output
				} else {
					log.SetPrefix("info: mp3 decode msg: ")
				}
				err := d.Decode(&f)
				log.SetPrefix(tmp)
				if !debugging {
					log.SetOutput(os.Stderr)
				}
				if err == io.EOF {
					break
				}
				if err != nil {
					if debugging {
						log.Printf("Skipping frame, d.Decode() err=%v", err)
					}
					continue
				}
				buf, err := ioutil.ReadAll(f.Reader())
				if err != nil {
					if debugging {
						log.Printf("Skipping frame, ioutil.ReadAll() err=%v", err)
					}
					continue
				}
				nextFrame <- buf

				towait := f.Duration() - time.Now().Sub(t0)
				cumwait += towait // towait can be negative -> cumwait
				if cumwait > 1*time.Second {
					time.Sleep(cumwait)
					cumwait = 0
				}
			}
		}
	}()

	// broadcast frame to clients
	go func() {
		for {
			f := <-nextFrame
			// notify clients of new audio frame or let them quit
			m.Lock()
			for _, ch := range m.clients {
				m.Unlock()
				ch <- f
				br := <-m.result // handle quitting clients
				if br.err != nil {
					m.Lock()
					close(m.clients[br.qid])
					delete(m.clients, br.qid)
					m.Unlock()
					if debugging {
						log.Printf("Connection exited, qid: %v, error %v. Now streaming to %v connections.", br.qid, br.err, len(m.clients))
					} else if *verbose {
						fmt.Printf("Connection exited (qid: %v), streaming to %v connections, at %v\n", br.qid, len(m.clients), time.Now().Format(time.Stamp))
					}
				}
				m.Lock()
			}
			m.Unlock()
		}
	}()

	return m
}

type streamHandler struct {
	stream *mux
}

// chrome and firefox play mp3 audio stream directly
func (sh streamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	now := time.Now().UTC()
	frames := make(chan streamFrame)
	qid, br := sh.stream.subscribe(frames)
	if qid < 0 {
		log.Printf("Error: new connection request denied, already serving %v connections. See -h for details.", *maxConnections)
		w.WriteHeader(http.StatusTooManyRequests)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Header().Set("Date", now.Format(http.TimeFormat))
	w.Header().Set("Connection", "Keep-Alive")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Content-Type", "audio/mpeg")
	w.Header().Set("Server", "BoringStreamer/4.0")

	// some browsers need ID3 tag to identify first frame as audio media to be played
	// minimal ID3 header to designate audio stream
	b := []byte{0x49, 0x44, 0x33, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	_, err := io.Copy(w, bytes.NewReader(b))
	if err == nil {
		// broadcast mp3 stream to w
		broadcastTimeout := 4 * time.Second // timeout for slow clients
		result := make(chan error)
		for {
			buf := <-frames

			go func(r chan error, b []byte) {
				_, err = io.Copy(w, bytes.NewReader(b))
				r <- err
			}(result, buf)

			select {
			case err = <-result:
				if err != nil {
					break
				}
				br <- broadcastResult{qid, nil} // frame streamed, no error, send ack
			case <-time.After(broadcastTimeout): // it's an error if io.Copy() is not finished within broadcastTimeout, ServeHTTP should exit
				err = errors.New(fmt.Sprintf("timeout: %v", broadcastTimeout))
			}

			if err != nil {
				break
			}
		}
	}
	br <- broadcastResult{qid, err} // error, send nack
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [flags] [path]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Browse to listen (e.g. http://localhost:4444/)\n\nflags:\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	if len(flag.Args()) > 1 && flag.Args()[1] != "-debug" {
		flag.Usage()
		os.Exit(1)
	}

	path := "."
	switch len(flag.Args()) {
	case 0:
		if *verbose {
			fmt.Printf("Using path %#v, see -h for details.\n", path)
		}
	case 1:
		path = flag.Args()[0]
	case 2:
		path = flag.Args()[0]
		debugging = true
	}

	if *verbose {
		fmt.Printf("Looking for files available from \"%v\" ...\n", path)
	}

	// check if path is available
	matches, err := filepath.Glob(path)
	if err != nil || len(matches) != 1 {
		fmt.Fprintf(os.Stderr, "Error: \"%v\" unavailable, nothing to play.\n", path)
		os.Exit(1)
	}

	// initialize and start mp3 streamer
	http.Handle("/", streamHandler{new(mux).start(path)})
	if *verbose {
		fmt.Printf("Waiting for connections on %v\n", *addr)
	}

	err = http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>modify logging for more robust operations (no io during lock/unlock<commit_after>// Boringstreamer looks for mp3 files and broadcasts via http (live streaming.)
//
// $ boringstreamer
//
// or
//
// c:\>boringstreamer.exe
//
// recursively looks for .mp3 files starting from current working directory and broadcasts on port 4444 for at most 42 concurrent http clients.
//
// See -h for details.
//
// Browse to listen (e.g. http://localhost:4444/)
package main

import (
	"bytes"
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/tcolgate/mp3"
)

var (
	addr           = flag.String("addr", ":4444", "listen on address (format: :port or host:port)")
	maxConnections = flag.Int("max", 42, "set maximum number of streaming connections")
	recursively    = flag.Bool("r", true, "recursively look for music starting from path")
	verbose        = flag.Bool("v", false, "display verbose messages")
)

var debugging bool	// controlled by hidden command line argument -debug

// like /dev/null
type nullWriter struct {
}

func (nw nullWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

type streamFrame []byte

// client's event
type broadcastResult struct {
	qid int
	err error
}

// After a start() mux broadcasts audio stream to subscribed clients (ie. to http servers).
// Clients subscribe() and unsubscribe by writing to result chanel.
type mux struct {
	sync.Mutex

	clients map[int]chan streamFrame // set of listener clients to be notified
	result  chan broadcastResult     // clients share broadcast success-failure here
}

// subscribe(ch) adds ch to the set of channels to be received on by the clients when a new audio frame is available.
// Returns uniq client id (qid) for ch and a broadcast result channel for the client.
// Returns -1, nil if too many clients are already listening.
// clients: qid, br := m.subscribe(ch)
func (m *mux) subscribe(ch chan streamFrame) (int, chan broadcastResult) {
	m.Lock()
	// search for available qid
	qid := 0
	_, ok := m.clients[qid]
	for ; ok; _, ok = m.clients[qid] {
		if qid >= *maxConnections-1 {
			m.Unlock()
			return -1, nil
		}
		qid++
	}
	m.clients[qid] = ch
	m.Unlock()
	if *verbose {
		fmt.Printf("New connection (qid: %v), streaming to %v connections, at %v\n", qid, len(m.clients), time.Now().Format(time.Stamp))
	}

	return qid, m.result
}

// start() initializes a multiplexer for raw audio streams
// e.g: m := new(mux).start(path)
func (m *mux) start(path string) *mux {
	m.result = make(chan broadcastResult)
	m.clients = make(map[int]chan streamFrame)

	// flow structure: fs -> nextFile -> nextStream -> nextFrame -> subscribed http servers -> browsers
	nextFile := make(chan string)			// next file to be broadcast
	nextStream := make(chan io.Reader)		// next raw audio stream
	nextFrame := make(chan streamFrame)	// next audio frame

	// generate randomized list of files available from path
	rand.Seed(time.Now().Unix()) // minimal randomness
	rescan := make(chan chan string)
	go func() {
		for {
			files := <-rescan
			filepath.Walk(path, func(wpath string, info os.FileInfo, err error) error {
				if err != nil {
					return nil
				}
				if !info.Mode().IsRegular() {
					return nil
				}
				probably := strings.HasSuffix(strings.ToLower(info.Name()), ".mp3") // probably mp3
				if !info.IsDir() && !probably {
					return nil
				}
				files <- wpath // found file

				return nil
			})
			close(files)
			time.Sleep(1 * time.Second) // if no files are found, poll at least with 1Hz
		}
	}()

	// buffer and shuffle
	go func() {
		for {
			files := make(chan string)
			rescan <- files

			shuffled := make([]string, 0) // randomized set of files

			for f := range files {
				select {
				case <- time.After(100*time.Millisecond):	// start playing as soon as possible, but wait at least 0.1 second for shuffling
					nextFile <- f
					if *verbose {
						fmt.Printf("Next: %v\n", f)
					}
				default:
					// shuffle files for random playback
					// (random permutation)
					if len(shuffled) == 0 {
						shuffled = append(shuffled, f)
					} else {
						i := rand.Intn(len(shuffled))
						shuffled = append(shuffled, shuffled[i])
						shuffled[i] = f
					}
				}
			}

			// queue shuffled files
			for _, f := range shuffled {
				nextFile <- f
				if *verbose {
					fmt.Printf("Next: %v\n", f)
				}
			}
		}
	}()

	// open file
	go func() {
		for {
			filename := <-nextFile
			f, err := os.Open(filename)
			if err != nil {
				if debugging {
					log.Printf("Skipped \"%v\", err=%v", filename, err)
				}
				continue
			}
			nextStream <- bufio.NewReaderSize(f, 1024*1024)
			if *verbose {
				fmt.Printf("Now playing: %v\n", filename)
			}
		}
	}()

	// decode stream to frames and delay for frame duration
	go func() {
		nullwriter := new(nullWriter)
		var cumwait time.Duration
		for {
			streamReader := <- nextStream
			d := mp3.NewDecoder(streamReader)
			var f mp3.Frame
			for {
				t0 := time.Now()
				tmp := log.Prefix()
				if !debugging {
					log.SetOutput(nullwriter) // hack to silence mp3 debug/log output
				} else {
					log.SetPrefix("info: mp3 decode msg: ")
				}
				err := d.Decode(&f)
				log.SetPrefix(tmp)
				if !debugging {
					log.SetOutput(os.Stderr)
				}
				if err == io.EOF {
					break
				}
				if err != nil {
					if debugging {
						log.Printf("Skipping frame, d.Decode() err=%v", err)
					}
					continue
				}
				buf, err := ioutil.ReadAll(f.Reader())
				if err != nil {
					if debugging {
						log.Printf("Skipping frame, ioutil.ReadAll() err=%v", err)
					}
					continue
				}
				nextFrame <- buf

				towait := f.Duration() - time.Now().Sub(t0)
				cumwait += towait // towait can be negative -> cumwait
				if cumwait > 1*time.Second {
					time.Sleep(cumwait)
					cumwait = 0
				}
			}
		}
	}()

	// broadcast frame to clients
	go func() {
		for {
			f := <-nextFrame
			// notify clients of new audio frame or let them quit
			m.Lock()
			for _, ch := range m.clients {
				m.Unlock()
				ch <- f
				br := <-m.result // handle quitting clients
				if br.err != nil {
					m.Lock()
					close(m.clients[br.qid])
					delete(m.clients, br.qid)
					nclients := len(m.clients)
					m.Unlock()
					if debugging {
						log.Printf("Connection exited, qid: %v, error %v. Now streaming to %v connections.", br.qid, br.err, nclients)
					} else if *verbose {
						fmt.Printf("Connection exited, qid: %v. Now streaming to %v connections, at %v\n", br.qid, nclients, time.Now().Format(time.Stamp))
					}
				}
				m.Lock()
			}
			m.Unlock()
		}
	}()

	return m
}

type streamHandler struct {
	stream *mux
}

// chrome and firefox play mp3 audio stream directly
func (sh streamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	now := time.Now().UTC()
	frames := make(chan streamFrame)
	qid, br := sh.stream.subscribe(frames)
	if qid < 0 {
		log.Printf("Error: new connection request denied, already serving %v connections. See -h for details.", *maxConnections)
		w.WriteHeader(http.StatusTooManyRequests)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Header().Set("Date", now.Format(http.TimeFormat))
	w.Header().Set("Connection", "Keep-Alive")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Content-Type", "audio/mpeg")
	w.Header().Set("Server", "BoringStreamer/4.0")

	// some browsers need ID3 tag to identify first frame as audio media to be played
	// minimal ID3 header to designate audio stream
	b := []byte{0x49, 0x44, 0x33, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	_, err := io.Copy(w, bytes.NewReader(b))
	if err == nil {
		// broadcast mp3 stream to w
		broadcastTimeout := 4 * time.Second // timeout for slow clients
		result := make(chan error)
		for {
			buf := <-frames

			go func(r chan error, b []byte) {
				_, err = io.Copy(w, bytes.NewReader(b))
				r <- err
			}(result, buf)

			select {
			case err = <-result:
				if err != nil {
					break
				}
				br <- broadcastResult{qid, nil} // frame streamed, no error, send ack
			case <-time.After(broadcastTimeout): // it's an error if io.Copy() is not finished within broadcastTimeout, ServeHTTP should exit
				err = errors.New(fmt.Sprintf("timeout: %v", broadcastTimeout))
			}

			if err != nil {
				break
			}
		}
	}
	br <- broadcastResult{qid, err} // error, send nack
}

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [flags] [path]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Browse to listen (e.g. http://localhost:4444/)\n\nflags:\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	if len(flag.Args()) > 1 && flag.Args()[1] != "-debug" {
		flag.Usage()
		os.Exit(1)
	}

	path := "."
	switch len(flag.Args()) {
	case 0:
		if *verbose {
			fmt.Printf("Using path %#v, see -h for details.\n", path)
		}
	case 1:
		path = flag.Args()[0]
	case 2:
		path = flag.Args()[0]
		debugging = true
	}

	if *verbose {
		fmt.Printf("Looking for files available from \"%v\" ...\n", path)
	}

	// check if path is available
	matches, err := filepath.Glob(path)
	if err != nil || len(matches) != 1 {
		fmt.Fprintf(os.Stderr, "Error: \"%v\" unavailable, nothing to play.\n", path)
		os.Exit(1)
	}

	// initialize and start mp3 streamer
	http.Handle("/", streamHandler{new(mux).start(path)})
	if *verbose {
		fmt.Printf("Waiting for connections on %v\n", *addr)
	}

	err = http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before><commit_msg>add GetStrings method<commit_after><|endoftext|>


<commit_before>package core

import (
	"context"
	"sync"

	"v2ray.com/core/common"
	"v2ray.com/core/common/uuid"
)

// Server is an instance of V2Ray. At any time, there must be at most one Server instance running.
// Deprecated. Use Instance directly.
type Server interface {
	common.Runnable
}

// Feature is the interface for V2Ray features. All features must implement this interface.
// All existing features have an implementation in app directory. These features can be replaced by third-party ones.
type Feature interface {
	common.Runnable
}

// Instance combines all functionalities in V2Ray.
type Instance struct {
	dnsClient     syncDNSClient
	policyManager syncPolicyManager
	dispatcher    syncDispatcher
	router        syncRouter
	ihm           syncInboundHandlerManager
	ohm           syncOutboundHandlerManager
	clock         syncClock
	cmd           syncCommander

	access   sync.Mutex
	features []Feature
	id       uuid.UUID
	running  bool
}

// New returns a new V2Ray instance based on given configuration.
// The instance is not started at this point.
// To make sure V2Ray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional.
func New(config *Config) (*Instance, error) {
	var server = &Instance{
		id: uuid.New(),
	}

	if err := config.Transport.Apply(); err != nil {
		return nil, err
	}

	for _, appSettings := range config.App {
		settings, err := appSettings.GetInstance()
		if err != nil {
			return nil, err
		}
		if _, err := server.CreateObject(settings); err != nil {
			return nil, err
		}
	}

	for _, inbound := range config.Inbound {
		rawHandler, err := server.CreateObject(inbound)
		if err != nil {
			return nil, err
		}
		handler, ok := rawHandler.(InboundHandler)
		if !ok {
			return nil, newError("not an InboundHandler")
		}
		if err := server.InboundHandlerManager().AddHandler(context.Background(), handler); err != nil {
			return nil, err
		}
	}

	for _, outbound := range config.Outbound {
		rawHandler, err := server.CreateObject(outbound)
		if err != nil {
			return nil, err
		}
		handler, ok := rawHandler.(OutboundHandler)
		if !ok {
			return nil, newError("not an OutboundHandler")
		}
		if err := server.OutboundHandlerManager().AddHandler(context.Background(), handler); err != nil {
			return nil, err
		}
	}

	return server, nil
}

func (s *Instance) CreateObject(config interface{}) (interface{}, error) {
	ctx := context.WithValue(context.Background(), v2rayKey, s)
	return common.CreateObject(ctx, config)
}

// ID returns an unique ID for this V2Ray instance.
func (s *Instance) ID() uuid.UUID {
	return s.id
}

// Close shutdown the V2Ray instance.
func (s *Instance) Close() error {
	s.access.Lock()
	defer s.access.Unlock()

	s.running = false
	for _, f := range s.features {
		f.Close()
	}

	return nil
}

// Start starts the V2Ray instance, including all registered features. When Start returns error, the state of the instance is unknown.
// A V2Ray instance can be started only once. Upon closing, the instance is not guaranteed to start again.
func (s *Instance) Start() error {
	s.access.Lock()
	defer s.access.Unlock()

	s.running = true
	for _, f := range s.features {
		if err := f.Start(); err != nil {
			return err
		}
	}

	newError("V2Ray started").AtWarning().WriteToLog()

	return nil
}

// RegisterFeature registers the given feature into V2Ray.
// If feature is one of the following types, the corressponding feature in this Instance
// will be replaced: DNSClient, PolicyManager, Router, Dispatcher, InboundHandlerManager, OutboundHandlerManager.
func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error {
	switch feature.(type) {
	case DNSClient, *DNSClient:
		s.dnsClient.Set(instance.(DNSClient))
	case PolicyManager, *PolicyManager:
		s.policyManager.Set(instance.(PolicyManager))
	case Router, *Router:
		s.router.Set(instance.(Router))
	case Dispatcher, *Dispatcher:
		s.dispatcher.Set(instance.(Dispatcher))
	case InboundHandlerManager, *InboundHandlerManager:
		s.ihm.Set(instance.(InboundHandlerManager))
	case OutboundHandlerManager, *OutboundHandlerManager:
		s.ohm.Set(instance.(OutboundHandlerManager))
	case Clock, *Clock:
		s.clock.Set(instance.(Clock))
	case Commander, *Commander:
		s.cmd.Set(instance.(Commander))
	}
	s.access.Lock()
	defer s.access.Unlock()

	s.features = append(s.features, instance)
	if s.running {
		return instance.Start()
	}
	return nil
}

// GetFeature returns a feature that was registered in this Instance. Nil if not found.
func (s *Instance) GetFeature(featureType interface{}) Feature {
	for _, f := range s.features {
		if hasType, ok := f.(common.HasType); ok {
			if hasType.Type() == featureType {
				return f
			}
		}
	}
	return nil
}

// DNSClient returns the DNSClient used by this Instance. The returned DNSClient is always functional.
func (s *Instance) DNSClient() DNSClient {
	return &(s.dnsClient)
}

// PolicyManager returns the PolicyManager used by this Instance. The returned PolicyManager is always functional.
func (s *Instance) PolicyManager() PolicyManager {
	return &(s.policyManager)
}

// Router returns the Router used by this Instance. The returned Router is always functional.
func (s *Instance) Router() Router {
	return &(s.router)
}

// Dispatcher returns the Dispatcher used by this Instance. If Dispatcher was not registered before, the returned value doesn't work, although it is not nil.
func (s *Instance) Dispatcher() Dispatcher {
	return &(s.dispatcher)
}

// InboundHandlerManager returns the InboundHandlerManager used by this Instance. If InboundHandlerManager was not registered before, the returned value doesn't work.
func (s *Instance) InboundHandlerManager() InboundHandlerManager {
	return &(s.ihm)
}

// OutboundHandlerManager returns the OutboundHandlerManager used by this Instance. If OutboundHandlerManager was not registered before, the returned value doesn't work.
func (s *Instance) OutboundHandlerManager() OutboundHandlerManager {
	return &(s.ohm)
}

// Clock returns the Clock used by this Instance. The returned Clock is always functional.
func (s *Instance) Clock() Clock {
	return &(s.clock)
}

// Commander returns the Commander used by this Instance. The returned Commander is always functional.
func (s *Instance) Commander() Commander {
	return &(s.cmd)
}
<commit_msg>details about GetFeature<commit_after>package core

import (
	"context"
	"sync"

	"v2ray.com/core/common"
	"v2ray.com/core/common/uuid"
)

// Server is an instance of V2Ray. At any time, there must be at most one Server instance running.
// Deprecated. Use Instance directly.
type Server interface {
	common.Runnable
}

// Feature is the interface for V2Ray features. All features must implement this interface.
// All existing features have an implementation in app directory. These features can be replaced by third-party ones.
type Feature interface {
	common.Runnable
}

// Instance combines all functionalities in V2Ray.
type Instance struct {
	dnsClient     syncDNSClient
	policyManager syncPolicyManager
	dispatcher    syncDispatcher
	router        syncRouter
	ihm           syncInboundHandlerManager
	ohm           syncOutboundHandlerManager
	clock         syncClock
	cmd           syncCommander

	access   sync.Mutex
	features []Feature
	id       uuid.UUID
	running  bool
}

// New returns a new V2Ray instance based on given configuration.
// The instance is not started at this point.
// To make sure V2Ray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional.
func New(config *Config) (*Instance, error) {
	var server = &Instance{
		id: uuid.New(),
	}

	if err := config.Transport.Apply(); err != nil {
		return nil, err
	}

	for _, appSettings := range config.App {
		settings, err := appSettings.GetInstance()
		if err != nil {
			return nil, err
		}
		if _, err := server.CreateObject(settings); err != nil {
			return nil, err
		}
	}

	for _, inbound := range config.Inbound {
		rawHandler, err := server.CreateObject(inbound)
		if err != nil {
			return nil, err
		}
		handler, ok := rawHandler.(InboundHandler)
		if !ok {
			return nil, newError("not an InboundHandler")
		}
		if err := server.InboundHandlerManager().AddHandler(context.Background(), handler); err != nil {
			return nil, err
		}
	}

	for _, outbound := range config.Outbound {
		rawHandler, err := server.CreateObject(outbound)
		if err != nil {
			return nil, err
		}
		handler, ok := rawHandler.(OutboundHandler)
		if !ok {
			return nil, newError("not an OutboundHandler")
		}
		if err := server.OutboundHandlerManager().AddHandler(context.Background(), handler); err != nil {
			return nil, err
		}
	}

	return server, nil
}

func (s *Instance) CreateObject(config interface{}) (interface{}, error) {
	ctx := context.WithValue(context.Background(), v2rayKey, s)
	return common.CreateObject(ctx, config)
}

// ID returns an unique ID for this V2Ray instance.
func (s *Instance) ID() uuid.UUID {
	return s.id
}

// Close shutdown the V2Ray instance.
func (s *Instance) Close() error {
	s.access.Lock()
	defer s.access.Unlock()

	s.running = false
	for _, f := range s.features {
		f.Close()
	}

	return nil
}

// Start starts the V2Ray instance, including all registered features. When Start returns error, the state of the instance is unknown.
// A V2Ray instance can be started only once. Upon closing, the instance is not guaranteed to start again.
func (s *Instance) Start() error {
	s.access.Lock()
	defer s.access.Unlock()

	s.running = true
	for _, f := range s.features {
		if err := f.Start(); err != nil {
			return err
		}
	}

	newError("V2Ray started").AtWarning().WriteToLog()

	return nil
}

// RegisterFeature registers the given feature into V2Ray.
// If feature is one of the following types, the corressponding feature in this Instance
// will be replaced: DNSClient, PolicyManager, Router, Dispatcher, InboundHandlerManager, OutboundHandlerManager.
func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error {
	switch feature.(type) {
	case DNSClient, *DNSClient:
		s.dnsClient.Set(instance.(DNSClient))
	case PolicyManager, *PolicyManager:
		s.policyManager.Set(instance.(PolicyManager))
	case Router, *Router:
		s.router.Set(instance.(Router))
	case Dispatcher, *Dispatcher:
		s.dispatcher.Set(instance.(Dispatcher))
	case InboundHandlerManager, *InboundHandlerManager:
		s.ihm.Set(instance.(InboundHandlerManager))
	case OutboundHandlerManager, *OutboundHandlerManager:
		s.ohm.Set(instance.(OutboundHandlerManager))
	case Clock, *Clock:
		s.clock.Set(instance.(Clock))
	case Commander, *Commander:
		s.cmd.Set(instance.(Commander))
	}
	s.access.Lock()
	defer s.access.Unlock()

	s.features = append(s.features, instance)
	if s.running {
		return instance.Start()
	}
	return nil
}

// GetFeature returns a feature that was registered in this Instance. Nil if not found.
// The returned Feature must implement common.HasType and whose type equals the given feature type.
func (s *Instance) GetFeature(featureType interface{}) Feature {
	for _, f := range s.features {
		if hasType, ok := f.(common.HasType); ok {
			if hasType.Type() == featureType {
				return f
			}
		}
	}
	return nil
}

// DNSClient returns the DNSClient used by this Instance. The returned DNSClient is always functional.
func (s *Instance) DNSClient() DNSClient {
	return &(s.dnsClient)
}

// PolicyManager returns the PolicyManager used by this Instance. The returned PolicyManager is always functional.
func (s *Instance) PolicyManager() PolicyManager {
	return &(s.policyManager)
}

// Router returns the Router used by this Instance. The returned Router is always functional.
func (s *Instance) Router() Router {
	return &(s.router)
}

// Dispatcher returns the Dispatcher used by this Instance. If Dispatcher was not registered before, the returned value doesn't work, although it is not nil.
func (s *Instance) Dispatcher() Dispatcher {
	return &(s.dispatcher)
}

// InboundHandlerManager returns the InboundHandlerManager used by this Instance. If InboundHandlerManager was not registered before, the returned value doesn't work.
func (s *Instance) InboundHandlerManager() InboundHandlerManager {
	return &(s.ihm)
}

// OutboundHandlerManager returns the OutboundHandlerManager used by this Instance. If OutboundHandlerManager was not registered before, the returned value doesn't work.
func (s *Instance) OutboundHandlerManager() OutboundHandlerManager {
	return &(s.ohm)
}

// Clock returns the Clock used by this Instance. The returned Clock is always functional.
func (s *Instance) Clock() Clock {
	return &(s.clock)
}

// Commander returns the Commander used by this Instance. The returned Commander is always functional.
func (s *Instance) Commander() Commander {
	return &(s.cmd)
}
<|endoftext|>