<commit_before>package main

import (
	"fmt"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/fatih/color"
	mkr "github.com/mackerelio/mackerel-client-go"
	"github.com/mackerelio/mkr/logger"
	"gopkg.in/urfave/cli.v1"
)

var commandAlerts = cli.Command{
	Name:      "alerts",
	Usage:     "Retrieve/Close alerts",
	ArgsUsage: "[--withClosed | -w] [--limit | -l]",
	Description: `
    Retrieve/Close alerts. With no subcommand specified, this will show all alerts.
    Requests APIs under "/api/v0/alerts". See https://mackerel.io/api-docs/entry/alerts .
`,
	Action: doAlertsRetrieve,
	Flags: []cli.Flag{
		cli.BoolFlag{Name: "withClosed, w", Usage: "Displays closed alerts."},
		cli.IntFlag{Name: "limit, l", Value: 100, Usage: "Number of alerts to display at one time"},
	},
	Subcommands: []cli.Command{
		{
			Name:      "list",
			Usage:     "list alerts",
			ArgsUsage: "[--service | -s <service>] [--host-status | -S <file>] [--color | -c]",
			Description: `
    Shows alerts in human-readable format.
`,
			Action: doAlertsList,
			Flags: []cli.Flag{
				cli.StringSliceFlag{
					Name:  "service, s",
					Value: &cli.StringSlice{},
					Usage: "Filters alerts by service. Multiple choices are allowed.",
				},
				cli.StringSliceFlag{
					Name:  "host-status, S",
					Value: &cli.StringSlice{},
					Usage: "Filters alerts by status of each host. Multiple choices are allowed.",
				},
				cli.BoolTFlag{Name: "color, c", Usage: "Colorize output. default: true"},
			},
		},
		{
			Name:      "close",
			Usage:     "close alerts",
			ArgsUsage: "<alertIds....>",
			Description: `
    Closes alerts. Multiple alert IDs can be specified.
`,
			Action: doAlertsClose,
			Flags: []cli.Flag{
				cli.StringFlag{Name: "reason, r", Value: "", Usage: "Reason of closing alert."},
				cli.BoolFlag{Name: "verbose, v", Usage: "Verbose output mode"},
			},
		},
	},
}

type alertSet struct {
	Alert   *mkr.Alert
	Host    *mkr.Host
	Monitor mkr.Monitor
}

func joinMonitorsAndHosts(client *mkr.Client, alerts []*mkr.Alert) []*alertSet {
	hostsJSON, err := client.FindHosts(&mkr.FindHostsParam{
		Statuses: []string{"working", "standby", "poweroff", "maintenance"},
	})
	logger.DieIf(err)

	hosts := map[string]*mkr.Host{}
	for _, host := range hostsJSON {
		hosts[host.ID] = host
	}

	monitorsJSON, err := client.FindMonitors()
	logger.DieIf(err)

	monitors := map[string]mkr.Monitor{}
	for _, monitor := range monitorsJSON {
		monitors[monitor.MonitorID()] = monitor
	}

	alertSets := []*alertSet{}
	for _, alert := range alerts {
		alertSets = append(
			alertSets,
			&alertSet{Alert: alert, Host: hosts[alert.HostID], Monitor: monitors[alert.MonitorID]},
		)
	}
	return alertSets
}

func formatJoinedAlert(alertSet *alertSet, colorize bool) string {
	const layout = "2006-01-02 15:04:05"

	host := alertSet.Host
	monitor := alertSet.Monitor
	alert := alertSet.Alert

	hostMsg := ""
	if host != nil {
		statusMsg := host.Status
		if host.IsRetired == true {
			statusMsg = "retired"
		}
		if colorize {
			switch statusMsg {
			case "working":
				statusMsg = color.BlueString("working")
			case "standby":
				statusMsg = color.GreenString("standby")
			case "poweroff":
				statusMsg = "poweroff"
			case "maintenance":
				statusMsg = color.YellowString("maintenance")
			}
		}
		hostMsg = fmt.Sprintf(" %s %s", host.Name, statusMsg)
		roleMsgs := []string{}
		for service, roles := range host.Roles {
			roleMsgs = append(roleMsgs, fmt.Sprintf("%s:%s", service, strings.Join(roles, ",")))
		}
		hostMsg += " [" + strings.Join(roleMsgs, ", ") + "]"
	}

	monitorMsg := ""
	if monitor != nil {
		switch m := monitor.(type) {
		case *mkr.MonitorConnectivity:
			monitorMsg = ""
		case *mkr.MonitorHostMetric:
			if alert.Status == "CRITICAL" && m.Critical != nil {
				monitorMsg = fmt.Sprintf("%s %.2f %s %.2f", m.Metric, alert.Value, m.Operator, *m.Critical)
			} else if alert.Status == "WARNING" && m.Warning != nil {
				monitorMsg = fmt.Sprintf("%s %.2f %s %.2f", m.Metric, alert.Value, m.Operator, *m.Warning)
			} else {
				monitorMsg = fmt.Sprintf("%s %.2f", m.Metric, alert.Value)
			}
		case *mkr.MonitorServiceMetric:
			if alert.Status == "CRITICAL" && m.Critical != nil {
				monitorMsg = fmt.Sprintf("%s %s %.2f %s %.2f", m.Service, m.Metric, alert.Value, m.Operator, *m.Critical)
			} else if alert.Status == "WARNING" && m.Warning != nil {
				monitorMsg = fmt.Sprintf("%s %s %.2f %s %.2f", m.Service, m.Metric, alert.Value, m.Operator, *m.Warning)
			} else {
				monitorMsg = fmt.Sprintf("%s %s %.2f", m.Service, m.Metric, alert.Value)
			}
		case *mkr.MonitorExternalHTTP:
			statusRegexp, _ := regexp.Compile("^[2345][0-9][0-9]$")
			switch alert.Status {
			case "CRITICAL":
				if statusRegexp.MatchString(alert.Message) && m.ResponseTimeCritical != nil {
					monitorMsg = fmt.Sprintf("%s %.2f > %.2f msec, status:%s", m.URL, alert.Value, *m.ResponseTimeCritical, alert.Message)
				} else {
					monitorMsg = fmt.Sprintf("%s %.2f msec, %s", m.URL, alert.Value, alert.Message)
				}
			case "WARNING":
				if statusRegexp.MatchString(alert.Message) && m.ResponseTimeWarning != nil {
					monitorMsg = fmt.Sprintf("%.2f > %.2f msec, status:%s", alert.Value, *m.ResponseTimeWarning, alert.Message)
				} else {
					monitorMsg = fmt.Sprintf("%.2f msec, %s", alert.Value, alert.Message)
				}
			default:
				monitorMsg = fmt.Sprintf("%.2f msec, status:%s", alert.Value, alert.Message)
			}
		case *mkr.MonitorExpression:
			expression := formatExpressionOneline(m.Expression)
			if alert.Status == "CRITICAL" && m.Critical != nil {
				monitorMsg = fmt.Sprintf("%s %.2f %s %.2f", expression, alert.Value, m.Operator, *m.Critical)
			} else if alert.Status == "WARNING" && m.Warning != nil {
				monitorMsg = fmt.Sprintf("%s %.2f %s %.2f", expression, alert.Value, m.Operator, *m.Warning)
			} else if alert.Status == "UNKNOWN" {
				monitorMsg = fmt.Sprintf("%s", expression)
			} else {
				monitorMsg = fmt.Sprintf("%s %.2f", expression, alert.Value)
			}
		default:
			monitorMsg = fmt.Sprintf("%s", monitor.MonitorType())
		}
		if monitorMsg == "" {
			monitorMsg = monitor.MonitorName()
		} else {
			monitorMsg = monitor.MonitorName() + " " + monitorMsg
		}
	}
	// If alert is caused by check monitoring, take monitorMsg from alert.message
	if alert.Type == "check" {
		monitorMsg = formatCheckMessage(alert.Message)
	}

	statusMsg := alert.Status
	if colorize {
		switch alert.Status {
		case "CRITICAL":
			statusMsg = color.RedString("CRITICAL")
		case "WARNING":
			statusMsg = color.YellowString("WARNING ")
		case "UNKNOWN":
			statusMsg = "UNKNOWN "
		}
	}
	return fmt.Sprintf("%s %s %s %s%s", alert.ID, time.Unix(alert.OpenedAt, 0).Format(layout), statusMsg, monitorMsg, hostMsg)
}

var expressionNewlinePattern = regexp.MustCompile(`\s*[\r\n]+\s*`)

func formatExpressionOneline(expr string) string {
	expr = strings.Trim(expressionNewlinePattern.ReplaceAllString(expr, " "), " ")
	return strings.Replace(strings.Replace(expr, "( ", "(", -1), " )", ")", -1)
}

func formatCheckMessage(msg string) string {
	truncated := false
	if index := strings.IndexAny(msg, "\n\r"); index != -1 {
		msg = msg[0:index]
		truncated = true
	}
	if runes := []rune(msg); len(runes) > 100 {
		msg = string(runes[0:100])
		truncated = true
	}
	if truncated {
		msg = msg + "..."
	}
	return msg
}

func doAlertsRetrieve(c *cli.Context) error {
	client := newMackerelFromContext(c)
	isClosed := c.Bool("withClosed")
	limit := c.Int("limit")
	count := 0

	if isClosed {
		alerts, id, err := client.FindWithClosedAlerts()
		logger.DieIf(err)
		if id != "" {
			for {
				nextAlerts := alerts
				count += len(alerts)
				if limit >= count {
					nextAlerts, id, err = client.FindWithClosedAlertsByNextID(id)
					logger.DieIf(err)
					alerts = append(alerts, nextAlerts...)
					if id == "" {
						break
					}
				} else {
					break
				}
				time.Sleep(1 * time.Second)
			}
		}

		PrettyPrintJSON(alerts)
	} else {
		alerts, id, err := client.FindAlerts()
		logger.DieIf(err)
		if id != "" {
			for {
				nextAlerts := alerts
				nextAlerts, id, err = client.FindAlertsByNextID(id)
				logger.DieIf(err)
				alerts = append(alerts, nextAlerts...)
				if id == "" {
					break
				}
				time.Sleep(1 * time.Second)
			}
		}

		PrettyPrintJSON(alerts)
	}
	return nil
}

func doAlertsList(c *cli.Context) error {
	filterServices := c.StringSlice("service")
	filterStatuses := c.StringSlice("host-status")
	client := newMackerelFromContext(c)
	isClosed := c.Bool("withClosed")
	limit := c.Int("limit")
	count := 0

	if isClosed {
		alerts, id, err := client.FindWithClosedAlerts()
		logger.DieIf(err)
		if id != "" {
			for {
				nextAlerts := alerts
				count += len(alerts)
				if limit >= count {
					nextAlerts, id, err = client.FindWithClosedAlertsByNextID(id)
					logger.DieIf(err)
					alerts = append(alerts, nextAlerts...)
					if id == "" {
						break
					}
				} else {
					break
				}
				time.Sleep(1 * time.Second)
			}
		}
		joinedAlerts := joinMonitorsAndHosts(client, alerts)
		for _, joinAlert := range joinedAlerts {
			if len(filterServices) > 0 {
				found := false
				for _, filterService := range filterServices {
					if joinAlert.Host != nil {
						if _, ok := joinAlert.Host.Roles[filterService]; ok {
							found = true
						}
					} else {
						var service string
						if m, ok := joinAlert.Monitor.(*mkr.MonitorServiceMetric); ok {
							service = m.Service
						} else if m, ok := joinAlert.Monitor.(*mkr.MonitorExternalHTTP); ok {
							service = m.Service
						}
						found = service == filterService
					}
				}
				if !found {
					continue
				}
			}
			if len(filterStatuses) > 0 {
				found := false
				for _, filterStatus := range filterStatuses {
					if joinAlert.Host != nil && joinAlert.Host.Status == filterStatus {
						found = true
					}
				}
				if !found {
					continue
				}
			}
			fmt.Fprintln(color.Output, formatJoinedAlert(joinAlert, c.BoolT("color")))
		}
	} else {
		alerts, id, err := client.FindAlerts()
		logger.DieIf(err)
		if id != "" {
			for {
				nextAlerts := alerts
				nextAlerts, id, err = client.FindAlertsByNextID(id)
				logger.DieIf(err)
				alerts = append(alerts, nextAlerts...)
				if id == "" {
					break
				}
				time.Sleep(1 * time.Second)
			}
		}
		joinedAlerts := joinMonitorsAndHosts(client, alerts)
		for _, joinAlert := range joinedAlerts {
			if len(filterServices) > 0 {
				found := false
				for _, filterService := range filterServices {
					if joinAlert.Host != nil {
						if _, ok := joinAlert.Host.Roles[filterService]; ok {
							found = true
						}
					} else {
						var service string
						if m, ok := joinAlert.Monitor.(*mkr.MonitorServiceMetric); ok {
							service = m.Service
						} else if m, ok := joinAlert.Monitor.(*mkr.MonitorExternalHTTP); ok {
							service = m.Service
						}
						found = service == filterService
					}
				}
				if !found {
					continue
				}
			}
			if len(filterStatuses) > 0 {
				found := false
				for _, filterStatus := range filterStatuses {
					if joinAlert.Host != nil && joinAlert.Host.Status == filterStatus {
						found = true
					}
				}
				if !found {
					continue
				}
			}
			fmt.Fprintln(color.Output, formatJoinedAlert(joinAlert, c.BoolT("color")))
		}
	}
	return nil
}

func doAlertsClose(c *cli.Context) error {
	isVerbose := c.Bool("verbose")
	argAlertIDs := c.Args()
	reason := c.String("reason")

	if len(argAlertIDs) < 1 {
		cli.ShowCommandHelp(c, "alerts")
		os.Exit(1)
	}

	client := newMackerelFromContext(c)
	for _, alertID := range argAlertIDs {
		alert, err := client.CloseAlert(alertID, reason)
		logger.DieIf(err)

		logger.Log("Alert closed", alertID)
		if isVerbose == true {
			PrettyPrintJSON(alert)
		}
	}
	return nil
}
<commit_msg>Fixed alerts list<commit_after>package main

import (
	"fmt"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/fatih/color"
	mkr "github.com/mackerelio/mackerel-client-go"
	"github.com/mackerelio/mkr/logger"
	"gopkg.in/urfave/cli.v1"
)

var commandAlerts = cli.Command{
	Name:      "alerts",
	Usage:     "Retrieve/Close alerts",
	ArgsUsage: "[--withClosed | -w] [--limit | -l]",
	Description: `
    Retrieve/Close alerts. With no subcommand specified, this will show all alerts.
    Requests APIs under "/api/v0/alerts". See https://mackerel.io/api-docs/entry/alerts .
`,
	Action: doAlertsRetrieve,
	Flags: []cli.Flag{
		cli.BoolFlag{Name: "withClosed, w", Usage: "Displays closed alerts."},
		cli.IntFlag{Name: "limit, l", Value: 100, Usage: "Number of alerts to display at one time"},
	},
	Subcommands: []cli.Command{
		{
			Name:      "list",
			Usage:     "list alerts",
			ArgsUsage: "[--service | -s <service>] [--host-status | -S <file>] [--color | -c]",
			Description: `
    Shows alerts in human-readable format.
`,
			Action: doAlertsList,
			Flags: []cli.Flag{
				cli.StringSliceFlag{
					Name:  "service, s",
					Value: &cli.StringSlice{},
					Usage: "Filters alerts by service. Multiple choices are allowed.",
				},
				cli.StringSliceFlag{
					Name:  "host-status, S",
					Value: &cli.StringSlice{},
					Usage: "Filters alerts by status of each host. Multiple choices are allowed.",
				},
				cli.BoolTFlag{Name: "color, c", Usage: "Colorize output. default: true"},
				cli.BoolFlag{Name: "withClosed, w", Usage: "Displays closed alerts."},
				cli.IntFlag{Name: "limit, l", Value: 100, Usage: "Number of alerts to display at one time"},
			},
		},
		{
			Name:      "close",
			Usage:     "close alerts",
			ArgsUsage: "<alertIds....>",
			Description: `
    Closes alerts. Multiple alert IDs can be specified.
`,
			Action: doAlertsClose,
			Flags: []cli.Flag{
				cli.StringFlag{Name: "reason, r", Value: "", Usage: "Reason of closing alert."},
				cli.BoolFlag{Name: "verbose, v", Usage: "Verbose output mode"},
			},
		},
	},
}

type alertSet struct {
	Alert   *mkr.Alert
	Host    *mkr.Host
	Monitor mkr.Monitor
}

func joinMonitorsAndHosts(client *mkr.Client, alerts []*mkr.Alert) []*alertSet {
	hostsJSON, err := client.FindHosts(&mkr.FindHostsParam{
		Statuses: []string{"working", "standby", "poweroff", "maintenance"},
	})
	logger.DieIf(err)

	hosts := map[string]*mkr.Host{}
	for _, host := range hostsJSON {
		hosts[host.ID] = host
	}

	monitorsJSON, err := client.FindMonitors()
	logger.DieIf(err)

	monitors := map[string]mkr.Monitor{}
	for _, monitor := range monitorsJSON {
		monitors[monitor.MonitorID()] = monitor
	}

	alertSets := []*alertSet{}
	for _, alert := range alerts {
		alertSets = append(
			alertSets,
			&alertSet{Alert: alert, Host: hosts[alert.HostID], Monitor: monitors[alert.MonitorID]},
		)
	}
	return alertSets
}

func formatJoinedAlert(alertSet *alertSet, colorize bool) string {
	const layout = "2006-01-02 15:04:05"

	host := alertSet.Host
	monitor := alertSet.Monitor
	alert := alertSet.Alert

	hostMsg := ""
	if host != nil {
		statusMsg := host.Status
		if host.IsRetired == true {
			statusMsg = "retired"
		}
		if colorize {
			switch statusMsg {
			case "working":
				statusMsg = color.BlueString("working")
			case "standby":
				statusMsg = color.GreenString("standby")
			case "poweroff":
				statusMsg = "poweroff"
			case "maintenance":
				statusMsg = color.YellowString("maintenance")
			}
		}
		hostMsg = fmt.Sprintf(" %s %s", host.Name, statusMsg)
		roleMsgs := []string{}
		for service, roles := range host.Roles {
			roleMsgs = append(roleMsgs, fmt.Sprintf("%s:%s", service, strings.Join(roles, ",")))
		}
		hostMsg += " [" + strings.Join(roleMsgs, ", ") + "]"
	}

	monitorMsg := ""
	if monitor != nil {
		switch m := monitor.(type) {
		case *mkr.MonitorConnectivity:
			monitorMsg = ""
		case *mkr.MonitorHostMetric:
			if alert.Status == "CRITICAL" && m.Critical != nil {
				monitorMsg = fmt.Sprintf("%s %.2f %s %.2f", m.Metric, alert.Value, m.Operator, *m.Critical)
			} else if alert.Status == "WARNING" && m.Warning != nil {
				monitorMsg = fmt.Sprintf("%s %.2f %s %.2f", m.Metric, alert.Value, m.Operator, *m.Warning)
			} else {
				monitorMsg = fmt.Sprintf("%s %.2f", m.Metric, alert.Value)
			}
		case *mkr.MonitorServiceMetric:
			if alert.Status == "CRITICAL" && m.Critical != nil {
				monitorMsg = fmt.Sprintf("%s %s %.2f %s %.2f", m.Service, m.Metric, alert.Value, m.Operator, *m.Critical)
			} else if alert.Status == "WARNING" && m.Warning != nil {
				monitorMsg = fmt.Sprintf("%s %s %.2f %s %.2f", m.Service, m.Metric, alert.Value, m.Operator, *m.Warning)
			} else {
				monitorMsg = fmt.Sprintf("%s %s %.2f", m.Service, m.Metric, alert.Value)
			}
		case *mkr.MonitorExternalHTTP:
			statusRegexp, _ := regexp.Compile("^[2345][0-9][0-9]$")
			switch alert.Status {
			case "CRITICAL":
				if statusRegexp.MatchString(alert.Message) && m.ResponseTimeCritical != nil {
					monitorMsg = fmt.Sprintf("%s %.2f > %.2f msec, status:%s", m.URL, alert.Value, *m.ResponseTimeCritical, alert.Message)
				} else {
					monitorMsg = fmt.Sprintf("%s %.2f msec, %s", m.URL, alert.Value, alert.Message)
				}
			case "WARNING":
				if statusRegexp.MatchString(alert.Message) && m.ResponseTimeWarning != nil {
					monitorMsg = fmt.Sprintf("%.2f > %.2f msec, status:%s", alert.Value, *m.ResponseTimeWarning, alert.Message)
				} else {
					monitorMsg = fmt.Sprintf("%.2f msec, %s", alert.Value, alert.Message)
				}
			default:
				monitorMsg = fmt.Sprintf("%.2f msec, status:%s", alert.Value, alert.Message)
			}
		case *mkr.MonitorExpression:
			expression := formatExpressionOneline(m.Expression)
			if alert.Status == "CRITICAL" && m.Critical != nil {
				monitorMsg = fmt.Sprintf("%s %.2f %s %.2f", expression, alert.Value, m.Operator, *m.Critical)
			} else if alert.Status == "WARNING" && m.Warning != nil {
				monitorMsg = fmt.Sprintf("%s %.2f %s %.2f", expression, alert.Value, m.Operator, *m.Warning)
			} else if alert.Status == "UNKNOWN" {
				monitorMsg = fmt.Sprintf("%s", expression)
			} else {
				monitorMsg = fmt.Sprintf("%s %.2f", expression, alert.Value)
			}
		default:
			monitorMsg = fmt.Sprintf("%s", monitor.MonitorType())
		}
		if monitorMsg == "" {
			monitorMsg = monitor.MonitorName()
		} else {
			monitorMsg = monitor.MonitorName() + " " + monitorMsg
		}
	}
	// If alert is caused by check monitoring, take monitorMsg from alert.message
	if alert.Type == "check" {
		monitorMsg = formatCheckMessage(alert.Message)
	}

	statusMsg := alert.Status
	if colorize {
		switch alert.Status {
		case "CRITICAL":
			statusMsg = color.RedString("CRITICAL")
		case "WARNING":
			statusMsg = color.YellowString("WARNING ")
		case "UNKNOWN":
			statusMsg = "UNKNOWN "
		}
	}
	return fmt.Sprintf("%s %s %s %s%s", alert.ID, time.Unix(alert.OpenedAt, 0).Format(layout), statusMsg, monitorMsg, hostMsg)
}

var expressionNewlinePattern = regexp.MustCompile(`\s*[\r\n]+\s*`)

func formatExpressionOneline(expr string) string {
	expr = strings.Trim(expressionNewlinePattern.ReplaceAllString(expr, " "), " ")
	return strings.Replace(strings.Replace(expr, "( ", "(", -1), " )", ")", -1)
}

func formatCheckMessage(msg string) string {
	truncated := false
	if index := strings.IndexAny(msg, "\n\r"); index != -1 {
		msg = msg[0:index]
		truncated = true
	}
	if runes := []rune(msg); len(runes) > 100 {
		msg = string(runes[0:100])
		truncated = true
	}
	if truncated {
		msg = msg + "..."
	}
	return msg
}

func doAlertsRetrieve(c *cli.Context) error {
	client := newMackerelFromContext(c)
	isClosed := c.Bool("withClosed")
	limit := c.Int("limit")
	count := 0

	if isClosed {
		alerts, id, err := client.FindWithClosedAlerts()
		logger.DieIf(err)
		if id != "" {
			for {
				nextAlerts := alerts
				count += len(alerts)
				if limit >= count {
					nextAlerts, id, err = client.FindWithClosedAlertsByNextID(id)
					logger.DieIf(err)
					alerts = append(alerts, nextAlerts...)
					if id == "" {
						break
					}
				} else {
					break
				}
				time.Sleep(1 * time.Second)
			}
		}

		PrettyPrintJSON(alerts)
	} else {
		alerts, id, err := client.FindAlerts()
		logger.DieIf(err)
		if id != "" {
			for {
				nextAlerts := alerts
				nextAlerts, id, err = client.FindAlertsByNextID(id)
				logger.DieIf(err)
				alerts = append(alerts, nextAlerts...)
				if id == "" {
					break
				}
				time.Sleep(1 * time.Second)
			}
		}

		PrettyPrintJSON(alerts)
	}
	return nil
}

func doAlertsList(c *cli.Context) error {
	filterServices := c.StringSlice("service")
	filterStatuses := c.StringSlice("host-status")
	client := newMackerelFromContext(c)
	isClosed := c.Bool("withClosed")
	limit := c.Int("limit")
	count := 0

	if isClosed {
		alerts, id, err := client.FindWithClosedAlerts()
		logger.DieIf(err)
		if id != "" {
			for {
				nextAlerts := alerts
				count += len(alerts)
				if limit >= count {
					nextAlerts, id, err = client.FindWithClosedAlertsByNextID(id)
					logger.DieIf(err)
					alerts = append(alerts, nextAlerts...)
					if id == "" {
						break
					}
				} else {
					break
				}
				time.Sleep(1 * time.Second)
			}
		}
		joinedAlerts := joinMonitorsAndHosts(client, alerts)
		for _, joinAlert := range joinedAlerts {
			if len(filterServices) > 0 {
				found := false
				for _, filterService := range filterServices {
					if joinAlert.Host != nil {
						if _, ok := joinAlert.Host.Roles[filterService]; ok {
							found = true
						}
					} else {
						var service string
						if m, ok := joinAlert.Monitor.(*mkr.MonitorServiceMetric); ok {
							service = m.Service
						} else if m, ok := joinAlert.Monitor.(*mkr.MonitorExternalHTTP); ok {
							service = m.Service
						}
						found = service == filterService
					}
				}
				if !found {
					continue
				}
			}
			if len(filterStatuses) > 0 {
				found := false
				for _, filterStatus := range filterStatuses {
					if joinAlert.Host != nil && joinAlert.Host.Status == filterStatus {
						found = true
					}
				}
				if !found {
					continue
				}
			}
			fmt.Fprintln(color.Output, formatJoinedAlert(joinAlert, c.BoolT("color")))
		}
	} else {
		alerts, id, err := client.FindAlerts()
		logger.DieIf(err)
		if id != "" {
			for {
				nextAlerts := alerts
				nextAlerts, id, err = client.FindAlertsByNextID(id)
				logger.DieIf(err)
				alerts = append(alerts, nextAlerts...)
				if id == "" {
					break
				}
				time.Sleep(1 * time.Second)
			}
		}
		joinedAlerts := joinMonitorsAndHosts(client, alerts)
		for _, joinAlert := range joinedAlerts {
			if len(filterServices) > 0 {
				found := false
				for _, filterService := range filterServices {
					if joinAlert.Host != nil {
						if _, ok := joinAlert.Host.Roles[filterService]; ok {
							found = true
						}
					} else {
						var service string
						if m, ok := joinAlert.Monitor.(*mkr.MonitorServiceMetric); ok {
							service = m.Service
						} else if m, ok := joinAlert.Monitor.(*mkr.MonitorExternalHTTP); ok {
							service = m.Service
						}
						found = service == filterService
					}
				}
				if !found {
					continue
				}
			}
			if len(filterStatuses) > 0 {
				found := false
				for _, filterStatus := range filterStatuses {
					if joinAlert.Host != nil && joinAlert.Host.Status == filterStatus {
						found = true
					}
				}
				if !found {
					continue
				}
			}
			fmt.Fprintln(color.Output, formatJoinedAlert(joinAlert, c.BoolT("color")))
		}
	}
	return nil
}

func doAlertsClose(c *cli.Context) error {
	isVerbose := c.Bool("verbose")
	argAlertIDs := c.Args()
	reason := c.String("reason")

	if len(argAlertIDs) < 1 {
		cli.ShowCommandHelp(c, "alerts")
		os.Exit(1)
	}

	client := newMackerelFromContext(c)
	for _, alertID := range argAlertIDs {
		alert, err := client.CloseAlert(alertID, reason)
		logger.DieIf(err)

		logger.Log("Alert closed", alertID)
		if isVerbose == true {
			PrettyPrintJSON(alert)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package anaconda

import (
	"net/url"
	"strconv"
)

func (a TwitterApi) GetUsersLookup(usernames string, v url.Values) (u []User, err error) {
	v = cleanValues(v)
	v.Set("screen_name", usernames)
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/users/lookup.json", v, &u, _GET, response_ch}
	return u, (<-response_ch).err
}

func (a TwitterApi) GetUsersLookupByIds(ids []int64, v url.Values) (u []User, err error) {
	var pids string
	for w, i := range ids {
		//pids += strconv.Itoa(i)
		pids += strconv.FormatInt(i, 10)
		if w != len(ids)-1 {
			pids += ","
		}
	}
	v = cleanValues(v)
	v.Set("user_id", pids)
	err = a.apiGet(BaseUrl + "/users/lookup.json", v, &u)
	return
}

func (a TwitterApi) GetUsersShow(username string, v url.Values) (u User, err error) {
	v = cleanValues(v)
	v.Set("screen_name", username)
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/users/show.json", v, &u, _GET, response_ch}
	return u, (<-response_ch).err
}

func (a TwitterApi) GetUsersShowById(id int64, v url.Values) (u User, err error) {
	v = cleanValues(v)
	v.Set("user_id", strconv.FormatInt(id, 10))
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/users/show.json", v, &u, _GET, response_ch}
	return u, (<-response_ch).err
}
<commit_msg>Add user search to Anaconda.<commit_after>package anaconda

import (
	"net/url"
	"strconv"
)

func (a TwitterApi) GetUsersLookup(usernames string, v url.Values) (u []User, err error) {
	v = cleanValues(v)
	v.Set("screen_name", usernames)
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/users/lookup.json", v, &u, _GET, response_ch}
	return u, (<-response_ch).err
}

func (a TwitterApi) GetUsersLookupByIds(ids []int64, v url.Values) (u []User, err error) {
	var pids string
	for w, i := range ids {
		//pids += strconv.Itoa(i)
		pids += strconv.FormatInt(i, 10)
		if w != len(ids)-1 {
			pids += ","
		}
	}
	v = cleanValues(v)
	v.Set("user_id", pids)
	err = a.apiGet(BaseUrl + "/users/lookup.json", v, &u)
	return
}

func (a TwitterApi) GetUsersShow(username string, v url.Values) (u User, err error) {
	v = cleanValues(v)
	v.Set("screen_name", username)
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/users/show.json", v, &u, _GET, response_ch}
	return u, (<-response_ch).err
}

func (a TwitterApi) GetUsersShowById(id int64, v url.Values) (u User, err error) {
	v = cleanValues(v)
	v.Set("user_id", strconv.FormatInt(id, 10))
	response_ch := make(chan response)
	a.queryQueue <- query{BaseUrl + "/users/show.json", v, &u, _GET, response_ch}
	return u, (<-response_ch).err
}

func (a TwitterApi) GetUserSearch(searchTerm string, v url.Values) (u []TwitterUser, err error) {
    v = cleanValues(v)
    v.Set("q", searchTerm)
    // Set other values before calling this method:
    // page, count, include_entities
    err = a.apiGet("http://api.twitter.com/1.1/users/search.json", v, &u)
    return
}

<|endoftext|>


<commit_before>package goinsta

import (
	"encoding/json"
)

type userResp struct {
	Status string `json:"status"`
	User   User   `json:"user"`
}

type Users struct {
	inst *Instagram
}

// User is the representation of instagram's user profile
type User struct {
	//Feed *Feed
	//Followers *Followers
	//Following *Following
	//Status *Friendship
	//Story *Story
	//Messages *Messages

	ID                         int64        `json:"pk"`
	Username                   string       `json:"username"`
	FullName                   string       `json:"full_name"`
	Biography                  string       `json:"biography"`
	ProfilePicURL              string       `json:"profile_pic_url"`
	Email                      string       `json:"email"`
	PhoneNumber                string       `json:"phone_number"`
	IsBusiness                 bool         `json:"is_business"`
	Gender                     int          `json:"gender"`
	ProfilePicID               string       `json:"profile_pic_id"`
	HasAnonymousProfilePicture bool         `json:"has_anonymous_profile_picture"`
	IsPrivate                  bool         `json:"is_private"`
	IsVerified                 bool         `json:"is_verified"`
	MediaCount                 int          `json:"media_count"`
	FollowerCount              int          `json:"follower_count"`
	FollowingCount             int          `json:"following_count"`
	GeoMediaCount              int          `json:"geo_media_count"`
	ExternalURL                string       `json:"external_url"`
	HasBiographyTranslation    bool         `json:"has_biography_translation"`
	ExternalLynxURL            string       `json:"external_lynx_url"`
	HdProfilePicURLInfo        PicURLInfo   `json:"hd_profile_pic_url_info"`
	HdProfilePicVersions       []PicURLInfo `json:"hd_profile_pic_versions"`
	UsertagsCount              int          `json:"usertags_count"`
	HasChaining                bool         `json:"has_chaining"`
	IsFavorite                 bool         `json:"is_favorite"`
	ReelAutoArchive            string       `json:"reel_auto_archive"`
	School                     School       `json:"school"`
	PublicEmail                string       `json:"public_email"`
	PublicPhoneNumber          string       `json:"public_phone_number"`
	PublicPhoneCountryCode     string       `json:"public_phone_country_code"`
	ContactPhoneNumber         string       `json:"contact_phone_number"`
	CityID                     int64        `json:"city_id"`
	CityName                   string       `json:"city_name"`
	AddressStreet              string       `json:"address_street"`
	DirectMessaging            string       `json:"direct_messaging"`
	Latitude                   float64      `json:"latitude"`
	Longitude                  float64      `json:"longitude"`
	Category                   string       `json:"category"`
	BusinessContactMethod      string       `json:"business_contact_method"`
	IsCallToActionEnabled      bool         `json:"is_call_to_action_enabled"`
	FbPageCallToActionID       string       `json:"fb_page_call_to_action_id"`
	Zip                        string       `json:"zip"`
}

// NewUsers creates new users struct to interact with user functions.
//
// ...
func NewUsers(inst *Instagram) *Users {
	users := &Users{inst: inst}

	return users
}

// SetInstagram sets new instagram to user structure
func (users *Users) SetInstagram(inst *Instagram) {
	users.inst = inst
}

// ByName stores name information in *User structure (Instagram.User)
//
// Last *User data will be remove and replace by the new one
func (users *Users) ByName(name string) (*User, error) {
	body, err := users.inst.sendSimpleRequest(urlUserByName, name)
	if err == nil {
		resp := userResp{}
		err = json.Unmarshal(body, &resp)
		if err == nil {
			user := &resp.User
			return user, nil
		}
	}
	return nil, err
}
<commit_msg>Added ByID function<commit_after>package goinsta

import (
	"encoding/json"
	"fmt"
)

type Users struct {
	inst *Instagram
}

// NewUsers creates new users struct to interact with user functions.
//
// ...
func NewUsers(inst *Instagram) *Users {
	users := &Users{inst: inst}

	return users
}

// SetInstagram sets new instagram to user structure
func (users *Users) SetInstagram(inst *Instagram) {
	users.inst = inst
}

// ByName return a *User structure parsed by username
func (users *Users) ByName(name string) (*User, error) {
	body, err := users.inst.sendSimpleRequest(urlUserByName, name)
	if err == nil {
		resp := userResp{}
		err = json.Unmarshal(body, &resp)
		if err == nil {
			user := &resp.User
			return user, nil
		}
	}
	return nil, err
}

// ByID returns a *User structure parsed by user id
func (users *Users) ByID(id int64) (*User, error) {
	data, err := users.inst.prepareData()
	if err != nil {
		return nil, err
	}

	body, err := users.inst.sendRequest(
		&reqOptions{
			Endpoint: fmt.Sprintf(urlUserById, id),
			Query:    generateSignature(data),
		},
	)
	if err == nil {
		user := userResp{}
		err = json.Unmarshal(body, &user)
		return &user.User, err
	}
	return nil, err
}

type userResp struct {
	Status string `json:"status"`
	User   User   `json:"user"`
}

// User is the representation of instagram's user profile
type User struct {
	//Feed *Feed
	//Followers *Followers
	//Following *Following
	//Status *Friendship
	//Story *Story
	//Messages *Messages

	ID                         int64        `json:"pk"`
	Username                   string       `json:"username"`
	FullName                   string       `json:"full_name"`
	Biography                  string       `json:"biography"`
	ProfilePicURL              string       `json:"profile_pic_url"`
	Email                      string       `json:"email"`
	PhoneNumber                string       `json:"phone_number"`
	IsBusiness                 bool         `json:"is_business"`
	Gender                     int          `json:"gender"`
	ProfilePicID               string       `json:"profile_pic_id"`
	HasAnonymousProfilePicture bool         `json:"has_anonymous_profile_picture"`
	IsPrivate                  bool         `json:"is_private"`
	IsVerified                 bool         `json:"is_verified"`
	MediaCount                 int          `json:"media_count"`
	FollowerCount              int          `json:"follower_count"`
	FollowingCount             int          `json:"following_count"`
	GeoMediaCount              int          `json:"geo_media_count"`
	ExternalURL                string       `json:"external_url"`
	HasBiographyTranslation    bool         `json:"has_biography_translation"`
	ExternalLynxURL            string       `json:"external_lynx_url"`
	HdProfilePicURLInfo        PicURLInfo   `json:"hd_profile_pic_url_info"`
	HdProfilePicVersions       []PicURLInfo `json:"hd_profile_pic_versions"`
	UsertagsCount              int          `json:"usertags_count"`
	HasChaining                bool         `json:"has_chaining"`
	IsFavorite                 bool         `json:"is_favorite"`
	ReelAutoArchive            string       `json:"reel_auto_archive"`
	School                     School       `json:"school"`
	PublicEmail                string       `json:"public_email"`
	PublicPhoneNumber          string       `json:"public_phone_number"`
	PublicPhoneCountryCode     string       `json:"public_phone_country_code"`
	ContactPhoneNumber         string       `json:"contact_phone_number"`
	CityID                     int64        `json:"city_id"`
	CityName                   string       `json:"city_name"`
	AddressStreet              string       `json:"address_street"`
	DirectMessaging            string       `json:"direct_messaging"`
	Latitude                   float64      `json:"latitude"`
	Longitude                  float64      `json:"longitude"`
	Category                   string       `json:"category"`
	BusinessContactMethod      string       `json:"business_contact_method"`
	IsCallToActionEnabled      bool         `json:"is_call_to_action_enabled"`
	FbPageCallToActionID       string       `json:"fb_page_call_to_action_id"`
	Zip                        string       `json:"zip"`
}
<|endoftext|>


<commit_before>package handlers_test

import (
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"

	"github.com/konjoot/reeky/test"

	. "github.com/konjoot/reeky/errors"
	. "github.com/konjoot/reeky/handlers"
	. "github.com/konjoot/reeky/test/matchers"
	. "github.com/konjoot/reeky/test/mocks"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Handlers", func() {
	var (
		err      error
		body     io.Reader
		fMap     map[string]string
		form     interface{}
		entity   *ResourceMock
		response *httptest.ResponseRecorder
	)

	Describe("Create", func() {
		BeforeEach(func() {
			fMap = map[string]string{"Name": "Test", "Desc": "TestDesc"}
			body = test.NewJsonReader(fMap)
			form = test.Form()
			response = httptest.NewRecorder()
		})

		JustBeforeEach(func() {
			request, _ := http.NewRequest("POST", "/tests", body)
			context := test.Context(request, response, entity)
			err = Create(context)
		})

		Describe("positive case", func() {
			BeforeEach(func() {
				entity = &ResourceMock{F: form}
			})

			It("should create entity and return right response", func() {
				Expect(err).To(BeNil())
				Expect(fMap).To(BeBindedTo(entity))
				Expect(entity).To(BeCreated())
				Expect(response.Code).To(Equal(201))
				Expect(response.Header().Get("Location")).To(Equal(entity.Url()))
				Expect(response.Body.Len()).To(BeZero())
			})
		})

		Describe("negative case (Error while saving)", func() {
			BeforeEach(func() {
				entity = &ResourceMock{F: form, Invalid: true}
			})

			It("should not create entity and return ConflictError", func() {
				Expect(err).To(BeTypeOf(&ConflictError{}))
				Expect(fMap).To(BeBindedTo(entity))
				Expect(entity).NotTo(BeCreated())
				Expect(response.Code).NotTo(Equal(201))
				Expect(response.Header().Get("Location")).To(BeEmpty())
				Expect(response.Body.Len()).To(BeZero())
			})
		})

		Describe("negative case (Syntax error)", func() {
			BeforeEach(func() {
				body = test.NewStringReader("bad request")
				entity = &ResourceMock{F: form}
			})

			It("should not create entity and return SyntaxError error", func() {
				Expect(err).To(BeTypeOf(&json.SyntaxError{}))
				Expect(fMap).NotTo(BeBindedTo(entity))
				Expect(entity).NotTo(BeCreated())
				Expect(response.Code).NotTo(Equal(201))
				Expect(response.Header().Get("Location")).To(BeEmpty())
				Expect(response.Body.Len()).To(BeZero())
			})
		})

		Describe("negative case (UnsupportedMediaType error)", func() {
			JustBeforeEach(func() {
				response = httptest.NewRecorder()
				entity = &ResourceMock{F: form}
				request, _ := http.NewRequest("POST", "/tests", body)
				context := test.BadContext(request, response, entity)
				err = Create(context)
			})

			It("should not create entity and return UnsupportedMediaType error", func() {
				Expect(err).To(Equal(errors.New("echo ⇒ unsupported media type")))
				Expect(fMap).NotTo(BeBindedTo(entity))
				Expect(entity).NotTo(BeCreated())
				Expect(response.Code).NotTo(Equal(201))
				Expect(response.Header().Get("Location")).NotTo(Equal(entity.Url()))
				Expect(response.Body.Len()).To(BeZero())
			})
		})

		Describe("negative case (Failed Dependency)", func() {
			JustBeforeEach(func() {
				response = httptest.NewRecorder()
				request, _ := http.NewRequest("POST", "/tests", body)
				context := test.Context(request, response, nil)
				err = Create(context)
			})

			It("should not create entity and return EmptyResourceError", func() {
				Expect(err).To(BeTypeOf(&EmptyResourceError{}))
				Expect(response.Code).NotTo(Equal(201))
				Expect(response.Header().Get("Location")).To(BeEmpty())
				Expect(response.Body.Len()).To(BeZero())
			})
		})
	})
})
<commit_msg>refactoring a little in create-handler test<commit_after>package handlers_test

import (
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"

	"github.com/konjoot/reeky/test"

	. "github.com/konjoot/reeky/errors"
	. "github.com/konjoot/reeky/handlers"
	. "github.com/konjoot/reeky/test/matchers"
	. "github.com/konjoot/reeky/test/mocks"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Handlers", func() {
	var (
		err      error
		body     io.Reader
		fMap     map[string]string
		form     interface{}
		entity   *ResourceMock
		response *httptest.ResponseRecorder
	)

	Describe("Create", func() {
		BeforeEach(func() {
			fMap = map[string]string{"Name": "Test", "Desc": "TestDesc"}
			body = test.NewJsonReader(fMap)
			form = &test.Form{}
			response = httptest.NewRecorder()
		})

		JustBeforeEach(func() {
			request, _ := http.NewRequest("POST", "/tests", body)
			context := test.Context(request, response, entity)
			err = Create(context)
		})

		Describe("positive case", func() {
			BeforeEach(func() {
				entity = &ResourceMock{F: form}
			})

			It("should create entity and return right response", func() {
				Expect(err).To(BeNil())
				Expect(fMap).To(BeBindedTo(entity))
				Expect(entity).To(BeCreated())
				Expect(response.Code).To(Equal(201))
				Expect(response.Header().Get("Location")).To(Equal(entity.Url()))
				Expect(response.Body.Len()).To(BeZero())
			})
		})

		Describe("negative case (Error while saving)", func() {
			BeforeEach(func() {
				entity = &ResourceMock{F: form, Invalid: true}
			})

			It("should not create entity and return ConflictError", func() {
				Expect(err).To(BeTypeOf(NewConflictError()))
				Expect(fMap).To(BeBindedTo(entity))
				Expect(entity).NotTo(BeCreated())
				Expect(response.Code).NotTo(Equal(201))
				Expect(response.Header().Get("Location")).To(BeEmpty())
				Expect(response.Body.Len()).To(BeZero())
			})
		})

		Describe("negative case (Syntax error)", func() {
			BeforeEach(func() {
				body = test.NewStringReader("bad request")
				entity = &ResourceMock{F: form}
			})

			It("should not create entity and return SyntaxError error", func() {
				Expect(err).To(BeTypeOf(&json.SyntaxError{}))
				Expect(fMap).NotTo(BeBindedTo(entity))
				Expect(entity).NotTo(BeCreated())
				Expect(response.Code).NotTo(Equal(201))
				Expect(response.Header().Get("Location")).To(BeEmpty())
				Expect(response.Body.Len()).To(BeZero())
			})
		})

		Describe("negative case (UnsupportedMediaType error)", func() {
			JustBeforeEach(func() {
				response = httptest.NewRecorder()
				entity = &ResourceMock{F: form}
				request, _ := http.NewRequest("POST", "/tests", body)
				context := test.BadContext(request, response, entity)
				err = Create(context)
			})

			It("should not create entity and return UnsupportedMediaType error", func() {
				Expect(err).To(Equal(errors.New("echo ⇒ unsupported media type")))
				Expect(fMap).NotTo(BeBindedTo(entity))
				Expect(entity).NotTo(BeCreated())
				Expect(response.Code).NotTo(Equal(201))
				Expect(response.Header().Get("Location")).NotTo(Equal(entity.Url()))
				Expect(response.Body.Len()).To(BeZero())
			})
		})

		Describe("negative case (Failed Dependency)", func() {
			JustBeforeEach(func() {
				response = httptest.NewRecorder()
				request, _ := http.NewRequest("POST", "/tests", body)
				context := test.Context(request, response, nil)
				err = Create(context)
			})

			It("should not create entity and return EmptyResourceError", func() {
				Expect(err).To(BeTypeOf(NewEmptyResourceError()))
				Expect(response.Code).NotTo(Equal(201))
				Expect(response.Header().Get("Location")).To(BeEmpty())
				Expect(response.Body.Len()).To(BeZero())
			})
		})
	})
})
<|endoftext|>


<commit_before>/*
Released under MIT License <http://www.opensource.org/licenses/mit-license.php
Copyright (C) ITsysCOM GmbH. All Rights Reserved.

Provides Asterisk ARI connector from Go programming language.
*/

package aringo

import (
	"net/http"
	"sync"
	"time"

	"golang.org/x/net/websocket"
)

// successive Fibonacci numbers.
func Fib() func() time.Duration {
	a, b := 0, 1
	return func() time.Duration {
		a, b = b, a+b
		return time.Duration(a) * time.Second
	}
}

func NewARInGO(appName, httpUrl, wsUrl, wsOrigin string) *ARInGO {
	return &ARInGO{appName: appName, httpUrl: httpUrl, httpClient: new(http.Client),
		wsUrl: wsUrl, wsOrigin: wsOrigin, wsMux: new(sync.RWMutex)}
}

// ARInGO represents one ARI connection/application
type ARInGO struct {
	appName    string
	httpUrl    string
	httpClient *http.Client
	wsUrl      string
	wsOrigin   string
	ws         *websocket.Conn
	wsMux      *sync.RWMutex
}

func (ari *ARInGO) connectWS() (err error) {
	ari.wsMux.Lock()
	defer ari.wsMux.Unlock()
	ari.ws, err = websocket.Dial(ari.wsUrl, "", ari.wsOrigin)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>More shape<commit_after>/*
Released under MIT License <http://www.opensource.org/licenses/mit-license.php
Copyright (C) ITsysCOM GmbH. All Rights Reserved.

Provides Asterisk ARI connector from Go programming language.
*/

package aringo

import (
	"net/http"
	"net/url"
	"sync"
	"time"

	"golang.org/x/net/websocket"
)

// successive Fibonacci numbers.
func Fib() func() time.Duration {
	a, b := 0, 1
	return func() time.Duration {
		a, b = b, a+b
		return time.Duration(a) * time.Second
	}
}

func NewARInGO(wsUrl string, reconnects int) (*ARInGO, error) {
	ari := &ARInGO{httpClient: new(http.Client), wsUrl: wsUrl, reconnects: reconnects, wsMux: new(sync.RWMutex)}
	if err := ari.Connect(); err != nil {
		return nil, err
	}
	return ari, nil
}

// ARInGO represents one ARI connection/application
type ARInGO struct {
	httpClient *http.Client
	wsUrl      string
	ws         *websocket.Conn
	reconnects int
	wsMux      *sync.RWMutex
}

func (ari *ARInGO) connectWS() (err error) {
	ari.wsMux.Lock()
	defer ari.wsMux.Unlock()
	ari.ws, err = websocket.Dial(ari.wsUrl, "", "http://localhost")
	if err != nil {
		return
	}
	return nil
}

func (ari *ARInGO) Connect() error {
	if err := ari.connectWS(); err != nil {
		return err
	}
	return nil
}

// Call represents one REST call to Asterisk using httpClient
// Returns a http.Response so we can process additional data out of it
func (ari *ARInGO) Call(url string, data url.Values, resp *http.Response) error {
	if reply, err := ari.httpClient.PostForm(url, data); err != nil {
		return err
	} else {
		*resp = *reply
	}
	return nil
}
<|endoftext|>


<commit_before>package ns1

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"path"

	"github.com/google/go-querystring/query"
)

const ApiVersion = "v1"

var (
	ErrNotFound     = errors.New("Not Found")
	ErrAuthFailure  = errors.New("Authentication failed")
	ErrAccessDenied = errors.New("Access denied")
	ErrNilResponse  = errors.New("Nil response")
)

type Zone struct {
	Id   string `json:"id"`
	Zone string `json:"zone"`
}

type Qps struct {
	Qps float64 `json:"qps"`
}

type MonitoringJob struct {
	Id        string                `json:"id"`
	Name      string                `json:"name"`
	Status    map[string]*JobStatus `json:"status"`
	Frequency int                   `json:"frequency"`
}

type JobStatus struct {
	Since  int    `json:"since"`
	Status string `json:"status"`
}

type MonitoringMetric struct {
	JobId   string             `json:"jobid"`
	Region  string             `json:"region"`
	Metrics map[string]*Metric `json:"metrics"`
}

type Metric struct {
	Avg   float64  `json:"avg"`
	Graph []*Point `json:"graph"`
}

type Point struct {
	Timestamp int
	Value     float64
}

func (p *Point) UnmarshalJSON(data []byte) error {
	tmp := make([]json.Number, 0)
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		return err
	}
	if len(tmp) != 2 {
		return fmt.Errorf("data array should only have 2 values, [ts,val]")
	}
	ts, err := tmp[0].Int64()
	if err != nil {
		return err
	}
	p.Timestamp = int(ts)
	p.Value, err = tmp[1].Float64()
	if err != nil {
		return err
	}
	return nil
}

type Client struct {
	URL    *url.URL
	http   *http.Client
	ApiKey string
	prefix string
}

func NewClient(serverUrl, apiKey string, insecure bool) (*Client, error) {
	u, err := url.Parse(serverUrl)
	if err != nil {
		return nil, err
	}
	if u.Scheme != "http" && u.Scheme != "https" {
		return nil, fmt.Errorf("URL %s is not in the format of http(s)://<ip>:<port>", serverUrl)
	}
	u.Path = path.Clean(u.Path + "/" + ApiVersion)
	c := &Client{
		URL:    u,
		ApiKey: apiKey,
		http: &http.Client{
			Transport: &http.Transport{
				TLSClientConfig: &tls.Config{
					InsecureSkipVerify: insecure,
				},
			},
		},
		prefix: u.String(),
	}
	return c, nil
}

func (c *Client) get(path string, query interface{}) ([]byte, error) {
	if query != nil {
		qstr, err := ToQueryString(query)
		if err != nil {
			return nil, err
		}
		path = path + "?" + qstr
	}
	log.Printf("sending request for %s", c.prefix+path)
	req, err := http.NewRequest("GET", c.prefix+path, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-NSONE-KEY", c.ApiKey)
	rsp, err := c.http.Do(req)
	if err != nil {
		return nil, err
	}
	return handleResp(rsp)
}

func handleResp(rsp *http.Response) ([]byte, error) {
	if rsp.StatusCode == 401 {
		return nil, ErrAuthFailure
	}
	if rsp.StatusCode == 403 {
		return nil, ErrAccessDenied
	}
	if rsp.StatusCode == 404 {
		return nil, ErrNotFound
	}
	if rsp.StatusCode != 200 {
		return nil, fmt.Errorf("Unknown error encountered. %s", rsp.Status)
	}
	b, err := ioutil.ReadAll(rsp.Body)
	rsp.Body.Close()
	if err != nil {
		return nil, err
	}

	return b, nil
}

// Convert an interface{} to a urlencoded querystring
func ToQueryString(q interface{}) (string, error) {
	v, err := query.Values(q)
	if err != nil {
		return "", err
	}
	return v.Encode(), nil
}

func (c *Client) Zones() ([]*Zone, error) {
	body, err := c.get("/zones", nil)
	if err != nil {
		return nil, err
	}
	zones := make([]*Zone, 0)
	err = json.Unmarshal(body, &zones)
	if err != nil {
		return nil, err
	}
	return zones, nil
}

func (c *Client) Qps(zone string) (*Qps, error) {
	path := "/stats/qps"
	if zone != "" {
		path = path + "/" + zone
	}
	body, err := c.get(path, nil)
	if err != nil {
		return nil, err
	}
	qps := Qps{}
	err = json.Unmarshal(body, &qps)
	if err != nil {
		return nil, err
	}
	return &qps, nil
}

func (c *Client) MonitoringJobs() ([]*MonitoringJob, error) {
	body, err := c.get("/monitoring/jobs", nil)
	if err != nil {
		return nil, err
	}
	jobs := make([]*MonitoringJob, 0)
	err = json.Unmarshal(body, &jobs)
	if err != nil {
		return nil, err
	}
	return jobs, nil
}

func (c *Client) MonitoringMetics(jobid string) ([]*MonitoringMetric, error) {
	body, err := c.get("/monitoring/metrics/"+jobid, nil)
	if err != nil {
		return nil, err
	}
	metrics := make([]*MonitoringMetric, 0)
	err = json.Unmarshal(body, &metrics)
	if err != nil {
		return nil, err
	}
	return metrics, nil
}
<commit_msg>fixes #5<commit_after>package ns1

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"path"

	"github.com/google/go-querystring/query"
)

const ApiVersion = "v1"

var (
	ErrNotFound     = errors.New("Not Found")
	ErrAuthFailure  = errors.New("Authentication failed")
	ErrAccessDenied = errors.New("Access denied")
	ErrNilResponse  = errors.New("Nil response")
)

type Zone struct {
	Id   string `json:"id"`
	Zone string `json:"zone"`
}

type Qps struct {
	Qps float64 `json:"qps"`
}

type MonitoringJob struct {
	Id        string                `json:"id"`
	Name      string                `json:"name"`
	Status    map[string]*JobStatus `json:"status"`
	Frequency json.Number           `json:"frequency"`
}

type JobStatus struct {
	Since  json.Number `json:"since"`
	Status string      `json:"status"`
}

type MonitoringMetric struct {
	JobId   string             `json:"jobid"`
	Region  string             `json:"region"`
	Metrics map[string]*Metric `json:"metrics"`
}

type Metric struct {
	Avg   float64  `json:"avg"`
	Graph []*Point `json:"graph"`
}

type Point struct {
	Timestamp int
	Value     float64
}

func (p *Point) UnmarshalJSON(data []byte) error {
	tmp := make([]json.Number, 0)
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		return err
	}
	if len(tmp) != 2 {
		return fmt.Errorf("data array should only have 2 values, [ts,val]")
	}
	ts, err := tmp[0].Int64()
	if err != nil {
		return err
	}
	p.Timestamp = int(ts)
	p.Value, err = tmp[1].Float64()
	if err != nil {
		return err
	}
	return nil
}

type Client struct {
	URL    *url.URL
	http   *http.Client
	ApiKey string
	prefix string
}

func NewClient(serverUrl, apiKey string, insecure bool) (*Client, error) {
	u, err := url.Parse(serverUrl)
	if err != nil {
		return nil, err
	}
	if u.Scheme != "http" && u.Scheme != "https" {
		return nil, fmt.Errorf("URL %s is not in the format of http(s)://<ip>:<port>", serverUrl)
	}
	u.Path = path.Clean(u.Path + "/" + ApiVersion)
	c := &Client{
		URL:    u,
		ApiKey: apiKey,
		http: &http.Client{
			Transport: &http.Transport{
				TLSClientConfig: &tls.Config{
					InsecureSkipVerify: insecure,
				},
			},
		},
		prefix: u.String(),
	}
	return c, nil
}

func (c *Client) get(path string, query interface{}) ([]byte, error) {
	if query != nil {
		qstr, err := ToQueryString(query)
		if err != nil {
			return nil, err
		}
		path = path + "?" + qstr
	}
	log.Printf("sending request for %s", c.prefix+path)
	req, err := http.NewRequest("GET", c.prefix+path, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-NSONE-KEY", c.ApiKey)
	rsp, err := c.http.Do(req)
	if err != nil {
		return nil, err
	}
	return handleResp(rsp)
}

func handleResp(rsp *http.Response) ([]byte, error) {
	if rsp.StatusCode == 401 {
		return nil, ErrAuthFailure
	}
	if rsp.StatusCode == 403 {
		return nil, ErrAccessDenied
	}
	if rsp.StatusCode == 404 {
		return nil, ErrNotFound
	}
	if rsp.StatusCode != 200 {
		return nil, fmt.Errorf("Unknown error encountered. %s", rsp.Status)
	}
	b, err := ioutil.ReadAll(rsp.Body)
	rsp.Body.Close()
	if err != nil {
		return nil, err
	}

	return b, nil
}

// Convert an interface{} to a urlencoded querystring
func ToQueryString(q interface{}) (string, error) {
	v, err := query.Values(q)
	if err != nil {
		return "", err
	}
	return v.Encode(), nil
}

func (c *Client) Zones() ([]*Zone, error) {
	body, err := c.get("/zones", nil)
	if err != nil {
		return nil, err
	}
	zones := make([]*Zone, 0)
	err = json.Unmarshal(body, &zones)
	if err != nil {
		return nil, err
	}
	return zones, nil
}

func (c *Client) Qps(zone string) (*Qps, error) {
	path := "/stats/qps"
	if zone != "" {
		// we need to escape twice as internally the path is stored in encoded
		// form so it is not possible to tell if %2F or / were passed.
		// see https://golang.org/pkg/net/url/#URL
		path = path + "/" + url.QueryEscape(url.QueryEscape(zone))
	}
	body, err := c.get(path, nil)
	if err != nil {
		log.Printf("failed to get %s. %s", path, err)
		return nil, err
	}
	qps := Qps{}
	err = json.Unmarshal(body, &qps)
	if err != nil {
		return nil, err
	}
	return &qps, nil
}

func (c *Client) MonitoringJobs() ([]*MonitoringJob, error) {
	body, err := c.get("/monitoring/jobs", nil)
	if err != nil {
		return nil, err
	}
	jobs := make([]*MonitoringJob, 0)
	err = json.Unmarshal(body, &jobs)
	if err != nil {
		log.Printf("failed to unmarshal monitoringJob resp. %s", err)
		log.Printf("--------\n%s\n--------\n", body)
		return nil, err
	}
	return jobs, nil
}

func (c *Client) MonitoringMetics(jobid string) ([]*MonitoringMetric, error) {
	body, err := c.get("/monitoring/metrics/"+jobid, nil)
	if err != nil {
		return nil, err
	}
	metrics := make([]*MonitoringMetric, 0)
	err = json.Unmarshal(body, &metrics)
	if err != nil {
		return nil, err
	}
	return metrics, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/gosuri/uiprogress"
	"github.com/gosuri/uiprogress/util/strutil"

	"gopkg.in/kothar/go-backblaze.v0"
)

// TODO support directories
// TODO support replacing all previous versions
type Put struct {
}

func init() {
	parser.AddCommand("put", "Store a file",
		"Uploads one or more files. Specify the bucket with -b, and the filenames to upload as extra arguments.",
		&Put{})
}

func (o *Put) Execute(args []string) error {
	client, err := Client()
	if err != nil {
		return err
	}

	bucket, err := client.Bucket(opts.Bucket)
	if err != nil {
		return err
	}
	if bucket == nil {
		return errors.New("Bucket not found: " + opts.Bucket)
	}

	uiprogress.Start()
	for _, file := range args {
		_, err := upload(bucket, file)
		if err != nil {
			return err
		}
	}

	return nil
}

type progressReader struct {
	bar *uiprogress.Bar
	r   io.ReadSeeker
}

func (p *progressReader) Read(b []byte) (int, error) {
	read, err := p.r.Read(b)
	p.bar.Set(p.bar.Current() + read)
	return read, err
}

func (p *progressReader) Seek(offset int64, whence int) (int64, error) {
	switch whence {
	case 0:
		p.bar.Set(int(offset))
	case 1:
		p.bar.Set(p.bar.Current() + int(offset))
	case 2:
		p.bar.Set(p.bar.Total - int(offset))
	}
	return p.r.Seek(offset, whence)
}

func upload(bucket *backblaze.Bucket, file string) (*backblaze.File, error) {

	stat, err := os.Stat(file)
	if err != nil {
		return nil, err
	}

	reader, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	var r io.Reader = reader
	if opts.Verbose {
		bar := uiprogress.AddBar(int(stat.Size()))
		bar.AppendFunc(func(b *uiprogress.Bar) string {
			speed := (float32(b.Current()) / 1.024) / float32(b.TimeElapsed())
			return fmt.Sprintf("%7.2f KB/s", speed)
		})
		bar.AppendCompleted()
		bar.PrependFunc(func(b *uiprogress.Bar) string { return fmt.Sprintf("%10d", b.Total) })
		bar.PrependFunc(func(b *uiprogress.Bar) string { return strutil.Resize(file, 50) })
		bar.Width = 20

		r = &progressReader{bar, reader}
	}

	return bucket.UploadFile(filepath.Base(file), nil, r)
}
<commit_msg>Fix transfer rate calculation for uploads<commit_after>package main

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/gosuri/uiprogress"
	"github.com/gosuri/uiprogress/util/strutil"

	"gopkg.in/kothar/go-backblaze.v0"
)

// TODO support directories
// TODO support replacing all previous versions
type Put struct {
}

func init() {
	parser.AddCommand("put", "Store a file",
		"Uploads one or more files. Specify the bucket with -b, and the filenames to upload as extra arguments.",
		&Put{})
}

func (o *Put) Execute(args []string) error {
	client, err := Client()
	if err != nil {
		return err
	}

	bucket, err := client.Bucket(opts.Bucket)
	if err != nil {
		return err
	}
	if bucket == nil {
		return errors.New("Bucket not found: " + opts.Bucket)
	}

	uiprogress.Start()
	for _, file := range args {
		_, err := upload(bucket, file)
		if err != nil {
			return err
		}
	}

	return nil
}

type progressReader struct {
	bar *uiprogress.Bar
	r   io.ReadSeeker
}

func (p *progressReader) Read(b []byte) (int, error) {
	read, err := p.r.Read(b)
	p.bar.Set(p.bar.Current() + read)
	return read, err
}

func (p *progressReader) Seek(offset int64, whence int) (int64, error) {
	switch whence {
	case 0:
		p.bar.Set(int(offset))
	case 1:
		p.bar.Set(p.bar.Current() + int(offset))
	case 2:
		p.bar.Set(p.bar.Total - int(offset))
	}
	return p.r.Seek(offset, whence)
}

func upload(bucket *backblaze.Bucket, file string) (*backblaze.File, error) {

	stat, err := os.Stat(file)
	if err != nil {
		return nil, err
	}

	reader, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	var r io.Reader = reader
	if opts.Verbose {
		bar := uiprogress.AddBar(int(stat.Size()))
		bar.AppendFunc(func(b *uiprogress.Bar) string {
			speed := (float32(b.Current()) / 1024) / float32(b.TimeElapsed().Seconds())
			return fmt.Sprintf("%7.2f KB/s", speed)
		})
		bar.AppendCompleted()
		bar.PrependFunc(func(b *uiprogress.Bar) string { return fmt.Sprintf("%10d", b.Total) })
		bar.PrependFunc(func(b *uiprogress.Bar) string { return strutil.Resize(file, 50) })
		bar.Width = 20

		r = &progressReader{bar, reader}
	}

	return bucket.UploadFile(filepath.Base(file), nil, r)
}
<|endoftext|>


<commit_before>// Package beta provides algorithms for working with beta distributions.
//
// https://en.wikipedia.org/wiki/Beta_distribution
package beta

import (
	"github.com/ready-steady/sfunc"
)

// Beta represents a particular distribution from the family.
type Beta struct {
	α float64
	β float64
	a float64
	b float64
}

// New returns a beta distribution with α and β on [a, b].
func New(α, β, a, b float64) *Beta {
	return &Beta{α, β, a, b}
}

// CDF evaluates the CDF of the distribution.
func (b *Beta) CDF(x float64) float64 {
	return sfunc.IncBeta((x-b.a)/(b.b-b.a), b.α, b.β, sfunc.LogBeta(b.α, b.β))
}

// InvCDF evaluates the inverse CDF of the distribution.
func (b *Beta) InvCDF(p float64) float64 {
	return (b.b-b.a)*sfunc.InvIncBeta(p, b.α, b.β, sfunc.LogBeta(b.α, b.β)) + b.a
}
<commit_msg>Renamed sfunc into special<commit_after>// Package beta provides algorithms for working with beta distributions.
//
// https://en.wikipedia.org/wiki/Beta_distribution
package beta

import (
	"github.com/ready-steady/special"
)

// Beta represents a particular distribution from the family.
type Beta struct {
	α float64
	β float64
	a float64
	b float64
}

// New returns a beta distribution with α and β on [a, b].
func New(α, β, a, b float64) *Beta {
	return &Beta{α, β, a, b}
}

// CDF evaluates the CDF of the distribution.
func (b *Beta) CDF(x float64) float64 {
	return special.IncBeta((x-b.a)/(b.b-b.a), b.α, b.β, special.LogBeta(b.α, b.β))
}

// InvCDF evaluates the inverse CDF of the distribution.
func (b *Beta) InvCDF(p float64) float64 {
	return (b.b-b.a)*special.InvIncBeta(p, b.α, b.β, special.LogBeta(b.α, b.β)) + b.a
}
<|endoftext|>


<commit_before>// Copyright ©2012 The bíogo.bam Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bgzf

import (
	"bytes"
	"code.google.com/p/biogo.bam/bgzf/egzip"
	"compress/gzip"
	"errors"
	"io"
	"io/ioutil"
	"os"
)

const (
	BlockSize    = 0x0ff00 // Size of input data block.
	MaxBlockSize = 0x10000 // Maximum size of output block.
)

const (
	bgzfExtra = "BC\x02\x00\x00\x00"
	minFrame  = 20 + len(bgzfExtra) // Minimum bgzf header+footer length.

	// Magic EOF block.
	magicBlock = "\x1f\x8b\x08\x04\x00\x00\x00\x00\x00\xff\x06\x00\x42\x43\x02\x00\x1b\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00"
)

var bgzfExtraPrefix = []byte(bgzfExtra[:4])

func compressBound(srcLen int) int {
	return srcLen + srcLen>>12 + srcLen>>14 + srcLen>>25 + 13 + minFrame
}

func init() {
	if compressBound(BlockSize) > MaxBlockSize {
		panic("bam: BlockSize too large")
	}
}

var (
	NewBlock         = egzip.NewBlock
	ErrClosed        = errors.New("bgzf: write to closed writer")
	ErrBlockOverflow = errors.New("bgzf: block overflow")
	ErrWrongFileType = errors.New("bgzf: file is a directory")
)

type Reader struct {
	gzip.Header
	gz  *egzip.Reader
	err error
}

func CheckEOF(f *os.File) (bool, error) {
	fi, err := f.Stat()
	if err != nil || fi.IsDir() {
		return false, ErrWrongFileType
	}

	b := make([]byte, len(magicBlock))
	_, err = f.ReadAt(b, fi.Size()-int64(len(magicBlock)))
	if err != nil {
		return false, err
	}
	for i := range b {
		if b[i] != magicBlock[i] {
			return false, nil
		}
	}
	return true, nil
}

func NewReader(r io.Reader, limited bool) (*Reader, error) {
	bg := &Reader{}
	gz, err := egzip.NewReader(r, &bg.Header)
	gz.BlockLimited = limited
	if err != nil {
		return nil, err
	}
	bg.gz = gz
	return bg, nil
}

type Offset struct {
	File  int64
	Block uint16
}

func (bg *Reader) Seek(off Offset, whence int) error {
	bg.err = bg.gz.Seek(off.File, whence)
	if bg.err != nil {
		return bg.err
	}
	if off.Block > 0 {
		_, bg.err = io.CopyN(ioutil.Discard, bg.gz, int64(off.Block))
	}
	return bg.err
}

func (bg *Reader) Close() error {
	return bg.gz.Close()
}

func (bg *Reader) Read(p []byte) (int, error) {
	if bg.err != nil {
		return 0, bg.err
	}
	n, err := bg.gz.Read(p)
	if n < len(p) && err == nil {
		var pn int
		pn, err = bg.Read(p[n:])
		n += pn
	}
	if n > 0 && err == io.EOF {
		err = nil
	}
	return n, err
}

func (bg *Reader) CurrBlockSize() (int, error) {
	if bg.err != nil {
		return -1, bg.err
	}
	i := bytes.Index(bg.Extra, bgzfExtraPrefix)
	if i+5 >= len(bg.Extra) {
		return -1, gzip.ErrHeader
	}
	return (int(bg.Extra[i+4]) | int(bg.Extra[i+5])<<8) + 1, nil
}

type Writer struct {
	gzip.Header
	level   int
	w       io.Writer
	gz      *egzip.Writer
	next    uint
	err     error
	written bool
	closed  bool
	block   [BlockSize]byte
	buf     *bytes.Buffer
}

func NewWriter(w io.Writer) *Writer {
	return NewWriterLevel(w, gzip.DefaultCompression)
}

func NewWriterLevel(w io.Writer, level int) *Writer {
	return &Writer{
		Header: gzip.Header{OS: 0xff},
		w:      w,
		level:  level,
		buf:    &bytes.Buffer{},
	}
}

func (bg *Writer) Next() int {
	return int(bg.next)
}

func (bg *Writer) Flush() error {
	if bg.err != nil {
		return bg.err
	}
	if bg.closed {
		return nil
	}
	if bg.written && bg.next == 0 {
		return nil
	}
	bg.written = true
	return bg.writeBlock()
}

func (bg *Writer) writeBlock() error {
	if bg.gz == nil {
		bg.gz, bg.err = egzip.NewWriterLevel(bg.buf, bg.level)
		if bg.err != nil {
			return bg.err
		}
	} else {
		bg.gz.Reset(bg.buf)
	}
	bg.gz.Header = gzip.Header{
		Comment: bg.Comment,
		Extra:   append([]byte(bgzfExtra), bg.Extra...),
		ModTime: bg.ModTime,
		Name:    bg.Name,
		OS:      bg.OS,
	}

	_, bg.err = bg.gz.Write(bg.block[:bg.next])
	if bg.err != nil {
		return bg.err
	}
	bg.err = bg.gz.Close()
	if bg.err != nil {
		return bg.err
	}
	bg.next = 0

	b := bg.buf.Bytes()
	i := bytes.Index(b, bgzfExtraPrefix)
	if i < 0 {
		return gzip.ErrHeader
	}
	size := len(b) - 1
	if size >= MaxBlockSize {
		bg.err = ErrBlockOverflow
		return bg.err
	}
	b[i+4], b[i+5] = byte(size), byte(size>>8)

	_, bg.err = io.Copy(bg.w, bg.buf)
	if bg.err != nil {
		return bg.err
	}
	bg.buf.Reset()

	return nil
}

func (bg *Writer) Close() error {
	if bg.err != nil {
		return bg.err
	}
	if bg.closed {
		return nil
	}
	bg.err = bg.writeBlock()
	if bg.err != nil {
		return bg.err
	}
	_, bg.err = bg.w.Write([]byte(magicBlock))
	if bg.err == nil {
		bg.closed = true
	}
	return bg.err
}

func (bg *Writer) Write(p []byte) (int, error) {
	if bg.err != nil {
		return 0, bg.err
	}
	if bg.closed {
		return 0, ErrClosed
	}

	bg.written = false
	var n int
	for len(p) > 0 {
		if bg.next+uint(len(p)) > BlockSize {
			bg.err = bg.Flush()
			if bg.err != nil {
				return 0, bg.err
			}
		}
		c := copy(bg.block[bg.next:], p)
		n += c
		p = p[c:]
		bg.next += uint(c)
		if bg.next == BlockSize {
			bg.err = bg.Flush()
			if bg.err != nil {
				return n, bg.err
			}
		}
	}

	return n, bg.err
}
<commit_msg>Don't access reader unless err is nil<commit_after>// Copyright ©2012 The bíogo.bam Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bgzf

import (
	"bytes"
	"code.google.com/p/biogo.bam/bgzf/egzip"
	"compress/gzip"
	"errors"
	"io"
	"io/ioutil"
	"os"
)

const (
	BlockSize    = 0x0ff00 // Size of input data block.
	MaxBlockSize = 0x10000 // Maximum size of output block.
)

const (
	bgzfExtra = "BC\x02\x00\x00\x00"
	minFrame  = 20 + len(bgzfExtra) // Minimum bgzf header+footer length.

	// Magic EOF block.
	magicBlock = "\x1f\x8b\x08\x04\x00\x00\x00\x00\x00\xff\x06\x00\x42\x43\x02\x00\x1b\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00"
)

var bgzfExtraPrefix = []byte(bgzfExtra[:4])

func compressBound(srcLen int) int {
	return srcLen + srcLen>>12 + srcLen>>14 + srcLen>>25 + 13 + minFrame
}

func init() {
	if compressBound(BlockSize) > MaxBlockSize {
		panic("bam: BlockSize too large")
	}
}

var (
	NewBlock         = egzip.NewBlock
	ErrClosed        = errors.New("bgzf: write to closed writer")
	ErrBlockOverflow = errors.New("bgzf: block overflow")
	ErrWrongFileType = errors.New("bgzf: file is a directory")
)

type Reader struct {
	gzip.Header
	gz  *egzip.Reader
	err error
}

func CheckEOF(f *os.File) (bool, error) {
	fi, err := f.Stat()
	if err != nil || fi.IsDir() {
		return false, ErrWrongFileType
	}

	b := make([]byte, len(magicBlock))
	_, err = f.ReadAt(b, fi.Size()-int64(len(magicBlock)))
	if err != nil {
		return false, err
	}
	for i := range b {
		if b[i] != magicBlock[i] {
			return false, nil
		}
	}
	return true, nil
}

func NewReader(r io.Reader, limited bool) (*Reader, error) {
	bg := &Reader{}
	gz, err := egzip.NewReader(r, &bg.Header)
	if err != nil {
		return nil, err
	}
	gz.BlockLimited = limited
	bg.gz = gz
	return bg, nil
}

type Offset struct {
	File  int64
	Block uint16
}

func (bg *Reader) Seek(off Offset, whence int) error {
	bg.err = bg.gz.Seek(off.File, whence)
	if bg.err != nil {
		return bg.err
	}
	if off.Block > 0 {
		_, bg.err = io.CopyN(ioutil.Discard, bg.gz, int64(off.Block))
	}
	return bg.err
}

func (bg *Reader) Close() error {
	return bg.gz.Close()
}

func (bg *Reader) Read(p []byte) (int, error) {
	if bg.err != nil {
		return 0, bg.err
	}
	n, err := bg.gz.Read(p)
	if n < len(p) && err == nil {
		var pn int
		pn, err = bg.Read(p[n:])
		n += pn
	}
	if n > 0 && err == io.EOF {
		err = nil
	}
	return n, err
}

func (bg *Reader) CurrBlockSize() (int, error) {
	if bg.err != nil {
		return -1, bg.err
	}
	i := bytes.Index(bg.Extra, bgzfExtraPrefix)
	if i+5 >= len(bg.Extra) {
		return -1, gzip.ErrHeader
	}
	return (int(bg.Extra[i+4]) | int(bg.Extra[i+5])<<8) + 1, nil
}

type Writer struct {
	gzip.Header
	level   int
	w       io.Writer
	gz      *egzip.Writer
	next    uint
	err     error
	written bool
	closed  bool
	block   [BlockSize]byte
	buf     *bytes.Buffer
}

func NewWriter(w io.Writer) *Writer {
	return NewWriterLevel(w, gzip.DefaultCompression)
}

func NewWriterLevel(w io.Writer, level int) *Writer {
	return &Writer{
		Header: gzip.Header{OS: 0xff},
		w:      w,
		level:  level,
		buf:    &bytes.Buffer{},
	}
}

func (bg *Writer) Next() int {
	return int(bg.next)
}

func (bg *Writer) Flush() error {
	if bg.err != nil {
		return bg.err
	}
	if bg.closed {
		return nil
	}
	if bg.written && bg.next == 0 {
		return nil
	}
	bg.written = true
	return bg.writeBlock()
}

func (bg *Writer) writeBlock() error {
	if bg.gz == nil {
		bg.gz, bg.err = egzip.NewWriterLevel(bg.buf, bg.level)
		if bg.err != nil {
			return bg.err
		}
	} else {
		bg.gz.Reset(bg.buf)
	}
	bg.gz.Header = gzip.Header{
		Comment: bg.Comment,
		Extra:   append([]byte(bgzfExtra), bg.Extra...),
		ModTime: bg.ModTime,
		Name:    bg.Name,
		OS:      bg.OS,
	}

	_, bg.err = bg.gz.Write(bg.block[:bg.next])
	if bg.err != nil {
		return bg.err
	}
	bg.err = bg.gz.Close()
	if bg.err != nil {
		return bg.err
	}
	bg.next = 0

	b := bg.buf.Bytes()
	i := bytes.Index(b, bgzfExtraPrefix)
	if i < 0 {
		return gzip.ErrHeader
	}
	size := len(b) - 1
	if size >= MaxBlockSize {
		bg.err = ErrBlockOverflow
		return bg.err
	}
	b[i+4], b[i+5] = byte(size), byte(size>>8)

	_, bg.err = io.Copy(bg.w, bg.buf)
	if bg.err != nil {
		return bg.err
	}
	bg.buf.Reset()

	return nil
}

func (bg *Writer) Close() error {
	if bg.err != nil {
		return bg.err
	}
	if bg.closed {
		return nil
	}
	bg.err = bg.writeBlock()
	if bg.err != nil {
		return bg.err
	}
	_, bg.err = bg.w.Write([]byte(magicBlock))
	if bg.err == nil {
		bg.closed = true
	}
	return bg.err
}

func (bg *Writer) Write(p []byte) (int, error) {
	if bg.err != nil {
		return 0, bg.err
	}
	if bg.closed {
		return 0, ErrClosed
	}

	bg.written = false
	var n int
	for len(p) > 0 {
		if bg.next+uint(len(p)) > BlockSize {
			bg.err = bg.Flush()
			if bg.err != nil {
				return 0, bg.err
			}
		}
		c := copy(bg.block[bg.next:], p)
		n += c
		p = p[c:]
		bg.next += uint(c)
		if bg.next == BlockSize {
			bg.err = bg.Flush()
			if bg.err != nil {
				return n, bg.err
			}
		}
	}

	return n, bg.err
}
<|endoftext|>


<commit_before>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*

Go Builder is a continuous build client for the Go project.
It integrates with the Go Dashboard AppEngine application.

Go Builder is intended to run continuously as a background process.

It periodically pulls updates from the Go Mercurial repository.

When a newer revision is found, Go Builder creates a clone of the repository,
runs all.bash, and reports build success or failure to the Go Dashboard.

For a successful build, Go Builder will also run benchmarks
(cd $GOROOT/src/pkg; make bench) and send the results to the Go Dashboard.

For a release revision (a change description that matches "release.YYYY-MM-DD"),
Go Builder will create a tar.gz archive of the GOROOT and deliver it to the
Go Google Code project's downloads section.

Usage:

  gobuilder goos-goarch...

  Several goos-goarch combinations can be provided, and the builder will
  build them in serial.

Optional flags:

  -dashboard="godashboard.appspot.com": Go Dashboard Host
    The location of the Go Dashboard application to which Go Builder will
    report its results.

  -bench: Run benchmarks

  -release: Build and deliver binary release archive

  -rev=N: Build revision N and exit

  -cmd="./all.bash": Build command (specify absolute or relative to go/src)

  -v: Verbose logging

  -external: External package builder mode (will not report Go build
     state to dashboard, issue releases, or run benchmarks)

The key file should be located at $HOME/.gobuilder or, for a builder-specific
key, $HOME/.gobuilder-$BUILDER (eg, $HOME/.gobuilder-linux-amd64).

The build key file is a text file of the format:

  godashboard-key
  googlecode-username
  googlecode-password

If the Google Code credentials are not provided the archival step
will be skipped.

*/
package documentation
<commit_msg>misc/dashboard/builder/doc.go: gofmt it<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*

Go Builder is a continuous build client for the Go project.
It integrates with the Go Dashboard AppEngine application.

Go Builder is intended to run continuously as a background process.

It periodically pulls updates from the Go Mercurial repository.

When a newer revision is found, Go Builder creates a clone of the repository,
runs all.bash, and reports build success or failure to the Go Dashboard.

For a successful build, Go Builder will also run benchmarks
(cd $GOROOT/src/pkg; make bench) and send the results to the Go Dashboard.

For a release revision (a change description that matches "release.YYYY-MM-DD"),
Go Builder will create a tar.gz archive of the GOROOT and deliver it to the
Go Google Code project's downloads section.

Usage:

  gobuilder goos-goarch...

  Several goos-goarch combinations can be provided, and the builder will
  build them in serial.

Optional flags:

  -dashboard="godashboard.appspot.com": Go Dashboard Host
    The location of the Go Dashboard application to which Go Builder will
    report its results.

  -bench: Run benchmarks

  -release: Build and deliver binary release archive

  -rev=N: Build revision N and exit

  -cmd="./all.bash": Build command (specify absolute or relative to go/src)

  -v: Verbose logging

  -external: External package builder mode (will not report Go build
     state to dashboard, issue releases, or run benchmarks)

The key file should be located at $HOME/.gobuilder or, for a builder-specific
key, $HOME/.gobuilder-$BUILDER (eg, $HOME/.gobuilder-linux-amd64).

The build key file is a text file of the format:

  godashboard-key
  googlecode-username
  googlecode-password

If the Google Code credentials are not provided the archival step
will be skipped.

*/
package documentation
<|endoftext|>


<commit_before>package godatai18n

import (
	"reflect"
)

func indirectType(t reflect.Type) reflect.Type {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	return t
}
<commit_msg>Fix indirectType() util code<commit_after>package godatai18n

import (
	"reflect"
)

func indirectType(t reflect.Type) reflect.Type {
	if t.Kind() == reflect.Ptr {
		return t.Elem()
	}
	return t
}
<|endoftext|>


<commit_before>package gobot

import (
	"encoding/json"
	"github.com/tarm/goserial"
	"io"
	"math/rand"
	"net"
	"reflect"
	"regexp"
	"time"
)

type Port io.ReadWriteCloser

func Every(t string, f func()) {
	dur := parseDuration(t)
	go func() {
		for {
			time.Sleep(dur)
			go f()
		}
	}()
}

func After(t string, f func()) {
	dur := parseDuration(t)
	go func() {
		time.Sleep(dur)
		f()
	}()
}

func parseDuration(t string) time.Duration {
	dur, err := time.ParseDuration(t)
	if err != nil {
		panic(err)
	}
	return dur
}

func Rand(max int) int {
	rand.Seed(time.Now().UTC().UnixNano())
	return rand.Intn(max)
}

func On(cs chan interface{}) interface{} {
	for s := range cs {
		return s
	}
	return nil
}

func ConnectToTcp(port string) io.ReadWriteCloser {
	tcpPort, err := net.Dial("tcp", port)
	if err != nil {
		panic(err)
	}
	return tcpPort
}

func ConnectToSerial(port string, baud int) io.ReadWriteCloser {
	c := &serial.Config{Name: port, Baud: baud}
	s, err := serial.OpenPort(c)
	if err != nil {
		panic(err)
	}
	return s
}

func IsUrl(url string) bool {
	rp := regexp.MustCompile("([^A-Za-z0-9]+).([^A-Za-z0-9]+).([^A-Za-z0-9]+)")
	return rp.MatchString(url)
}

func Call(thing interface{}, method string, params ...interface{}) []reflect.Value {
	in := make([]reflect.Value, len(params))
	for k, param := range params {
		in[k] = reflect.ValueOf(param)
	}
	return reflect.ValueOf(thing).MethodByName(method).Call(in)
}

func toJson(obj interface{}) string {
	b, _ := json.Marshal(obj)
	return string(b)
}
<commit_msg>DRY up On function<commit_after>package gobot

import (
	"encoding/json"
	"github.com/tarm/goserial"
	"io"
	"math/rand"
	"net"
	"reflect"
	"regexp"
	"time"
)

type Port io.ReadWriteCloser

func Every(t string, f func()) {
	dur := parseDuration(t)
	go func() {
		for {
			time.Sleep(dur)
			go f()
		}
	}()
}

func After(t string, f func()) {
	dur := parseDuration(t)
	go func() {
		time.Sleep(dur)
		f()
	}()
}

func parseDuration(t string) time.Duration {
	dur, err := time.ParseDuration(t)
	if err != nil {
		panic(err)
	}
	return dur
}

func Rand(max int) int {
	rand.Seed(time.Now().UTC().UnixNano())
	return rand.Intn(max)
}

func On(cs chan interface{}, f func(s interface{})) {
	go func() {
		for s := range cs {
			f(s)
		}
	}()
}

func ConnectToTcp(port string) io.ReadWriteCloser {
	tcpPort, err := net.Dial("tcp", port)
	if err != nil {
		panic(err)
	}
	return tcpPort
}

func ConnectToSerial(port string, baud int) io.ReadWriteCloser {
	c := &serial.Config{Name: port, Baud: baud}
	s, err := serial.OpenPort(c)
	if err != nil {
		panic(err)
	}
	return s
}

func IsUrl(url string) bool {
	rp := regexp.MustCompile("([^A-Za-z0-9]+).([^A-Za-z0-9]+).([^A-Za-z0-9]+)")
	return rp.MatchString(url)
}

func Call(thing interface{}, method string, params ...interface{}) []reflect.Value {
	in := make([]reflect.Value, len(params))
	for k, param := range params {
		in[k] = reflect.ValueOf(param)
	}
	return reflect.ValueOf(thing).MethodByName(method).Call(in)
}

func toJson(obj interface{}) string {
	b, _ := json.Marshal(obj)
	return string(b)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"crypto/sha512"
	"encoding/base64"
	"bufio"
)

func encode(password string) string {
	hasher := sha512.New()
	hasher.Write([]byte(password))
	return base64.StdEncoding.EncodeToString(hasher.Sum(nil))
}

func loadPassHash(filename string) map[string]bool {
	f, err := os.Open(filename)
	if err != nil {
		panic(err)
	}

	defer f.Close()

	passHashMap := make(map[string]bool)
	scanner := bufio.NewScanner(f)

	for scanner.Scan() {
		passHashMap[scanner.Text()] = true
	}

	return passHashMap
}
<commit_msg>More explicit function naming<commit_after>package main

import (
	"os"
	"crypto/sha512"
	"encoding/base64"
	"bufio"
)

func encode(password string) string {
	hasher := sha512.New()
	hasher.Write([]byte(password))
	return base64.StdEncoding.EncodeToString(hasher.Sum(nil))
}

func loadPassHashes(filename string) map[string]bool {
	f, err := os.Open(filename)
	if err != nil {
		panic(err)
	}

	defer f.Close()

	passHashMap := make(map[string]bool)
	scanner := bufio.NewScanner(f)

	for scanner.Scan() {
		passHashMap[scanner.Text()] = true
	}

	return passHashMap
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/samuel/go-gettext/gettext"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
)

func IsParamSet(r *http.Request, param string) bool {
	if len(r.URL.Query().Get(param)) > 0 {
		return true
	}
	return false
}

func Lang(r *http.Request) string {
	lang := r.URL.Query().Get("lang")
	if len(lang) == 0 {
		lang = "en_US"
	}
	return lang
}

func GetQS(q url.Values, param string, deflt int) (num int, str string) {
	str = q.Get(param)
	num, err := strconv.Atoi(str)
	if err != nil {
		num = deflt
		str = ""
	} else {
		str = fmt.Sprintf("&%s=%s", param, str)
	}
	return
}

func GetHost(r *http.Request) (host string, err error) {
	// get remote ip
	host = r.Header.Get("X-Forwarded-For")
	if len(host) > 0 {
		parts := strings.Split(host, ",")
		// apache will append the remote address
		host = strings.TrimSpace(parts[len(parts)-1])
	} else {
		host, _, err = net.SplitHostPort(r.RemoteAddr)
	}
	return
}

var TBBUserAgents = map[string]bool{
	"Mozilla/5.0 (Windows NT 6.1; rv:10.0) Gecko/20100101 Firefox/10.0": true,
	"Mozilla/5.0 (Windows NT 6.1; rv:17.0) Gecko/20100101 Firefox/17.0": true,
	"Mozilla/5.0 (Windows NT 6.1; rv:24.0) Gecko/20100101 Firefox/24.0": true,
}

func LikelyTBB(ua string) bool {
	_, ok := TBBUserAgents[ua]
	return ok
}

var HaveManual = map[string]bool{
	"ar":    true,
	"cs":    true,
	"de":    true,
	"el":    true,
	"en":    true,
	"es":    true,
	"fa":    true,
	"fr":    true,
	"hu":    true,
	"it":    true,
	"ja":    true,
	"lv":    true,
	"nb":    true,
	"nl":    true,
	"pl":    true,
	"pt_BR": true,
	"ru":    true,
	"sv":    true,
	"tr":    true,
	"zh_CN": true,
}

func FuncMap(domain *gettext.Domain) template.FuncMap {
	return template.FuncMap{
		"UnEscaped": func(x string) interface{} {
			return template.HTML(x)
		},
		"UnEscapedURL": func(x string) interface{} {
			return template.URL(x)
		},
		"GetText": func(lang string, text string) string {
			return domain.GetText(lang, text)
		},
		"Equal": func(one string, two string) bool {
			return one == two
		},
		"Not": func(b bool) bool {
			return !b
		},
		"And": func(a bool, b bool) bool {
			return a && b
		},
		"UserManual": func(lang string) string {
			if _, ok := HaveManual[lang]; !ok {
				lang = "en"
			}
			return lang
		},
	}
}

var Layout *template.Template

func CompileTemplate(base string, domain *gettext.Domain, templateName string) *template.Template {
	if Layout == nil {
		Layout = template.New("")
		Layout = Layout.Funcs(FuncMap(domain))
		Layout = template.Must(Layout.ParseFiles(
			path.Join(base, "public/base.html"),
			path.Join(base, "public/torbutton.html"),
		))
	}
	l, err := Layout.Clone()
	if err != nil {
		log.Fatal(err)
	}
	return template.Must(l.ParseFiles(path.Join(base, "public/", templateName)))
}

type locale struct {
	Code string
	Name string
}

func GetLocaleList(base string) map[string]string {
	// populated from https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
	// and https://sites.google.com/site/opti365/translate_codes
	// and https://en.wikipedia.org/w/api.php?action=sitematrix&format=json
	haveTranslatedNames := map[string]string{
		"af":    "Afrikaans",
		"ar":    "العربية",
		"bs":    "Bosanski jezik",
		"ca":    "Català",
		"cs":    "Čeština",
		"cy":    "Cymraeg",
		"da":    "Dansk",
		"de":    "Deutsch",
		"el":    "ελληνικά",
		"eo":    "Esperanto",
		"es":    "Español",
		"es_AR": "Español (Argentina)",
		"et":    "Eesti",
		"eu":    "Euskara",
		"fa":    "فارسی",
		"fi":    "Suomi",
		"fr":    "Français",
		"fr_CA": "Français (Canadien)",
		"gl":    "Galego",
		"he":    "עברית",
		"hi":    "हिन्दी",
		"hr":    "Hrvatski jezik",
		"hr_HR": "Hrvatski jezik (Croatia)",
		"hu":    "Magyar",
		"id":    "Bahasa Indonesia",
		"it":    "Italiano",
		"ja":    "日本語",
		"km":    "មែរ",
		"kn":    "ಕನ್ನಡ",
		"ko":    "한국어",
		"ko_KR": "한국어 (South Korea)",
		"lv":    "Latviešu valoda",
		"mk":    "македонски јазик",
		"ms_MY": "Bahasa Melayu",
		"my":    "ဗမာစာ",
		"nb":    "Norsk bokmål",
		"nl":    "Nederlands",
		"nl_BE": "Vlaams",
		"pa":    "ਪੰਜਾਬੀ",
		"pl":    "Język polski",
		"pl_PL": "Język polski (Poland)",
		"pt":    "Português",
		"pt_BR": "Português do Brasil",
		"ru":    "русский язык",
		"si_LK": "සිංහල",
		"sk":    "Slovenčina",
		"sl":    "Slovenski jezik",
		"sl_SI": "Slovenski jezik (Slovenia)",
		"sr":    "српски језик",
		"sv":    "Svenska",
		"te_IN": "తెలుగు",
		"th":    "ไทย",
		"tr":    "Türkçe",
		"uk":    "українська мова",
		"zh_CN": "中文简体",
		"zh_TW": "中文繁體",
	}

	// for all folders in locale which match a locale from https://www.transifex.com/api/2/languages/
	// use the language name unless we have an override
	webLocales, err := FetchTranslationLocales(base)
	if err != nil {
		log.Printf("Failed to get up to date language list, using fallback.")
		return haveTranslatedNames
	}

	return GetInstalledLocales(base, webLocales, haveTranslatedNames)
}

func FetchTranslationLocales(base string) (map[string]locale, error) {
	file, err := os.Open(path.Join(base, "data/langs"))
	if err != nil {
		return nil, err
	}
	defer file.Close()

	webLocales := make(map[string]locale)
	// Parse the api response into a list of possible locales
	dec := json.NewDecoder(file)
	for {
		var webList []locale
		if err = dec.Decode(&webList); err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}

		// The api returns an array, so we need to map it
		for _, l := range webList {
			webLocales[l.Code] = l
		}
	}

	return webLocales, nil
}

// Get a list of all languages installed in our locale folder with translations if available
func GetInstalledLocales(base string, webLocales map[string]locale, nameTranslations map[string]string) map[string]string {
	localFiles, err := ioutil.ReadDir(path.Join(base, "locale"))

	if err != nil {
		log.Print("No locales found in 'locale'. Try running 'make i18n'.")
		log.Fatal(err)
	}

	locales := make(map[string]string, len(localFiles))
	locales["en_US"] = "English"

	for _, f := range localFiles {
		// TODO: Ensure a language has 100% of the template file
		// Currently this is what should be on the torcheck_completed
		// branch on the translations git should be, so we don't really
		// have to check it in theory...
		code := f.Name()

		// Only accept folders which have corresponding locale
		if !f.IsDir() || webLocales[code] == (locale{}) {
			continue
		}

		// If we have a translated name for a given locale, use it
		if transName := nameTranslations[code]; transName != "" {
			locales[code] = transName
		} else {
			log.Print("No translated name for code: " + code)
			locales[code] = webLocales[code].Name
		}
	}

	return locales
}
<commit_msg>Use a regexp to test user agent strings<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/samuel/go-gettext/gettext"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
)

func IsParamSet(r *http.Request, param string) bool {
	return len(r.URL.Query().Get(param)) > 0
}

func Lang(r *http.Request) string {
	lang := r.URL.Query().Get("lang")
	if len(lang) == 0 {
		lang = "en_US"
	}
	return lang
}

func GetQS(q url.Values, param string, deflt int) (num int, str string) {
	str = q.Get(param)
	num, err := strconv.Atoi(str)
	if err != nil {
		num = deflt
		str = ""
	} else {
		str = fmt.Sprintf("&%s=%s", param, str)
	}
	return
}

func GetHost(r *http.Request) (host string, err error) {
	// get remote ip
	host = r.Header.Get("X-Forwarded-For")
	if len(host) > 0 {
		parts := strings.Split(host, ",")
		// apache will append the remote address
		host = strings.TrimSpace(parts[len(parts)-1])
	} else {
		host, _, err = net.SplitHostPort(r.RemoteAddr)
	}
	return
}

var TBBUserAgents = regexp.MustCompile(`^Mozilla/5\.0 \(Windows NT 6\.1; rv:[\d]+\.0\) Gecko/20100101 Firefox/[\d]+\.0$`)

func LikelyTBB(ua string) bool {
	return TBBUserAgents.MatchString(ua)
}

var HaveManual = map[string]bool{
	"ar":    true,
	"cs":    true,
	"de":    true,
	"el":    true,
	"en":    true,
	"es":    true,
	"fa":    true,
	"fr":    true,
	"hu":    true,
	"it":    true,
	"ja":    true,
	"lv":    true,
	"nb":    true,
	"nl":    true,
	"pl":    true,
	"pt_BR": true,
	"ru":    true,
	"sv":    true,
	"tr":    true,
	"zh_CN": true,
}

func FuncMap(domain *gettext.Domain) template.FuncMap {
	return template.FuncMap{
		"UnEscaped": func(x string) interface{} {
			return template.HTML(x)
		},
		"UnEscapedURL": func(x string) interface{} {
			return template.URL(x)
		},
		"GetText": func(lang string, text string) string {
			return domain.GetText(lang, text)
		},
		"Equal": func(one string, two string) bool {
			return one == two
		},
		"Not": func(b bool) bool {
			return !b
		},
		"And": func(a bool, b bool) bool {
			return a && b
		},
		"UserManual": func(lang string) string {
			if _, ok := HaveManual[lang]; !ok {
				lang = "en"
			}
			return lang
		},
	}
}

var Layout *template.Template

func CompileTemplate(base string, domain *gettext.Domain, templateName string) *template.Template {
	if Layout == nil {
		Layout = template.New("")
		Layout = Layout.Funcs(FuncMap(domain))
		Layout = template.Must(Layout.ParseFiles(
			path.Join(base, "public/base.html"),
			path.Join(base, "public/torbutton.html"),
		))
	}
	l, err := Layout.Clone()
	if err != nil {
		log.Fatal(err)
	}
	return template.Must(l.ParseFiles(path.Join(base, "public/", templateName)))
}

type locale struct {
	Code string
	Name string
}

func GetLocaleList(base string) map[string]string {
	// populated from https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
	// and https://sites.google.com/site/opti365/translate_codes
	// and https://en.wikipedia.org/w/api.php?action=sitematrix&format=json
	haveTranslatedNames := map[string]string{
		"af":    "Afrikaans",
		"ar":    "العربية",
		"bs":    "Bosanski jezik",
		"ca":    "Català",
		"cs":    "Čeština",
		"cy":    "Cymraeg",
		"da":    "Dansk",
		"de":    "Deutsch",
		"el":    "ελληνικά",
		"eo":    "Esperanto",
		"es":    "Español",
		"es_AR": "Español (Argentina)",
		"et":    "Eesti",
		"eu":    "Euskara",
		"fa":    "فارسی",
		"fi":    "Suomi",
		"fr":    "Français",
		"fr_CA": "Français (Canadien)",
		"gl":    "Galego",
		"he":    "עברית",
		"hi":    "हिन्दी",
		"hr":    "Hrvatski jezik",
		"hr_HR": "Hrvatski jezik (Croatia)",
		"hu":    "Magyar",
		"id":    "Bahasa Indonesia",
		"it":    "Italiano",
		"ja":    "日本語",
		"km":    "មែរ",
		"kn":    "ಕನ್ನಡ",
		"ko":    "한국어",
		"ko_KR": "한국어 (South Korea)",
		"lv":    "Latviešu valoda",
		"mk":    "македонски јазик",
		"ms_MY": "Bahasa Melayu",
		"my":    "ဗမာစာ",
		"nb":    "Norsk bokmål",
		"nl":    "Nederlands",
		"nl_BE": "Vlaams",
		"pa":    "ਪੰਜਾਬੀ",
		"pl":    "Język polski",
		"pl_PL": "Język polski (Poland)",
		"pt":    "Português",
		"pt_BR": "Português do Brasil",
		"ru":    "русский язык",
		"si_LK": "සිංහල",
		"sk":    "Slovenčina",
		"sl":    "Slovenski jezik",
		"sl_SI": "Slovenski jezik (Slovenia)",
		"sr":    "српски језик",
		"sv":    "Svenska",
		"te_IN": "తెలుగు",
		"th":    "ไทย",
		"tr":    "Türkçe",
		"uk":    "українська мова",
		"zh_CN": "中文简体",
		"zh_TW": "中文繁體",
	}

	// for all folders in locale which match a locale from https://www.transifex.com/api/2/languages/
	// use the language name unless we have an override
	webLocales, err := FetchTranslationLocales(base)
	if err != nil {
		log.Printf("Failed to get up to date language list, using fallback.")
		return haveTranslatedNames
	}

	return GetInstalledLocales(base, webLocales, haveTranslatedNames)
}

func FetchTranslationLocales(base string) (map[string]locale, error) {
	file, err := os.Open(path.Join(base, "data/langs"))
	if err != nil {
		return nil, err
	}
	defer file.Close()

	webLocales := make(map[string]locale)
	// Parse the api response into a list of possible locales
	dec := json.NewDecoder(file)
	for {
		var webList []locale
		if err = dec.Decode(&webList); err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}

		// The api returns an array, so we need to map it
		for _, l := range webList {
			webLocales[l.Code] = l
		}
	}

	return webLocales, nil
}

// Get a list of all languages installed in our locale folder with translations if available
func GetInstalledLocales(base string, webLocales map[string]locale, nameTranslations map[string]string) map[string]string {
	localFiles, err := ioutil.ReadDir(path.Join(base, "locale"))

	if err != nil {
		log.Print("No locales found in 'locale'. Try running 'make i18n'.")
		log.Fatal(err)
	}

	locales := make(map[string]string, len(localFiles))
	locales["en_US"] = "English"

	for _, f := range localFiles {
		// TODO: Ensure a language has 100% of the template file
		// Currently this is what should be on the torcheck_completed
		// branch on the translations git should be, so we don't really
		// have to check it in theory...
		code := f.Name()

		// Only accept folders which have corresponding locale
		if !f.IsDir() || webLocales[code] == (locale{}) {
			continue
		}

		// If we have a translated name for a given locale, use it
		if transName := nameTranslations[code]; transName != "" {
			locales[code] = transName
		} else {
			log.Print("No translated name for code: " + code)
			locales[code] = webLocales[code].Name
		}
	}

	return locales
}
<|endoftext|>