<commit_before>package main

import (
	"bufio"
	"errors"
	"io/ioutil"
	"net"
	"os"
	"strings"

	"github.com/kiesel/wormhole-go/lib"

	"gopkg.in/op/go-logging.v1"
	"gopkg.in/yaml.v2"
)

type Error interface {
	Error() string
}

var log = logging.MustGetLogger("wormhole")
var format = logging.MustStringFormatter(
	"%{color}%{time:15:04:05.000} %{level:.1s} %{id:03x}%{color:reset} >> %{message}",
)
var config wormhole.WormholeConfig

func main() {

	// Setup logging
	logbackend := logging.NewLogBackend(os.Stdout, "", 0)
	logbackendformatter := logging.NewBackendFormatter(logbackend, format)
	logging.SetBackend(logbackendformatter)

	// Read config
	log.Info("Trying to parse wormhole configuration from " + wormhole.GetDefaultConfig())
	source, err := ioutil.ReadFile(wormhole.GetDefaultConfig())
	if err != nil {
		log.Fatal(err)
	}

	err = yaml.Unmarshal(source, &config)
	if err != nil {
		log.Fatal(err)
	}
	log.Debug("Configuration: %v", config)

	// Start main
	log.Info("Wormhole server starting ...")

	l, err := net.Listen("tcp4", config.GetAddr())
	if err != nil {
		log.Fatal(err)
	}

	log.Info("Listening at " + l.Addr().String())

	defer l.Close()
	for {
		// Wait for connection
		conn, err := l.Accept()
		if err != nil {
			log.Fatal(err)
		}

		log.Debug("Received connection from %s", conn.RemoteAddr().String())

		// Handle connection
		go handleConnection(conn)
	}
}

func handleConnection(c net.Conn) {
	defer c.Close()

	line, err := bufio.NewReader(c).ReadString('\n')
	if err != nil {
		log.Fatal(err)
	}

	writer := bufio.NewWriter(c)

	log.Debug("[%s] %s", c.RemoteAddr().String(), line)
	resp, err := handleLine(c, line)

	if err != nil {
		writer.WriteString("[ERR] ")
		writer.WriteString(err.Error())
	} else {
		writer.WriteString("[OK] ")
		writer.WriteString(resp)
	}

	writer.WriteString("\n")
	writer.Flush()
}

func handleLine(c net.Conn, line string) (resp string, err Error) {
	parts := strings.Split(strings.TrimSpace(line), " ")

	log.Debug("Extracted parts %s", parts)
	if len(parts) < 2 {
		log.Warning("Too little parts, quit.")
		return "", errors.New("Too few words, expected at least 2.")
	}

	switch strings.ToLower(parts[0]) {
	case "invoke":
		return handleInvocation(parts[1], parts[2:])
	case "exit":
		return handleExit()
	case "reload":
		return handleReload()
	}

	return "", errors.New("Unknown command, expected one of " + config.AvailableApps())
}

func handleInvocation(mapping string, args []string) (resp string, err Error) {
	app, err := config.GetAppWith(mapping, args)
	if err != nil {
		return "", err
	}

	log.Info("Invoking '%v' (mapped by %v) with args: %v", app.Executable, mapping, args)
	go wormhole.ExecuteCommand(app.Executable, args...)

	return "Started " + mapping, nil
}

func handleExit() (response string, err Error) {
	return "Bye!", nil
}

func handleReload() (response string, err Error) {
	return "Reloading...", nil
}
<commit_msg>Refactor reading configuration into a separate function<commit_after>package main

import (
	"bufio"
	"errors"
	"io/ioutil"
	"net"
	"os"
	"strings"

	"github.com/kiesel/wormhole-go/lib"

	"gopkg.in/op/go-logging.v1"
	"gopkg.in/yaml.v2"
)

type Error interface {
	Error() string
}

var log = logging.MustGetLogger("wormhole")
var format = logging.MustStringFormatter(
	"%{color}%{time:15:04:05.000} %{level:.1s} %{id:03x}%{color:reset} >> %{message}",
)
var config wormhole.WormholeConfig

func readConfiguration() (err Error) {
	var newConfig wormhole.WormholeConfig

	log.Info("Trying to parse wormhole configuration from " + wormhole.GetDefaultConfig())

	source, err := ioutil.ReadFile(wormhole.GetDefaultConfig())
	if err != nil {
		log.Critical(err.Error())

		return err
	}

	err = yaml.Unmarshal(source, &newConfig)
	if err != nil {
		log.Critical(err.Error())
		return err
	}

	// Now replace existing config with new
	log.Debug("New configuration %v", config)
	config = newConfig

	return nil
}

func main() {

	// Setup logging
	logbackend := logging.NewLogBackend(os.Stdout, "", 0)
	logbackendformatter := logging.NewBackendFormatter(logbackend, format)
	logging.SetBackend(logbackendformatter)

	// Read config
	readConfiguration()

	// Start main
	log.Info("Wormhole server starting ...")

	l, err := net.Listen("tcp4", config.GetAddr())
	if err != nil {
		log.Fatal(err)
	}

	log.Info("Listening at " + l.Addr().String())

	defer l.Close()
	for {
		// Wait for connection
		conn, err := l.Accept()
		if err != nil {
			log.Fatal(err)
		}

		log.Debug("Received connection from %s", conn.RemoteAddr().String())

		// Handle connection
		go handleConnection(conn)
	}
}

func handleConnection(c net.Conn) {
	defer c.Close()

	line, err := bufio.NewReader(c).ReadString('\n')
	if err != nil {
		log.Fatal(err)
	}

	writer := bufio.NewWriter(c)

	log.Debug("[%s] %s", c.RemoteAddr().String(), line)
	resp, err := handleLine(c, line)

	if err != nil {
		writer.WriteString("[ERR] ")
		writer.WriteString(err.Error())
	} else {
		writer.WriteString("[OK] ")
		writer.WriteString(resp)
	}

	writer.WriteString("\n")
	writer.Flush()
}

func handleLine(c net.Conn, line string) (resp string, err Error) {
	parts := strings.Split(strings.TrimSpace(line), " ")

	log.Debug("Extracted parts %s", parts)
	if len(parts) < 2 {
		log.Warning("Too little parts, quit.")
		return "", errors.New("Too few words, expected at least 2.")
	}

	switch strings.ToLower(parts[0]) {
	case "invoke":
		return handleInvocation(parts[1], parts[2:])
	case "exit":
		return handleExit()
	case "reload":
		return handleReload()
	}

	return "", errors.New("Unknown command, expected one of " + config.AvailableApps())
}

func handleInvocation(mapping string, args []string) (resp string, err Error) {
	app, err := config.GetAppWith(mapping, args)
	if err != nil {
		return "", err
	}

	log.Info("Invoking '%v' (mapped by %v) with args: %v", app.Executable, mapping, args)
	go wormhole.ExecuteCommand(app.Executable, args...)

	return "Started " + mapping, nil
}

func handleExit() (response string, err Error) {
	return "Bye!", nil
}

func handleReload() (response string, err Error) {
	if err := readConfiguration(); err != nil {
		return "", err
	}

	return "Re-read configuration.", nil
}
<|endoftext|>


<commit_before>package awsCertlint

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/mozilla/tls-observatory/certificate"
	"github.com/mozilla/tls-observatory/worker"
)

var (
	workerName = "awsCertlint"
	workerDesc = "Runs the awslabs certificate linter and saves output"

	certlintDirectory = "/go/certlint" // path from tools/Dockerfile-scanner
)

type Result struct {
	Bugs          []string `json:"bugs"`
	Informational []string `json:"informational"`
	Notices       []string `json:"notices"`
	Warnings      []string `json:"warnings"`
	Errors        []string `json:"errors"`
	FatalErrors   []string `json:"fatalErrors"`
}

func init() {
	// override certlintDirectory if TLS_AWSCERTLINT_DIR
	if path := os.Getenv("TLS_AWSCERTLINT_DIR"); path != "" {
		certlintDirectory = path
	}

	runner := new(eval)
	worker.RegisterWorker(workerName, worker.Info{Runner: runner, Description: workerDesc})
}

type eval struct{}

func (e eval) Run(in worker.Input, resChan chan worker.Result) {
	result := worker.Result{
		WorkerName: workerName,
	}
	lintResult, err := e.runCertlint(in.Certificate)
	if err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("%s for certificate %s", err.Error(), in.Certificate.ID)},
			Result:     nil,
		}
		return
	}

	// Marshal the response
	bs, err := json.Marshal(&lintResult)
	if err != nil {
		result.Success = false
		result.Errors = append(result.Errors, err.Error())
	} else {
		result.Success = true
		result.Result = bs
	}
	resChan <- result
}

func (e eval) runCertlint(cert certificate.Certificate) (*Result, error) {
	tmp, err := ioutil.TempFile("", "awslabs-certlint")
	if err != nil {
		return nil, fmt.Errorf("error creating temp file for certificate %s", cert.ID)
	}
	defer os.Remove(tmp.Name())
	x509Cert, err := cert.ToX509()
	if err != nil {
		return nil, fmt.Errorf("error converting certificate %s to x509.Certificate", cert.ID)
	}
	if err := ioutil.WriteFile(tmp.Name(), x509Cert.Raw, 0644); err != nil {
		return nil, fmt.Errorf("error writing x509.Certificate to temp file, err=%v", err)
	}

	// Run certlint over certificate
	cmd := exec.Command("ruby", "-I", "lib:ext", "bin/certlint", tmp.Name())
	cmd.Dir = certlintDirectory

	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("error starting awslabs/certlint on certificate %s, err=%v, out=%q", cert.ID, err, strings.TrimSpace(stderr.String()))
	}

	waitChan := make(chan error, 1)
	go func() {
		waitChan <- cmd.Wait()
	}()

	select {
	case <-time.After(30 * time.Second):
		err := cmd.Process.Kill()
		return nil, fmt.Errorf("timed out waiting for awslabs/certlint on certificate %s, kill error=%v", cert.ID, err)
	case err := <-waitChan:
		if err != nil {
			return nil, fmt.Errorf("error running awslabs/certlint on certificate %s, err=%v, out=%q", cert.ID, err, strings.TrimSpace(stderr.String()))
		}
	}

	return e.parseResponse(stdout)
}

// From: https://github.com/awslabs/certlint#output
//
// * B: Bug. Your certificate has a feature not handled by certlint.
// * I: Information.  These are purely informational; no action is needed.
// * N: Notice.  These are items known to cause issues with one or more implementations of certificate processing but are not errors according to the standard.
// * W: Warning.  These are issues where a standard recommends differently but the standard uses terms such as "SHOULD" or "MAY".
// * E: Error.  These are issues where the certificate is not compliant with the standard.
// * F: Fatal Error.  These errors are fatal to the checks and prevent most further checks from being executed.  These are extremely bad errors.
func (e eval) parseResponse(resp bytes.Buffer) (*Result, error) {
	out := &Result{}

	r := bufio.NewScanner(&resp)
	for r.Scan() {
		line := strings.TrimSpace(r.Text())
		if line == "" {
			continue
		}

		// Match first letter of each line, which signifies its type.
		switch line[0] {
		case 'B':
			out.Bugs = append(out.Bugs, strings.TrimSpace(line[2:]))
		case 'I':
			out.Informational = append(out.Informational, strings.TrimSpace(line[2:]))
		case 'N':
			out.Notices = append(out.Notices, strings.TrimSpace(line[2:]))
		case 'W':
			out.Warnings = append(out.Warnings, strings.TrimSpace(line[2:]))
		case 'E':
			out.Errors = append(out.Errors, strings.TrimSpace(line[2:]))
		case 'F':
			out.FatalErrors = append(out.FatalErrors, strings.TrimSpace(line[2:]))
		}
	}
	return out, nil
}

// AnalysisPrinter outputs the results from awslabs/certlint over a given certificate
func (e eval) AnalysisPrinter(input []byte, printAll interface{}) (results []string, err error) {
	var result Result
	if err := json.Unmarshal(input, &result); err != nil {
		return nil, fmt.Errorf("awsCertlint Worker: failed to parse results: err=%v", err)
	}

	// Build results for webview
	for i := range result.FatalErrors {
		results = append(results, fmt.Sprintf(" - Fatal Error: %s", result.FatalErrors[i]))
	}
	for i := range result.Errors {
		results = append(results, fmt.Sprintf(" - Error: %s", result.Errors[i]))
	}
	for i := range result.Warnings {
		results = append(results, fmt.Sprintf(" - Warning: %s", result.Warnings[i]))
	}
	for i := range result.Informational {
		results = append(results, fmt.Sprintf(" - Information: %s", result.Informational[i]))
	}
	for i := range result.Notices {
		results = append(results, fmt.Sprintf(" - Notice: %s", result.Notices[i]))
	}
	for i := range result.Bugs {
		results = append(results, fmt.Sprintf(" - Bug: %s", result.Bugs[i]))
	}

	// Add header and summary as first two lines
	headers := []string{
		"* awslabs/certlint",
	}

	// We only want one summary line, so match in order of severity
	switch true {
	case len(result.FatalErrors) > 0 || len(result.Errors) > 0:
		headers = append(headers, fmt.Sprintf(" - %d errors, %d fatal", len(result.Errors), len(result.FatalErrors)))
	case len(result.Warnings) > 0:
		headers = append(headers, fmt.Sprintf(" - %d warnings found", len(result.Warnings)))
	}

	// Add header(s) as first line, print notice if nothing was found
	results = append(headers, results...)
	if len(results) == 1 {
		results = append(results, " - No messages")
	}
	return results, nil
}
<commit_msg>worker/awsCertlint: fixup printf interpolation<commit_after>package awsCertlint

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/mozilla/tls-observatory/certificate"
	"github.com/mozilla/tls-observatory/worker"
)

var (
	workerName = "awsCertlint"
	workerDesc = "Runs the awslabs certificate linter and saves output"

	certlintDirectory = "/go/certlint" // path from tools/Dockerfile-scanner
)

type Result struct {
	Bugs          []string `json:"bugs"`
	Informational []string `json:"informational"`
	Notices       []string `json:"notices"`
	Warnings      []string `json:"warnings"`
	Errors        []string `json:"errors"`
	FatalErrors   []string `json:"fatalErrors"`
}

func init() {
	// override certlintDirectory if TLS_AWSCERTLINT_DIR
	if path := os.Getenv("TLS_AWSCERTLINT_DIR"); path != "" {
		certlintDirectory = path
	}

	runner := new(eval)
	worker.RegisterWorker(workerName, worker.Info{Runner: runner, Description: workerDesc})
}

type eval struct{}

func (e eval) Run(in worker.Input, resChan chan worker.Result) {
	result := worker.Result{
		WorkerName: workerName,
	}
	lintResult, err := e.runCertlint(in.Certificate)
	if err != nil {
		resChan <- worker.Result{
			Success:    false,
			WorkerName: workerName,
			Errors:     []string{fmt.Sprintf("%s for certificate %d", err.Error(), in.Certificate.ID)},
			Result:     nil,
		}
		return
	}

	// Marshal the response
	bs, err := json.Marshal(&lintResult)
	if err != nil {
		result.Success = false
		result.Errors = append(result.Errors, err.Error())
	} else {
		result.Success = true
		result.Result = bs
	}
	resChan <- result
}

func (e eval) runCertlint(cert certificate.Certificate) (*Result, error) {
	tmp, err := ioutil.TempFile("", "awslabs-certlint")
	if err != nil {
		return nil, fmt.Errorf("error creating temp file for certificate %d", cert.ID)
	}
	defer os.Remove(tmp.Name())
	x509Cert, err := cert.ToX509()
	if err != nil {
		return nil, fmt.Errorf("error converting certificate %d to x509.Certificate", cert.ID)
	}
	if err := ioutil.WriteFile(tmp.Name(), x509Cert.Raw, 0644); err != nil {
		return nil, fmt.Errorf("error writing x509.Certificate to temp file, err=%v", err)
	}

	// Run certlint over certificate
	cmd := exec.Command("ruby", "-I", "lib:ext", binaryPath, tmp.Name())
	cmd.Dir = certlintDirectory

	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("error starting awslabs/certlint on certificate %d, err=%v, out=%q", cert.ID, err, strings.TrimSpace(stderr.String()))
	}

	waitChan := make(chan error, 1)
	go func() {
		waitChan <- cmd.Wait()
	}()

	select {
	case <-time.After(30 * time.Second):
		err := cmd.Process.Kill()
		return nil, fmt.Errorf("timed out waiting for awslabs/certlint on certificate %d, kill error=%v", cert.ID, err)
	case err := <-waitChan:
		if err != nil {
			return nil, fmt.Errorf("error running awslabs/certlint on certificate %d, err=%v, out=%q", cert.ID, err, strings.TrimSpace(stderr.String()))
		}
	}

	return e.parseResponse(stdout)
}

// From: https://github.com/awslabs/certlint#output
//
// * B: Bug. Your certificate has a feature not handled by certlint.
// * I: Information.  These are purely informational; no action is needed.
// * N: Notice.  These are items known to cause issues with one or more implementations of certificate processing but are not errors according to the standard.
// * W: Warning.  These are issues where a standard recommends differently but the standard uses terms such as "SHOULD" or "MAY".
// * E: Error.  These are issues where the certificate is not compliant with the standard.
// * F: Fatal Error.  These errors are fatal to the checks and prevent most further checks from being executed.  These are extremely bad errors.
func (e eval) parseResponse(resp bytes.Buffer) (*Result, error) {
	out := &Result{}

	r := bufio.NewScanner(&resp)
	for r.Scan() {
		line := strings.TrimSpace(r.Text())
		if line == "" {
			continue
		}

		// Match first letter of each line, which signifies its type.
		switch line[0] {
		case 'B':
			out.Bugs = append(out.Bugs, strings.TrimSpace(line[2:]))
		case 'I':
			out.Informational = append(out.Informational, strings.TrimSpace(line[2:]))
		case 'N':
			out.Notices = append(out.Notices, strings.TrimSpace(line[2:]))
		case 'W':
			out.Warnings = append(out.Warnings, strings.TrimSpace(line[2:]))
		case 'E':
			out.Errors = append(out.Errors, strings.TrimSpace(line[2:]))
		case 'F':
			out.FatalErrors = append(out.FatalErrors, strings.TrimSpace(line[2:]))
		}
	}
	return out, nil
}

// AnalysisPrinter outputs the results from awslabs/certlint over a given certificate
func (e eval) AnalysisPrinter(input []byte, printAll interface{}) (results []string, err error) {
	var result Result
	if err := json.Unmarshal(input, &result); err != nil {
		return nil, fmt.Errorf("awsCertlint Worker: failed to parse results: err=%v", err)
	}

	// Build results for webview
	for i := range result.FatalErrors {
		results = append(results, fmt.Sprintf(" - Fatal Error: %s", result.FatalErrors[i]))
	}
	for i := range result.Errors {
		results = append(results, fmt.Sprintf(" - Error: %s", result.Errors[i]))
	}
	for i := range result.Warnings {
		results = append(results, fmt.Sprintf(" - Warning: %s", result.Warnings[i]))
	}
	for i := range result.Informational {
		results = append(results, fmt.Sprintf(" - Information: %s", result.Informational[i]))
	}
	for i := range result.Notices {
		results = append(results, fmt.Sprintf(" - Notice: %s", result.Notices[i]))
	}
	for i := range result.Bugs {
		results = append(results, fmt.Sprintf(" - Bug: %s", result.Bugs[i]))
	}

	// Add header and summary as first two lines
	headers := []string{
		"* awslabs/certlint",
	}

	// We only want one summary line, so match in order of severity
	switch true {
	case len(result.FatalErrors) > 0 || len(result.Errors) > 0:
		headers = append(headers, fmt.Sprintf(" - %d errors, %d fatal", len(result.Errors), len(result.FatalErrors)))
	case len(result.Warnings) > 0:
		headers = append(headers, fmt.Sprintf(" - %d warnings found", len(result.Warnings)))
	}

	// Add header(s) as first line, print notice if nothing was found
	results = append(headers, results...)
	if len(results) == 1 {
		results = append(results, " - No messages")
	}
	return results, nil
}
<|endoftext|>


<commit_before>package tutum

import "encoding/json"

type StackListResponse struct {
	Objects []StackShort `json: "objects"`
}

type StackShort struct {
	Deployed_datetime  string   `json:"deployed_datetime"`
	Destroyed_datetime string   `json:"destroyed_datetime"`
	Name               string   `json:"name"`
	Resource_uri       string   `json:"resource_uri"`
	Service            []string `json:"services"`
	State              string   `json:"state"`
	Synchronized       bool     `json:"synchronized"`
	Uuid               string   `json:"uuid"`
}

type Stack struct {
	Deployed_datetime  string    `json:"deployed_datetime"`
	Destroyed_datetime string    `json:"destroyed_datetime"`
	Name               string    `json:"name"`
	Resource_uri       string    `json:"resource_uri"`
	Service            []Service `json:"services"`
	State              string    `json:"state"`
	Synchronized       bool      `json:"synchronized"`
	Uuid               string    `json:"uuid"`
}

func ListStacks() ([]StackShort, error) {
	url := "stack/"
	request := "GET"

	//Empty Body Request
	body := []byte(`{}`)
	var response StackListResponse

	data, err := TutumCall(url, request, body)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		return nil, err
	}

	return response.Objects, nil
}

func GetStack(uuid string) (Stack, error) {

	url := "stack/" + uuid + "/"
	request := "GET"
	//Empty Body Request
	body := []byte(`{}`)
	var response Stack

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func ExportStack(uuid string) (string, error) {

	url := "stack/" + uuid + "/export/"
	request := "GET"
	//Empty Body Request
	body := []byte(`{}`)

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	s := string(data)

	return s, nil
}

func CreateStack(newStack []byte) (Stack, error) {

	url := "stack/"
	request := "POST"

	var response Stack

	data, err := TutumCall(url, request, newStack)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}
<commit_msg>Add UpdateStack and CreateStack methods<commit_after>package tutum

import "encoding/json"

type StackListResponse struct {
	Objects []StackShort `json: "objects"`
}

type StackShort struct {
	Deployed_datetime  string   `json:"deployed_datetime"`
	Destroyed_datetime string   `json:"destroyed_datetime"`
	Name               string   `json:"name"`
	Resource_uri       string   `json:"resource_uri"`
	Service            []string `json:"services"`
	State              string   `json:"state"`
	Synchronized       bool     `json:"synchronized"`
	Uuid               string   `json:"uuid"`
}

type Stack struct {
	Deployed_datetime  string    `json:"deployed_datetime"`
	Destroyed_datetime string    `json:"destroyed_datetime"`
	Name               string    `json:"name"`
	Resource_uri       string    `json:"resource_uri"`
	Service            []Service `json:"services"`
	State              string    `json:"state"`
	Synchronized       bool      `json:"synchronized"`
	Uuid               string    `json:"uuid"`
}

func ListStacks() ([]StackShort, error) {
	url := "stack/"
	request := "GET"

	//Empty Body Request
	body := []byte(`{}`)
	var response StackListResponse

	data, err := TutumCall(url, request, body)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		return nil, err
	}

	return response.Objects, nil
}

func GetStack(uuid string) (Stack, error) {

	url := "stack/" + uuid + "/"
	request := "GET"
	//Empty Body Request
	body := []byte(`{}`)
	var response Stack

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func ExportStack(uuid string) (string, error) {

	url := "stack/" + uuid + "/export/"
	request := "GET"
	//Empty Body Request
	body := []byte(`{}`)

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	s := string(data)

	return s, nil
}

func CreateStack(newStack []byte) (Stack, error) {

	url := "stack/"
	request := "POST"

	var response Stack

	data, err := TutumCall(url, request, newStack)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func UpdateStack(uuid string, updatedStack []byte) (Stack, error) {

	url := "stack/" + uuid + "/"
	request := "PATCH"
	var response Stack

	data, err := TutumCall(url, request, updatedStack)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

func StartStack(uuid string) (Service, error) {

	url := "stack/" + uuid + "/start/"
	request := "POST"
	//Empty Body Request
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}
<|endoftext|>


<commit_before>// A tool to check params and take log for Weixin Pay Merchents.
package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"io/ioutil"
	"log"
	"math"
	"net"
	"net/http"
	"os"
	"time"

	"gopkg.in/natefinch/lumberjack.v2"

	"github.com/beevik/ntp"
	"github.com/miekg/dns"
)

// Configuration to load from json
type Configuration struct {
	// 监听地址, like:"0.0.0.0:80"
	Listen struct {
		// http协议监听地址
		HTTP string
		// https协议监听地址
		HTTPS string
	}
	// Key 秘钥，在商户平台的API安全里设置
	Key string
	// UseCert 是否使用证书
	UseCert bool `json:"use_cert"`
	// 证书配置
	Cert struct {
		// CertFile 证书文件路径
		CertFile string
		// KeyFile 秘钥文件路径
		KeyFile string
		// Ca CA证书路径
		Ca string
	}
	// Resign 是否重签名
	Resign bool
	// LogToFile 是否记录到文件
	LogToFile bool `json:"log_to_file"`
	// Diagnosis 是否诊断
	Diagnosis bool
	// IsLoad 判断是否已经载入配置
	IsLoad bool
}

var config Configuration
var cert tls.Certificate
var caCert []byte

func init() {
	// init logger
	config.IsLoad = false
	configFile, _ := os.Open("config.json")
	defer configFile.Close()
	decoder := json.NewDecoder(configFile)

	err := decoder.Decode(&config)
	if err != nil {
		log.Println("error:", err)
	}
	log.Println("listen address:", config.Listen.HTTP, config.Listen.HTTPS)
	config.IsLoad = true

	if config.LogToFile {
		log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
		l := &lumberjack.Logger{
			Filename:  "log.txt",
			MaxSize:   20,
			LocalTime: true,
		}
		l.Rotate()
		log.SetOutput(l)
	}

	//载入证书
	if config.UseCert {
		// Load client cert
		cert, err = tls.LoadX509KeyPair(config.Cert.CertFile, config.Cert.KeyFile)
		if err != nil {
			log.Fatal(err)
		}

		// Load CA cert
		caCert, err = ioutil.ReadFile(config.Cert.Ca)
		if err != nil {
			log.Fatal(err)
		}
	}
}

func main() {
	if config.Diagnosis {
		// do some test here
		// 0. Collect Infos
		log.Println("[INFO] ============")
		log.Println("[INFO] 0. Address Info")
		localAddrs, err := net.InterfaceAddrs()
		log.Printf("%v", localAddrs)
		// 1. DNS lookup test
		log.Println("[INFO] ============")
		log.Println("[INFO] 1. DNS Lookup test")
		addr, err := net.LookupHost("api.mch.weixin.qq.com")
		log.Printf("[INFO] Local DNS lookup result: %v", addr)

		c := dns.Client{}
		m := dns.Msg{}
		m.SetQuestion("api.mch.weixin.qq.com.", dns.TypeA)
		r, t, err := c.Exchange(&m, "119.29.29.29:53")
		if err != nil {
			log.Printf("[FATAL] Get ip from 119.29.29.29 failed. err:%s", err)
		}
		log.Printf("[INFO] Request took time: %v", t)
		if len(r.Answer) == 0 {
			log.Fatal("No results")
		}
		log.Printf("[INFO] Remote DNS lookup result:%v", r)
		//2. local time and remote time
		log.Println("[INFO] ============")
		log.Println("[INFO] 2. Time test")
		localTime := time.Now()
		log.Printf("[INFO] Local Time:%v", localTime.Unix())
		log.Println("[INFO] Getting remote time, should not wait too long")
		remoteTime, err := ntp.Time("cn.pool.ntp.org")
		if err != nil {
			log.Printf("[FATAL] Get time from cn.pool.ntp.org failed. err:%s", err)
		}
		log.Printf("[INFO] Remote Time:%v", remoteTime.Unix())
		if math.Abs(float64(localTime.Unix()-remoteTime.Unix())) > 10 {
			log.Printf("[WARNING] Time needs Sync:%v", remoteTime.Unix())
		}

		log.Println("[INFO] Test done.")
	}
	go func() {
		err2 := http.ListenAndServe(config.Listen.HTTP, http.HandlerFunc(mainHandler))
		if err2 != nil {
			log.Fatal("ListenAndServe:", err2)
		}
	}()

	err := http.ListenAndServeTLS(config.Listen.HTTPS, "server.crt", "server.key", http.HandlerFunc(mainHandler))
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

// mainHandler HTTP Handler func.
func mainHandler(w http.ResponseWriter, req *http.Request) {
	//if req.Method != "POST" {
	log.Println(req.URL.String())
	body, err := ioutil.ReadAll(req.Body)
	defer req.Body.Close()
	mapData, err := XMLStructToMap(body)
	log.Printf("[INFO][IN] Request content:%v", string(body))

	// sign
	eq, err := CompareSignature(body)
	if !eq {
		log.Println("[WARNING] Sign not match.")
		log.Printf("original sign: %s", mapData["sign"])
		newSign, err2 := CalculateSignature(mapData, config.Key)
		log.Printf("calculated sign: %s", newSign)
		if config.Resign {
			mapData["sign"] = newSign
			body, err2 = MapToXML(mapData)
			if err2 != nil {
				log.Fatal(err.Error())
			}
			log.Printf("[INFO][IN] Renewed request content:%v", string(body))
		}
	}

	newReq, err := http.NewRequest(req.Method, req.URL.String(), bytes.NewReader(body))
	newReq.Header = req.Header
	newReq.URL.Host = "api.mch.weixin.qq.com"
	newReq.Host = newReq.URL.Host
	newReq.URL.Scheme = "https"

	var client *http.Client
	//cert
	if config.UseCert {
		caCertPool := x509.NewCertPool()
		caCertPool.AppendCertsFromPEM(caCert)
		// Setup HTTPS client
		tlsConfig := &tls.Config{
			Certificates: []tls.Certificate{cert},
			RootCAs:      caCertPool,
		}
		tlsConfig.BuildNameToCertificate()
		transport := &http.Transport{TLSClientConfig: tlsConfig}
		client = &http.Client{Transport: transport}
	} else {
		client = &http.Client{}
	}

	resp, err := client.Do(newReq)

	if err != nil {
		log.Fatal(err.Error())
	}

	log.Println(resp.StatusCode)

	//io.Copy(w, resp.Body)
	respBody, err := ioutil.ReadAll(resp.Body)
	log.Printf("[INFO][IN] Respond content:%v", string(respBody))
	w.Write(respBody)
	defer resp.Body.Close()
	if err != nil {
		log.Fatal(err.Error())
	}

	return
	//}
}
<commit_msg>now use keep-alive<commit_after>// A tool to check params and take log for Weixin Pay Merchents.
package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"io/ioutil"
	"log"
	"math"
	"net"
	"net/http"
	"net/http/httptrace"
	"os"
	"time"

	"gopkg.in/natefinch/lumberjack.v2"

	"github.com/beevik/ntp"
	"github.com/miekg/dns"
)

// Configuration to load from json
type Configuration struct {
	// 监听地址, like:"0.0.0.0:80"
	Listen struct {
		// http协议监听地址
		HTTP string
		// https协议监听地址
		HTTPS string
	}
	// Key 秘钥，在商户平台的API安全里设置
	Key string
	// UseCert 是否使用证书
	UseCert bool `json:"use_cert"`
	// 证书配置
	Cert struct {
		// CertFile 证书文件路径
		CertFile string
		// KeyFile 秘钥文件路径
		KeyFile string
		// Ca CA证书路径
		Ca string
	}
	// Resign 是否重签名
	Resign bool
	// LogToFile 是否记录到文件
	LogToFile bool `json:"log_to_file"`
	// Diagnosis 是否诊断
	Diagnosis bool
	// IsLoad 判断是否已经载入配置
	IsLoad bool
}

var config Configuration
var cert tls.Certificate
var caCert []byte

var httpsClient *http.Client

func gotConnPrintReuse(info httptrace.GotConnInfo) {
	log.Println("Is reused:", info.Reused)
}

func init() {
	// init logger
	config.IsLoad = false
	configFile, _ := os.Open("config.json")
	defer configFile.Close()
	decoder := json.NewDecoder(configFile)

	err := decoder.Decode(&config)
	if err != nil {
		log.Println("error:", err)
	}
	log.Println("listen address:", config.Listen.HTTP, config.Listen.HTTPS)
	config.IsLoad = true

	if config.LogToFile {
		log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
		l := &lumberjack.Logger{
			Filename:  "log.txt",
			MaxSize:   20,
			LocalTime: true,
		}
		l.Rotate()
		log.SetOutput(l)
	}

	//载入证书
	if config.UseCert {
		// Load client cert
		cert, err = tls.LoadX509KeyPair(config.Cert.CertFile, config.Cert.KeyFile)
		if err != nil {
			log.Fatal(err)
		}

		// Load CA cert
		caCert, err = ioutil.ReadFile(config.Cert.Ca)
		if err != nil {
			log.Fatal(err)
		}
	}

	if config.UseCert {
		caCertPool := x509.NewCertPool()
		caCertPool.AppendCertsFromPEM(caCert)
		// Setup HTTPS client
		tlsConfig := &tls.Config{
			Certificates: []tls.Certificate{cert},
			RootCAs:      caCertPool,
		}
		tlsConfig.BuildNameToCertificate()
		transport := &http.Transport{
			TLSClientConfig:     tlsConfig,
			IdleConnTimeout:     8 * time.Second,
			MaxIdleConnsPerHost: 10,
		}
		httpsClient = &http.Client{Transport: transport}
	} else {
		transport := &http.Transport{
			IdleConnTimeout:     8 * time.Second,
			MaxIdleConnsPerHost: 10,
		}
		httpsClient = &http.Client{Transport: transport}
	}
}

func main() {
	if config.Diagnosis {
		// do some test here
		// 0. Collect Infos
		log.Println("[INFO] ============")
		log.Println("[INFO] 0. Address Info")
		localAddrs, err := net.InterfaceAddrs()
		log.Printf("%v", localAddrs)
		// 1. DNS lookup test
		log.Println("[INFO] ============")
		log.Println("[INFO] 1. DNS Lookup test")
		addr, err := net.LookupHost("api.mch.weixin.qq.com")
		log.Printf("[INFO] Local DNS lookup result: %v", addr)

		c := dns.Client{}
		m := dns.Msg{}
		m.SetQuestion("api.mch.weixin.qq.com.", dns.TypeA)
		r, t, err := c.Exchange(&m, "119.29.29.29:53")
		if err != nil {
			log.Printf("[FATAL] Get ip from 119.29.29.29 failed. err:%s", err)
		}
		log.Printf("[INFO] Request took time: %v", t)
		if len(r.Answer) == 0 {
			log.Fatal("No results")
		}
		log.Printf("[INFO] Remote DNS lookup result:%v", r)
		//2. local time and remote time
		log.Println("[INFO] ============")
		log.Println("[INFO] 2. Time test")
		localTime := time.Now()
		log.Printf("[INFO] Local Time:%v", localTime.Unix())
		log.Println("[INFO] Getting remote time, should not wait too long")
		remoteTime, err := ntp.Time("cn.pool.ntp.org")
		if err != nil {
			log.Printf("[FATAL] Get time from cn.pool.ntp.org failed. err:%s", err)
		}
		log.Printf("[INFO] Remote Time:%v", remoteTime.Unix())
		if math.Abs(float64(localTime.Unix()-remoteTime.Unix())) > 10 {
			log.Printf("[WARNING] Time needs Sync:%v", remoteTime.Unix())
		}

		log.Println("[INFO] Test done.")
	}
	go func() {
		err2 := http.ListenAndServe(config.Listen.HTTP, http.HandlerFunc(mainHandler))
		if err2 != nil {
			log.Fatal("ListenAndServe:", err2)
		}
	}()

	err := http.ListenAndServeTLS(config.Listen.HTTPS, "server.crt", "server.key", http.HandlerFunc(mainHandler))
	if err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

// mainHandler HTTP Handler func.
func mainHandler(w http.ResponseWriter, req *http.Request) {
	//if req.Method != "POST" {
	log.Println(req.URL.String())
	body, err := ioutil.ReadAll(req.Body)
	defer req.Body.Close()
	mapData, err := XMLStructToMap(body)
	log.Printf("[INFO][IN] Request content:%v", string(body))

	// sign
	eq, err := CompareSignature(body)
	if !eq {
		log.Println("[WARNING] Sign not match.")
		log.Printf("original sign: %s", mapData["sign"])
		newSign, err2 := CalculateSignature(mapData, config.Key)
		log.Printf("calculated sign: %s", newSign)
		if config.Resign {
			mapData["sign"] = newSign
			body, err2 = MapToXML(mapData)
			if err2 != nil {
				log.Fatal(err.Error())
			}
			log.Printf("[INFO][IN] Renewed request content:%v", string(body))
		}
	}

	newReq, err := http.NewRequest(req.Method, req.URL.String(), bytes.NewReader(body))
	newReq.Header = req.Header
	newReq.URL.Host = "api.mch.weixin.qq.com"
	newReq.Host = newReq.URL.Host
	newReq.URL.Scheme = "https"

	//trace
	trace := &httptrace.ClientTrace{
		GotConn: gotConnPrintReuse,
	}

	newReq = newReq.WithContext(httptrace.WithClientTrace(newReq.Context(), trace))

	resp, err := httpsClient.Do(newReq)

	if err != nil {
		log.Fatal(err.Error())
	}

	log.Println(resp.StatusCode)

	//io.Copy(w, resp.Body)
	respBody, err := ioutil.ReadAll(resp.Body)
	log.Printf("[INFO][IN] Respond content:%v", string(respBody))
	w.Write(respBody)
	defer resp.Body.Close()
	if err != nil {
		log.Fatal(err.Error())
	}

	return
	//}
}
<|endoftext|>


<commit_before>package gaestatic

import (
	"strings"
	"fmt"
	"strconv"
	"net/http"
	"text/template"
	"bytes"
	"net/url"
	"regexp"
)

const PLIST_TEMPLATE string = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>items</key>
		<array>
			<dict>
				<key>assets</key>
				<array>
					{{if .IpaUrl}}
					<dict>
						<key>kind</key>
						<string>software-package</string>
						<key>url</key>
						<string>{{.IpaUrl}}</string>
					</dict>
					{{end}}
					{{if .DisplayImageUrl}}
					<dict>
						<key>kind</key>
						<string>display-image</string>
						<key>url</key>
						<string>{{.DisplayImageUrl}}</string>
					</dict>
					{{end}}
					{{if .FullSizeImageUrl}}
					<dict>
						<key>kind</key>
						<string>full-size-image</string>
						<key>url</key>
						<string>{{.FullSizeImageUrl}}</string>
					</dict>
					{{end}}
				</array>
				<key>metadata</key>
				<dict>
					{{if .BundleIdentifer}}
					<key>bundle-identifier</key>
					<string>{{.BundleIdentifer}}</string>
					{{end}}
					{{if .BundleVersion}}
					<key>bundle-version</key>
					<string>{{.BundleVersion}}</string>
					<key>kind</key>
					{{end}}
					<string>software</string>
					<key>title</key>
					{{if .Title}}
					<string>{{.Title}}</string>
					{{end}}
				</dict>
			</dict>
		</array>
	</dict>
</plist>
`

type PlistTemplateParams struct {
	// eg. https://example.com/apps/ios/sample.ipa
	IpaUrl string
	// eg. https://example.com/apps/ios/image.png
	DisplayImageUrl string
	// eg. https://example.com/apps/ios/full-image.png
	FullSizeImageUrl string
	// eg. com.example.sample
	BundleIdentifer string
	// eg. 1.0
	BundleVersion string
	// eg. Sample App
	Title string
}

/**
 * Dynamic Plist Handler
 * http://example.com/{filePath}/{bundleId}/{bundleVersion}/{IpaPath}/{imagePath}/x.plist
 *   ?title={title}&full-image={fullSizeImagePath}
 * IpaPath : extension must be '.ipa'
 */
func plistHandler(w http.ResponseWriter, r *http.Request) bool {
	const DYNAMIC_PLIST_POSTFIX = "/x.plist"
	isDone := true

	config := GetAppConfig()
	if config == nil {
		// Internal Server Errror
		w.WriteHeader(500)
		w.Write([]byte("No Config"))
		return isDone
	}

	query := r.URL.Query()

	filePath := strings.Replace(r.URL.Path, config.PlistDir, "", 1)
	tmp := strings.SplitN(filePath, "/", 3)
	if len(tmp) < 3 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #1"))
		return isDone
	}
	// Bundle Identifer (Required)
	bundleIdentifer := tmp[0]
	// Bundle Version (Required)
	bundleVersion := tmp[1]
	// Parse
	filePath = tmp[2]

	//
	rex := regexp.MustCompile(`^(.+\.ipa)/(.+)/x.plist$`)
	if !rex.MatchString(filePath) {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #2"))
		return isDone
	}
	tmp = rex.FindStringSubmatch(filePath)
	ipaPath := tmp[1]
	imagePath := tmp[2]

	if len(bundleIdentifer) == 0 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("bundle-identifier is required"))
		return isDone
	}

	if len(bundleVersion) == 0 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("bundle-version is required"))
		return isDone
	}

	// Display Image Url (Required)
	if len(imagePath) == 0 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("display-image is required"))
		return isDone
	}

	// Full Image Url (Optional)
	fullImageUrl := query.Get("full-image")
	// Title (Optional)
	title := query.Get("title")

	ipaUrl, _ := url.Parse(r.RequestURI)
	if !r.URL.IsAbs() {
		ipaUrl.Scheme = "http"
		ipaUrl.Host = r.Host
	}
	ipaUrl.RawQuery = ""
	ipaUrl.Path = "/" + ipaPath

	imageUrl, _ := url.Parse(r.RequestURI)
	if !r.URL.IsAbs() {
		imageUrl.Scheme = "http"
		imageUrl.Host = r.Host
	}
	imageUrl.RawQuery = ""
	imageUrl.Path = "/" + imagePath

	params := PlistTemplateParams{}
	params.Title = title
	params.BundleVersion = bundleVersion
	params.BundleIdentifer = bundleIdentifer
	params.IpaUrl = ipaUrl.String()
	params.DisplayImageUrl = imageUrl.String()
	params.FullSizeImageUrl = fullImageUrl

	tmpl, err := template.New("plist").Parse(PLIST_TEMPLATE)

	if err != nil {
		// Not Found
		w.WriteHeader(501)
		w.Write([]byte(fmt.Sprintf("plist template is invalid.")))
		return isDone
	}

	writer := new(bytes.Buffer)
	err	= tmpl.Execute(writer, params)

	var contentLength string
	if err != nil {
		// Forbidden : サイズ取得失敗
		w.WriteHeader(403)
		w.Write([]byte(fmt.Sprintf("plist params is invalid.")))
		return isDone
	} else {
		contentLength = strconv.FormatInt(int64(writer.Len()), 10)
	}
	contentLength = contentLength + "bytes"

	contentType := GetContentType("_.plist")
	if contentType != "" {
		w.Header().Set("Content-Type", contentType)
	}
	w.Write(writer.Bytes())
	isDone = true
	return isDone
}
<commit_msg>dynamic plist : snapshot<commit_after>package gaestatic

import (
	"strings"
	"fmt"
	"strconv"
	"net/http"
	"text/template"
	"bytes"
	"net/url"
	"regexp"
)

const PLIST_TEMPLATE string = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>items</key>
		<array>
			<dict>
				<key>assets</key>
				<array>
					{{if .IpaUrl}}
					<dict>
						<key>kind</key>
						<string>software-package</string>
						<key>url</key>
						<string>{{.IpaUrl}}</string>
					</dict>
					{{end}}
					{{if .DisplayImageUrl}}
					<dict>
						<key>kind</key>
						<string>display-image</string>
						<key>url</key>
						<string>{{.DisplayImageUrl}}</string>
					</dict>
					{{end}}
					{{if .FullSizeImageUrl}}
					<dict>
						<key>kind</key>
						<string>full-size-image</string>
						<key>url</key>
						<string>{{.FullSizeImageUrl}}</string>
					</dict>
					{{end}}
				</array>
				<key>metadata</key>
				<dict>
					{{if .BundleIdentifer}}
					<key>bundle-identifier</key>
					<string>{{.BundleIdentifer}}</string>
					{{end}}
					{{if .BundleVersion}}
					<key>bundle-version</key>
					<string>{{.BundleVersion}}</string>
					<key>kind</key>
					{{end}}
					<string>software</string>
					<key>title</key>
					{{if .Title}}
					<string>{{.Title}}</string>
					{{end}}
				</dict>
			</dict>
		</array>
	</dict>
</plist>
`

type PlistTemplateParams struct {
	// eg. https://example.com/apps/ios/sample.ipa
	IpaUrl string
	// eg. https://example.com/apps/ios/image.png
	DisplayImageUrl string
	// eg. https://example.com/apps/ios/full-image.png
	FullSizeImageUrl string
	// eg. com.example.sample
	BundleIdentifer string
	// eg. 1.0
	BundleVersion string
	// eg. Sample App
	Title string
}

/**
 * Dynamic Plist Handler
 * http://example.com/{filePath}/{bundleId}/{bundleVersion}/{IpaPath}/{imagePath}/x.plist
 *   ?title={title}&full-image={fullSizeImagePath}
 * IpaPath : extension must be '.ipa'
 */
func plistHandler(w http.ResponseWriter, r *http.Request) bool {
	const DYNAMIC_PLIST_POSTFIX = "/x.plist"
	isDone := true

	config := GetAppConfig()
	if config == nil {
		// Internal Server Errror
		w.WriteHeader(500)
		w.Write([]byte("No Config"))
		return isDone
	}

	query := r.URL.Query()

	filePath := strings.Replace(r.URL.Path, config.PlistDir, "", 1)
	tmp := strings.SplitN(filePath, "/", 3)
	if len(tmp) < 3 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #1"))
		return isDone
	}
	// Bundle Identifer (Required)
	bundleIdentifer := tmp[0]
	// Bundle Version (Required)
	bundleVersion := tmp[1]
	// Parse
	filePath = tmp[2]

	//
	rex := regexp.MustCompile(`^(.+\.ipa)/(.+)/x.plist$`)
	if !rex.MatchString(filePath) {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #2"))
		return isDone
	}
	tmp = rex.FindStringSubmatch(filePath)
	ipaPath := tmp[1]
	imagePath := tmp[2]

	if len(bundleIdentifer) == 0 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("bundle-identifier is required"))
		return isDone
	}

	if len(bundleVersion) == 0 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("bundle-version is required"))
		return isDone
	}

	// Display Image Url (Required)
	if len(imagePath) == 0 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("display-image is required"))
		return isDone
	}

	// Full Image Url (Optional)
	fullImageUrl := query.Get("full-image")
	// Title (Optional)
	title := query.Get("title")

	ipaUrl, _ := url.Parse(r.URL.String())
	if !r.URL.IsAbs() {
		ipaUrl.Scheme = "http"
		ipaUrl.Host = r.Host
	}
	ipaUrl.RawQuery = ""
	ipaUrl.Path = "/" + ipaPath

	imageUrl, _ := url.Parse(r.URL.String())
	if !r.URL.IsAbs() {
		imageUrl.Scheme = "http"
		imageUrl.Host = r.Host
	}
	imageUrl.RawQuery = ""
	imageUrl.Path = "/" + imagePath

	params := PlistTemplateParams{}
	params.Title = title
	params.BundleVersion = bundleVersion
	params.BundleIdentifer = bundleIdentifer
	params.IpaUrl = ipaUrl.String()
	params.DisplayImageUrl = imageUrl.String()
	params.FullSizeImageUrl = fullImageUrl

	tmpl, err := template.New("plist").Parse(PLIST_TEMPLATE)

	if err != nil {
		// Not Found
		w.WriteHeader(501)
		w.Write([]byte(fmt.Sprintf("plist template is invalid.")))
		return isDone
	}

	writer := new(bytes.Buffer)
	err	= tmpl.Execute(writer, params)

	var contentLength string
	if err != nil {
		// Forbidden : サイズ取得失敗
		w.WriteHeader(403)
		w.Write([]byte(fmt.Sprintf("plist params is invalid.")))
		return isDone
	} else {
		contentLength = strconv.FormatInt(int64(writer.Len()), 10)
	}
	contentLength = contentLength + "bytes"

	contentType := GetContentType("_.plist")
	if contentType != "" {
		w.Header().Set("Content-Type", contentType)
	}
	w.Write(writer.Bytes())
	isDone = true
	return isDone
}
<|endoftext|>


<commit_before>package gaestatic

import (
	"strings"
	"fmt"
	"strconv"
	"net/http"
	"text/template"
	"bytes"
)

const PLIST_TEMPLATE string = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>items</key>
		<array>
			<dict>
				<key>assets</key>
				<array>
					{{if .IpaUrl}}
					<dict>
						<key>kind</key>
						<string>software-package</string>
						<key>url</key>
						<string>{{.IpaUrl}}</string>
					</dict>
					{{end}}
					{{if .DisplayImageUrl}}
					<dict>
						<key>kind</key>
						<string>display-image</string>
						<key>url</key>
						<string>{{.DisplayImageUrl}}</string>
					</dict>
					{{end}}
					{{if .FullSizeImageUrl}}
					<dict>
						<key>kind</key>
						<string>full-size-image</string>
						<key>url</key>
						<string>{{.FullSizeImageUrl}}</string>
					</dict>
					{{end}}
				</array>
				<key>metadata</key>
				<dict>
					{{if .BundleIdentifer}}
					<key>bundle-identifier</key>
					<string>{{.BundleIdentifer}}</string>
					{{end}}
					{{if .BundleVersion}}
					<key>bundle-version</key>
					<string>{{.BundleVersion}}</string>
					<key>kind</key>
					{{end}}
					{{if .Title}}
					<string>software</string>
					<key>title</key>
					<string>{{.Title}}</string>
					{{end}}
				</dict>
			</dict>
		</array>
	</dict>
</plist>
`

type PlistTemplateParams struct {
	// eg. https://example.com/apps/ios/sample.ipa
	IpaUrl string
	// eg. https://example.com/apps/ios/image.png
	DisplayImageUrl string
	// eg. https://example.com/apps/ios/full-image.png
	FullSizeImageUrl string
	// eg. com.example.sample
	BundleIdentifer string
	// eg. 1.0
	BundleVersion string
	// eg. Sample App
	Title string
}

/**
 * Dynamic Plist Handler
 */
func plistHandler(w http.ResponseWriter, r *http.Request) bool {
	var filePath string

	fileConfig := config.FileConfig

	isDone := true

	config := GetAppConfig()
	if config == nil {
		// Internal Server Errror
		w.WriteHeader(500)
		w.Write([]byte("No Config"))
		return isDone
	}

	filePath = strings.Replace(r.URL.Path, config.PubDir, fileConfig.PubPath, -1)

	params := PlistTemplateParams{}

	params.BundleIdentifer = "com.example.sample"

	tmpl, err := template.New("plist").Parse(PLIST_TEMPLATE)

	if err != nil {
		// Not Found
		w.WriteHeader(501)
		w.Write([]byte(fmt.Sprintf("plist template is invalid.")))
		return isDone
	}

	writer := new(bytes.Buffer)
	err	= tmpl.Execute(writer, params)

	var contentLength string
	if err != nil {
		// Forbidden : サイズ取得失敗
		w.WriteHeader(403)
		w.Write([]byte(fmt.Sprintf("plist params is invalid.")))
		return isDone
	} else {
		contentLength = strconv.FormatInt(int64(writer.Len()), 10)
	}
	contentLength = contentLength + "bytes"

	contentType := GetContentType(filePath + "_.plist")
	if contentType != "" {
		w.Header().Set("Content-Type", contentType)
	}
	w.Write(writer.Bytes())
	isDone = true
	return isDone
}
<commit_msg>dynamic plist : snapshot<commit_after>package gaestatic

import (
	"strings"
	"fmt"
	"strconv"
	"net/http"
	"text/template"
	"bytes"
)

const PLIST_TEMPLATE string = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>items</key>
		<array>
			<dict>
				<key>assets</key>
				<array>
					{{if .IpaUrl}}
					<dict>
						<key>kind</key>
						<string>software-package</string>
						<key>url</key>
						<string>{{.IpaUrl}}</string>
					</dict>
					{{end}}
					{{if .DisplayImageUrl}}
					<dict>
						<key>kind</key>
						<string>display-image</string>
						<key>url</key>
						<string>{{.DisplayImageUrl}}</string>
					</dict>
					{{end}}
					{{if .FullSizeImageUrl}}
					<dict>
						<key>kind</key>
						<string>full-size-image</string>
						<key>url</key>
						<string>{{.FullSizeImageUrl}}</string>
					</dict>
					{{end}}
				</array>
				<key>metadata</key>
				<dict>
					{{if .BundleIdentifer}}
					<key>bundle-identifier</key>
					<string>{{.BundleIdentifer}}</string>
					{{end}}
					{{if .BundleVersion}}
					<key>bundle-version</key>
					<string>{{.BundleVersion}}</string>
					<key>kind</key>
					{{end}}
					{{if .Title}}
					<string>software</string>
					<key>title</key>
					<string>{{.Title}}</string>
					{{end}}
				</dict>
			</dict>
		</array>
	</dict>
</plist>
`

type PlistTemplateParams struct {
	// eg. https://example.com/apps/ios/sample.ipa
	IpaUrl string
	// eg. https://example.com/apps/ios/image.png
	DisplayImageUrl string
	// eg. https://example.com/apps/ios/full-image.png
	FullSizeImageUrl string
	// eg. com.example.sample
	BundleIdentifer string
	// eg. 1.0
	BundleVersion string
	// eg. Sample App
	Title string
}

/**
 * Dynamic Plist Handler
 */
func plistHandler(w http.ResponseWriter, r *http.Request) bool {
	var filePath string

	fileConfig := config.FileConfig

	isDone := true

	config := GetAppConfig()
	if config == nil {
		// Internal Server Errror
		w.WriteHeader(500)
		w.Write([]byte("No Config"))
		return isDone
	}

	filePath = strings.Replace(r.URL.Path, config.PubDir, fileConfig.PubPath, -1)
	tmp := strings.SplitN(filePath, "/", 2)
	if len(tmp) < 2 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #1"))
		return isDone
	}
	bundleIdentifer := tmp[0]
	if strings.Contains(tmp[1], "..") {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #2"))
		return isDone
	}

	ipaUrl := r.URL
	ipaUrl.Path = "/" + tmp[1]

	params := PlistTemplateParams{}
	// http://example.com/{filePath}/{bundleId}/{IpaPath}?title={title}&version={bundleVersion}
	params.Title = r.URL.Query().Get("title")
	params.BundleVersion = r.URL.Query().Get("version")
	params.BundleIdentifer = bundleIdentifer
	params.IpaUrl = ipaUrl.RequestURI()

	tmpl, err := template.New("plist").Parse(PLIST_TEMPLATE)

	if err != nil {
		// Not Found
		w.WriteHeader(501)
		w.Write([]byte(fmt.Sprintf("plist template is invalid.")))
		return isDone
	}

	writer := new(bytes.Buffer)
	err	= tmpl.Execute(writer, params)

	var contentLength string
	if err != nil {
		// Forbidden : サイズ取得失敗
		w.WriteHeader(403)
		w.Write([]byte(fmt.Sprintf("plist params is invalid.")))
		return isDone
	} else {
		contentLength = strconv.FormatInt(int64(writer.Len()), 10)
	}
	contentLength = contentLength + "bytes"

	contentType := GetContentType("_.plist")
	if contentType != "" {
		w.Header().Set("Content-Type", contentType)
	}
	w.Write(writer.Bytes())
	isDone = true
	return isDone
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/urfave/cli"
	"log"
	"os"
)

const VERSION = "0.2.0"

func main() {
	log.Printf("GL00N")
	s := Settings{}
	s.Hostnames = []string{}
	app := cli.NewApp()
	app.Name = "gloon"
	app.Usage = "Custom dns resolver with build in docker container support"
	app.UsageText = "gloon [options]"
	app.Version = VERSION
	app.Action = func(c *cli.Context) error {
		if c.StringSlice("hostname") != nil {
			s.Hostnames = c.StringSlice("hostname")
		}
		return appMain(&s)
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "disable-forward",
			Usage:       "Disable request forwarding",
			Destination: &s.DisableForwarding,
		},
		cli.BoolFlag{
			Name:        "disable-docker",
			Usage:       "Disable docker support",
			Destination: &s.DisableDocker,
		},
		cli.StringFlag{
			Name:        "resolvconf, r",
			Value:       "/etc/resolv.conf",
			Usage:       "resolv.conf compatible `FILE` to use for request forwarding",
			Destination: &s.ResolvFile,
		},
		cli.StringFlag{
			Name:        "listen, l",
			Value:       ":53",
			Usage:       "Resolver listens on `ADDR`",
			Destination: &s.ResolverAddr,
		},
		cli.StringFlag{
			Name:        "api-addr, a",
			Value:       "",
			Usage:       "Api http server listens on `ADDR`. Default is no api server",
			Destination: &s.ApiAddr,
		},
		cli.StringFlag{
			Name:        "hostname-filter, f",
			Value:       "",
			Usage:       "Only docker containers with hostnames matching this `REGEX`  will have A records published",
			Destination: &s.HostnameFilter,
		},
		cli.StringFlag{
			Name:        "append-domain, d",
			Value:       "",
			Usage:       "Append `DOMAIN NAME` to all configured A records",
			Destination: &s.AppendDomain,
		},
		cli.StringFlag{
			Name:        "hostfile",
			Value:       "",
			Usage:       "Load up `FILE` at startup and add any records found. Wilcards are supported",
			Destination: &s.Hostfile,
		},
		cli.IntFlag{
			Name:        "reload-interval, i",
			Value:       0,
			Usage:       "Reload hostfile (where applicable) every `SEC` seconds. If unset, default is to try inotify or similiar where available",
			Destination: &s.HostfileReloadInterval,
		},
		cli.StringSliceFlag{
			Name:  "hostname, n",
			Usage: "Add A records in the form of `HOSTNAME=IP`  ",
		},
		cli.StringFlag{
			Name:        "store",
			Value:       "memory",
			Usage:       "Set local dns record storage to `TYPE`. Valid values are 'redis' and 'memory'",
			Destination: &s.Store,
		},
		cli.IntFlag{
			Name:        "ttl",
			Value:       10,
			Usage:       "Returned ttl in `SEC` seconds",
			Destination: &s.Ttl,
		},
	}

	app.Run(os.Args)
}

func appMain(settings *Settings) (err error) {
	log.Printf("gloon %s starting...", VERSION)
	s, err := NewServer(settings.ResolverAddr, settings)
	if err != nil {
		log.Fatalf("Unable to create server: %s", err.Error())
	}
	if !settings.DisableDocker {
		dm, err := NewDockerMonitor(s.RecordSet, settings)
		if err != nil {
			log.Printf("WARNING: unable to start docker monitor: %s. Docker hostname support will be disabled", err.Error())
		}
		go func() {
			dm.Run()
		}()
	}
	if settings.Hostfile != "" {
		hf := NewHostfile(settings.Hostfile, s.RecordSet, settings.HostfileReloadInterval)
		go func() {
			hf.Run()
		}()
	}

	if settings.ApiAddr != "" {
		go func() {
			RunApiServer(settings, s.RecordSet)
		}()
	}
	err = s.ListenAndServe()
	defer s.Shutdown()
	if err != nil {
		log.Fatalf("Unable to start server: %s", err.Error())
	}
	return
}
<commit_msg>hour default for ttl<commit_after>package main

import (
	"github.com/urfave/cli"
	"log"
	"os"
)

const VERSION = "0.2.0"

func main() {
	log.Printf("GL00N")
	s := Settings{}
	s.Hostnames = []string{}
	app := cli.NewApp()
	app.Name = "gloon"
	app.Usage = "Custom dns resolver with build in docker container support"
	app.UsageText = "gloon [options]"
	app.Version = VERSION
	app.Action = func(c *cli.Context) error {
		if c.StringSlice("hostname") != nil {
			s.Hostnames = c.StringSlice("hostname")
		}
		return appMain(&s)
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "disable-forward",
			Usage:       "Disable request forwarding",
			Destination: &s.DisableForwarding,
		},
		cli.BoolFlag{
			Name:        "disable-docker",
			Usage:       "Disable docker support",
			Destination: &s.DisableDocker,
		},
		cli.StringFlag{
			Name:        "resolvconf, r",
			Value:       "/etc/resolv.conf",
			Usage:       "resolv.conf compatible `FILE` to use for request forwarding",
			Destination: &s.ResolvFile,
		},
		cli.StringFlag{
			Name:        "listen, l",
			Value:       ":53",
			Usage:       "Resolver listens on `ADDR`",
			Destination: &s.ResolverAddr,
		},
		cli.StringFlag{
			Name:        "api-addr, a",
			Value:       "",
			Usage:       "Api http server listens on `ADDR`. Default is no api server",
			Destination: &s.ApiAddr,
		},
		cli.StringFlag{
			Name:        "hostname-filter, f",
			Value:       "",
			Usage:       "Only docker containers with hostnames matching this `REGEX`  will have A records published",
			Destination: &s.HostnameFilter,
		},
		cli.StringFlag{
			Name:        "append-domain, d",
			Value:       "",
			Usage:       "Append `DOMAIN NAME` to all configured A records",
			Destination: &s.AppendDomain,
		},
		cli.StringFlag{
			Name:        "hostfile",
			Value:       "",
			Usage:       "Load up `FILE` at startup and add any records found. Wilcards are supported",
			Destination: &s.Hostfile,
		},
		cli.IntFlag{
			Name:        "reload-interval, i",
			Value:       0,
			Usage:       "Reload hostfile (where applicable) every `SEC` seconds. If unset, default is to try inotify or similiar where available",
			Destination: &s.HostfileReloadInterval,
		},
		cli.StringSliceFlag{
			Name:  "hostname, n",
			Usage: "Add A records in the form of `HOSTNAME=IP`  ",
		},
		cli.StringFlag{
			Name:        "store",
			Value:       "memory",
			Usage:       "Set local dns record storage to `TYPE`. Valid values are 'redis' and 'memory'",
			Destination: &s.Store,
		},
		cli.IntFlag{
			Name:        "ttl",
			Value:       3600,
			Usage:       "Returned ttl in `SEC` seconds",
			Destination: &s.Ttl,
		},
	}

	app.Run(os.Args)
}

func appMain(settings *Settings) (err error) {
	log.Printf("gloon %s starting...", VERSION)
	s, err := NewServer(settings.ResolverAddr, settings)
	if err != nil {
		log.Fatalf("Unable to create server: %s", err.Error())
	}
	if !settings.DisableDocker {
		dm, err := NewDockerMonitor(s.RecordSet, settings)
		if err != nil {
			log.Printf("WARNING: unable to start docker monitor: %s. Docker hostname support will be disabled", err.Error())
		}
		go func() {
			dm.Run()
		}()
	}
	if settings.Hostfile != "" {
		hf := NewHostfile(settings.Hostfile, s.RecordSet, settings.HostfileReloadInterval)
		go func() {
			hf.Run()
		}()
	}

	if settings.ApiAddr != "" {
		go func() {
			RunApiServer(settings, s.RecordSet)
		}()
	}
	err = s.ListenAndServe()
	defer s.Shutdown()
	if err != nil {
		log.Fatalf("Unable to start server: %s", err.Error())
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2016 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.

package ld

import (
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"fmt"
	"log"
)

// A BuildMode indicates the sort of object we are building.
//
// Possible build modes are the same as those for the -buildmode flag
// in cmd/go, and are documented in 'go help buildmode'.
type BuildMode uint8

const (
	BuildModeUnset BuildMode = iota
	BuildModeExe
	BuildModePIE
	BuildModeCArchive
	BuildModeCShared
	BuildModeShared
	BuildModePlugin
)

func (mode *BuildMode) Set(s string) error {
	badmode := func() error {
		return fmt.Errorf("buildmode %s not supported on %s/%s", s, objabi.GOOS, objabi.GOARCH)
	}
	switch s {
	default:
		return fmt.Errorf("invalid buildmode: %q", s)
	case "exe":
		switch objabi.GOOS + "/" + objabi.GOARCH {
		case "darwin/arm64", "windows/arm", "windows/arm64": // On these platforms, everything is PIE
			*mode = BuildModePIE
		default:
			*mode = BuildModeExe
		}
	case "pie":
		switch objabi.GOOS {
		case "aix", "android", "linux", "windows", "darwin", "ios":
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModePIE
	case "c-archive":
		switch objabi.GOOS {
		case "aix", "darwin", "ios", "linux":
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		case "windows":
			switch objabi.GOARCH {
			case "amd64", "386", "arm", "arm64":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModeCArchive
	case "c-shared":
		switch objabi.GOARCH {
		case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
		default:
			return badmode()
		}
		*mode = BuildModeCShared
	case "shared":
		switch objabi.GOOS {
		case "linux":
			switch objabi.GOARCH {
			case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModeShared
	case "plugin":
		switch objabi.GOOS {
		case "linux":
			switch objabi.GOARCH {
			case "386", "amd64", "arm", "arm64", "s390x", "ppc64le":
			default:
				return badmode()
			}
		case "darwin":
			switch objabi.GOARCH {
			case "amd64", "arm64":
			default:
				return badmode()
			}
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModePlugin
	}
	return nil
}

func (mode *BuildMode) String() string {
	switch *mode {
	case BuildModeUnset:
		return "" // avoid showing a default in usage message
	case BuildModeExe:
		return "exe"
	case BuildModePIE:
		return "pie"
	case BuildModeCArchive:
		return "c-archive"
	case BuildModeCShared:
		return "c-shared"
	case BuildModeShared:
		return "shared"
	case BuildModePlugin:
		return "plugin"
	}
	return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
}

// LinkMode indicates whether an external linker is used for the final link.
type LinkMode uint8

const (
	LinkAuto LinkMode = iota
	LinkInternal
	LinkExternal
)

func (mode *LinkMode) Set(s string) error {
	switch s {
	default:
		return fmt.Errorf("invalid linkmode: %q", s)
	case "auto":
		*mode = LinkAuto
	case "internal":
		*mode = LinkInternal
	case "external":
		*mode = LinkExternal
	}
	return nil
}

func (mode *LinkMode) String() string {
	switch *mode {
	case LinkAuto:
		return "auto"
	case LinkInternal:
		return "internal"
	case LinkExternal:
		return "external"
	}
	return fmt.Sprintf("LinkMode(%d)", uint8(*mode))
}

// mustLinkExternal reports whether the program being linked requires
// the external linker be used to complete the link.
func mustLinkExternal(ctxt *Link) (res bool, reason string) {
	if ctxt.Debugvlog > 1 {
		defer func() {
			if res {
				log.Printf("external linking is forced by: %s\n", reason)
			}
		}()
	}

	if sys.MustLinkExternal(objabi.GOOS, objabi.GOARCH) {
		return true, fmt.Sprintf("%s/%s requires external linking", objabi.GOOS, objabi.GOARCH)
	}

	if *flagMsan {
		return true, "msan"
	}

	// Internally linking cgo is incomplete on some architectures.
	// https://golang.org/issue/14449
	// https://golang.org/issue/21961
	if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) {
		return true, objabi.GOARCH + " does not support internal cgo"
	}
	if iscgo && objabi.GOOS == "android" {
		return true, objabi.GOOS + " does not support internal cgo"
	}

	// When the race flag is set, the LLVM tsan relocatable file is linked
	// into the final binary, which means external linking is required because
	// internal linking does not support it.
	if *flagRace && ctxt.Arch.InFamily(sys.PPC64) {
		return true, "race on " + objabi.GOARCH
	}

	// Some build modes require work the internal linker cannot do (yet).
	switch ctxt.BuildMode {
	case BuildModeCArchive:
		return true, "buildmode=c-archive"
	case BuildModeCShared:
		return true, "buildmode=c-shared"
	case BuildModePIE:
		switch objabi.GOOS + "/" + objabi.GOARCH {
		case "linux/amd64", "linux/arm64", "android/arm64":
		case "windows/386", "windows/amd64", "windows/arm", "windows/arm64":
		case "darwin/amd64", "darwin/arm64":
		default:
			// Internal linking does not support TLS_IE.
			return true, "buildmode=pie"
		}
	case BuildModePlugin:
		return true, "buildmode=plugin"
	case BuildModeShared:
		return true, "buildmode=shared"
	}
	if ctxt.linkShared {
		return true, "dynamically linking with a shared library"
	}

	return false, ""
}

// determineLinkMode sets ctxt.LinkMode.
//
// It is called after flags are processed and inputs are processed,
// so the ctxt.LinkMode variable has an initial value from the -linkmode
// flag and the iscgo externalobj variables are set.
func determineLinkMode(ctxt *Link) {
	extNeeded, extReason := mustLinkExternal(ctxt)
	via := ""

	if ctxt.LinkMode == LinkAuto {
		// The environment variable GO_EXTLINK_ENABLED controls the
		// default value of -linkmode. If it is not set when the
		// linker is called we take the value it was set to when
		// cmd/link was compiled. (See make.bash.)
		switch objabi.Getgoextlinkenabled() {
		case "0":
			ctxt.LinkMode = LinkInternal
			via = "via GO_EXTLINK_ENABLED "
		case "1":
			ctxt.LinkMode = LinkExternal
			via = "via GO_EXTLINK_ENABLED "
		default:
			if extNeeded || (iscgo && externalobj) {
				ctxt.LinkMode = LinkExternal
			} else {
				ctxt.LinkMode = LinkInternal
			}
		}
	}

	switch ctxt.LinkMode {
	case LinkInternal:
		if extNeeded {
			Exitf("internal linking requested %sbut external linking required: %s", via, extReason)
		}
	case LinkExternal:
		switch {
		case objabi.GOARCH == "ppc64" && objabi.GOOS != "aix":
			Exitf("external linking not supported for %s/ppc64", objabi.GOOS)
		}
	}
}
<commit_msg>cmd/link: use ctxt.Logf instead of package log<commit_after>// Copyright 2016 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.

package ld

import (
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"fmt"
)

// A BuildMode indicates the sort of object we are building.
//
// Possible build modes are the same as those for the -buildmode flag
// in cmd/go, and are documented in 'go help buildmode'.
type BuildMode uint8

const (
	BuildModeUnset BuildMode = iota
	BuildModeExe
	BuildModePIE
	BuildModeCArchive
	BuildModeCShared
	BuildModeShared
	BuildModePlugin
)

func (mode *BuildMode) Set(s string) error {
	badmode := func() error {
		return fmt.Errorf("buildmode %s not supported on %s/%s", s, objabi.GOOS, objabi.GOARCH)
	}
	switch s {
	default:
		return fmt.Errorf("invalid buildmode: %q", s)
	case "exe":
		switch objabi.GOOS + "/" + objabi.GOARCH {
		case "darwin/arm64", "windows/arm", "windows/arm64": // On these platforms, everything is PIE
			*mode = BuildModePIE
		default:
			*mode = BuildModeExe
		}
	case "pie":
		switch objabi.GOOS {
		case "aix", "android", "linux", "windows", "darwin", "ios":
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModePIE
	case "c-archive":
		switch objabi.GOOS {
		case "aix", "darwin", "ios", "linux":
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		case "windows":
			switch objabi.GOARCH {
			case "amd64", "386", "arm", "arm64":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModeCArchive
	case "c-shared":
		switch objabi.GOARCH {
		case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
		default:
			return badmode()
		}
		*mode = BuildModeCShared
	case "shared":
		switch objabi.GOOS {
		case "linux":
			switch objabi.GOARCH {
			case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModeShared
	case "plugin":
		switch objabi.GOOS {
		case "linux":
			switch objabi.GOARCH {
			case "386", "amd64", "arm", "arm64", "s390x", "ppc64le":
			default:
				return badmode()
			}
		case "darwin":
			switch objabi.GOARCH {
			case "amd64", "arm64":
			default:
				return badmode()
			}
		case "freebsd":
			switch objabi.GOARCH {
			case "amd64":
			default:
				return badmode()
			}
		default:
			return badmode()
		}
		*mode = BuildModePlugin
	}
	return nil
}

func (mode *BuildMode) String() string {
	switch *mode {
	case BuildModeUnset:
		return "" // avoid showing a default in usage message
	case BuildModeExe:
		return "exe"
	case BuildModePIE:
		return "pie"
	case BuildModeCArchive:
		return "c-archive"
	case BuildModeCShared:
		return "c-shared"
	case BuildModeShared:
		return "shared"
	case BuildModePlugin:
		return "plugin"
	}
	return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
}

// LinkMode indicates whether an external linker is used for the final link.
type LinkMode uint8

const (
	LinkAuto LinkMode = iota
	LinkInternal
	LinkExternal
)

func (mode *LinkMode) Set(s string) error {
	switch s {
	default:
		return fmt.Errorf("invalid linkmode: %q", s)
	case "auto":
		*mode = LinkAuto
	case "internal":
		*mode = LinkInternal
	case "external":
		*mode = LinkExternal
	}
	return nil
}

func (mode *LinkMode) String() string {
	switch *mode {
	case LinkAuto:
		return "auto"
	case LinkInternal:
		return "internal"
	case LinkExternal:
		return "external"
	}
	return fmt.Sprintf("LinkMode(%d)", uint8(*mode))
}

// mustLinkExternal reports whether the program being linked requires
// the external linker be used to complete the link.
func mustLinkExternal(ctxt *Link) (res bool, reason string) {
	if ctxt.Debugvlog > 1 {
		defer func() {
			if res {
				ctxt.Logf("external linking is forced by: %s\n", reason)
			}
		}()
	}

	if sys.MustLinkExternal(objabi.GOOS, objabi.GOARCH) {
		return true, fmt.Sprintf("%s/%s requires external linking", objabi.GOOS, objabi.GOARCH)
	}

	if *flagMsan {
		return true, "msan"
	}

	// Internally linking cgo is incomplete on some architectures.
	// https://golang.org/issue/14449
	// https://golang.org/issue/21961
	if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) {
		return true, objabi.GOARCH + " does not support internal cgo"
	}
	if iscgo && objabi.GOOS == "android" {
		return true, objabi.GOOS + " does not support internal cgo"
	}

	// When the race flag is set, the LLVM tsan relocatable file is linked
	// into the final binary, which means external linking is required because
	// internal linking does not support it.
	if *flagRace && ctxt.Arch.InFamily(sys.PPC64) {
		return true, "race on " + objabi.GOARCH
	}

	// Some build modes require work the internal linker cannot do (yet).
	switch ctxt.BuildMode {
	case BuildModeCArchive:
		return true, "buildmode=c-archive"
	case BuildModeCShared:
		return true, "buildmode=c-shared"
	case BuildModePIE:
		switch objabi.GOOS + "/" + objabi.GOARCH {
		case "linux/amd64", "linux/arm64", "android/arm64":
		case "windows/386", "windows/amd64", "windows/arm", "windows/arm64":
		case "darwin/amd64", "darwin/arm64":
		default:
			// Internal linking does not support TLS_IE.
			return true, "buildmode=pie"
		}
	case BuildModePlugin:
		return true, "buildmode=plugin"
	case BuildModeShared:
		return true, "buildmode=shared"
	}
	if ctxt.linkShared {
		return true, "dynamically linking with a shared library"
	}

	return false, ""
}

// determineLinkMode sets ctxt.LinkMode.
//
// It is called after flags are processed and inputs are processed,
// so the ctxt.LinkMode variable has an initial value from the -linkmode
// flag and the iscgo externalobj variables are set.
func determineLinkMode(ctxt *Link) {
	extNeeded, extReason := mustLinkExternal(ctxt)
	via := ""

	if ctxt.LinkMode == LinkAuto {
		// The environment variable GO_EXTLINK_ENABLED controls the
		// default value of -linkmode. If it is not set when the
		// linker is called we take the value it was set to when
		// cmd/link was compiled. (See make.bash.)
		switch objabi.Getgoextlinkenabled() {
		case "0":
			ctxt.LinkMode = LinkInternal
			via = "via GO_EXTLINK_ENABLED "
		case "1":
			ctxt.LinkMode = LinkExternal
			via = "via GO_EXTLINK_ENABLED "
		default:
			if extNeeded || (iscgo && externalobj) {
				ctxt.LinkMode = LinkExternal
			} else {
				ctxt.LinkMode = LinkInternal
			}
		}
	}

	switch ctxt.LinkMode {
	case LinkInternal:
		if extNeeded {
			Exitf("internal linking requested %sbut external linking required: %s", via, extReason)
		}
	case LinkExternal:
		switch {
		case objabi.GOARCH == "ppc64" && objabi.GOOS != "aix":
			Exitf("external linking not supported for %s/ppc64", objabi.GOOS)
		}
	}
}
<|endoftext|>


<commit_before>package util

import (
	"path"
	"path/filepath"

	"github.com/gobwas/glob"
	"github.com/mitchellh/go-homedir"
	"gopkg.in/ini.v1"
)

// CLConfig holds our command-line configuration.
var CLConfig struct {
	Glob   string // (optional) specifies formats to lint (e.g., "*.{md,py}")
	Output string // (optional) output style ("line" or "CLI")
	Wrap   bool   // (optional) wrap output when CLI style
}

// Config holds our .txtlint configuration.
var Config = loadOptions()

// AlertLevels holds the possible values for "level" in an external rule.
var AlertLevels = []string{"suggestion", "warning", "error"}

// LevelToInt allows us to easily compare levels in lint.go.
var LevelToInt = map[string]int{
	"suggestion": 0,
	"warning":    1,
	"error":      2,
}

type config struct {
	Checks        []string                      // All checks to load
	GBaseStyles   []string                      // Global base style
	GChecks       map[string]bool               // Global checks
	MinAlertLevel int                           // Lowest alert level to display
	SBaseStyles   map[glob.Glob][]string        // Syntax-specific base styles
	SChecks       map[glob.Glob]map[string]bool // Syntax-specific checks
	StylesPath    string                        // Directory with Rule.yml files
}

func newConfig() *config {
	var cfg config
	cfg.GChecks = make(map[string]bool)
	cfg.SBaseStyles = make(map[glob.Glob][]string)
	cfg.SChecks = make(map[glob.Glob]map[string]bool)
	cfg.MinAlertLevel = 0
	cfg.GBaseStyles = []string{"txtlint"}
	return &cfg
}

// loadConfig loads the .txtlint file. It checks the current directory, and
// then the user's home directory.
func loadConfig(names []string) (*ini.File, error) {
	var configPath, hpath string
	var iniFile *ini.File
	var err error

	home, _ := homedir.Dir()
	for _, name := range names {
		hpath = path.Join(home, name)
		if FileExists(name) {
			configPath = name
			break
		} else if FileExists(hpath) {
			configPath = hpath
			break
		}
	}

	iniFile, err = ini.Load(configPath)
	return iniFile, err
}

// loadOptions reads the .txtlint file.
func loadOptions() config {
	cfg := newConfig()
	uCfg, err := loadConfig([]string{".txtlint", "_txtlint"})
	if err != nil {
		return *cfg
	}

	core := uCfg.Section("")
	global := uCfg.Section("*")

	// Default settings
	for _, k := range core.KeyStrings() {
		if k == "StylesPath" {
			abs, _ := filepath.Abs(core.Key(k).MustString(""))
			cfg.StylesPath = abs
		} else if k == "MinAlertLevel" {
			level := core.Key(k).In("info", AlertLevels)
			cfg.MinAlertLevel = LevelToInt[level]
		}
	}

	// Global settings
	cfg.GBaseStyles = global.Key("BasedOnStyles").Strings(",")
	for _, k := range global.KeyStrings() {
		if k == "BasedOnStyle" {
			continue
		} else {
			cfg.GChecks[k] = global.Key(k).MustBool(false)
			cfg.Checks = append(cfg.Checks, k)
		}
	}

	// Syntax-specific settings
	for _, sec := range uCfg.SectionStrings() {
		if sec == "*" || sec == "DEFAULT" {
			continue
		}
		glob := glob.MustCompile(sec)
		syntaxOpts := make(map[string]bool)
		for _, k := range uCfg.Section(sec).KeyStrings() {
			if k == "BasedOnStyles" {
				cfg.SBaseStyles[glob] = uCfg.Section(sec).Key(k).Strings(",")
			} else {
				syntaxOpts[k] = uCfg.Section(sec).Key(k).MustBool(false)
				cfg.Checks = append(cfg.Checks, k)
			}
		}
		cfg.SChecks[glob] = syntaxOpts
	}

	return *cfg
}
<commit_msg>fix: BasedOnStyle -> BasedOnStyles (typo)<commit_after>package util

import (
	"path"
	"path/filepath"

	"github.com/gobwas/glob"
	"github.com/mitchellh/go-homedir"
	"gopkg.in/ini.v1"
)

// CLConfig holds our command-line configuration.
var CLConfig struct {
	Glob   string // (optional) specifies formats to lint (e.g., "*.{md,py}")
	Output string // (optional) output style ("line" or "CLI")
	Wrap   bool   // (optional) wrap output when CLI style
}

// Config holds our .txtlint configuration.
var Config = loadOptions()

// AlertLevels holds the possible values for "level" in an external rule.
var AlertLevels = []string{"suggestion", "warning", "error"}

// LevelToInt allows us to easily compare levels in lint.go.
var LevelToInt = map[string]int{
	"suggestion": 0,
	"warning":    1,
	"error":      2,
}

type config struct {
	Checks        []string                      // All checks to load
	GBaseStyles   []string                      // Global base style
	GChecks       map[string]bool               // Global checks
	MinAlertLevel int                           // Lowest alert level to display
	SBaseStyles   map[glob.Glob][]string        // Syntax-specific base styles
	SChecks       map[glob.Glob]map[string]bool // Syntax-specific checks
	StylesPath    string                        // Directory with Rule.yml files
}

func newConfig() *config {
	var cfg config
	cfg.GChecks = make(map[string]bool)
	cfg.SBaseStyles = make(map[glob.Glob][]string)
	cfg.SChecks = make(map[glob.Glob]map[string]bool)
	cfg.MinAlertLevel = 0
	cfg.GBaseStyles = []string{"txtlint"}
	return &cfg
}

// loadConfig loads the .txtlint file. It checks the current directory, and
// then the user's home directory.
func loadConfig(names []string) (*ini.File, error) {
	var configPath, hpath string
	var iniFile *ini.File
	var err error

	home, _ := homedir.Dir()
	for _, name := range names {
		hpath = path.Join(home, name)
		if FileExists(name) {
			configPath = name
			break
		} else if FileExists(hpath) {
			configPath = hpath
			break
		}
	}

	iniFile, err = ini.Load(configPath)
	return iniFile, err
}

// loadOptions reads the .txtlint file.
func loadOptions() config {
	cfg := newConfig()
	uCfg, err := loadConfig([]string{".txtlint", "_txtlint"})
	if err != nil {
		return *cfg
	}

	core := uCfg.Section("")
	global := uCfg.Section("*")

	// Default settings
	for _, k := range core.KeyStrings() {
		if k == "StylesPath" {
			abs, _ := filepath.Abs(core.Key(k).MustString(""))
			cfg.StylesPath = abs
		} else if k == "MinAlertLevel" {
			level := core.Key(k).In("info", AlertLevels)
			cfg.MinAlertLevel = LevelToInt[level]
		}
	}

	// Global settings
	cfg.GBaseStyles = global.Key("BasedOnStyles").Strings(",")
	for _, k := range global.KeyStrings() {
		if k == "BasedOnStyles" {
			continue
		} else {
			cfg.GChecks[k] = global.Key(k).MustBool(false)
			cfg.Checks = append(cfg.Checks, k)
		}
	}

	// Syntax-specific settings
	for _, sec := range uCfg.SectionStrings() {
		if sec == "*" || sec == "DEFAULT" {
			continue
		}
		glob := glob.MustCompile(sec)
		syntaxOpts := make(map[string]bool)
		for _, k := range uCfg.Section(sec).KeyStrings() {
			if k == "BasedOnStyles" {
				cfg.SBaseStyles[glob] = uCfg.Section(sec).Key(k).Strings(",")
			} else {
				syntaxOpts[k] = uCfg.Section(sec).Key(k).MustBool(false)
				cfg.Checks = append(cfg.Checks, k)
			}
		}
		cfg.SChecks[glob] = syntaxOpts
	}

	return *cfg
}
<|endoftext|>


<commit_before>package utils

import (
	"reflect"
	"fmt"
)

func ParseWhereMap(wheres interface{}) (string,[]interface{}){

	whereMap := make(map[string]interface{},0)
	valList := make([]interface{},0)

	if reflect.ValueOf(wheres).IsValid() {
		if reflect.TypeOf(wheres).Kind() == reflect.Map {
			whereMap = wheres.(map[string]interface{})
		}
	}

	where := "1 "

	for k,v := range whereMap {
		var vv interface{}
		verifyed := false

		if reflect.TypeOf(v).Kind() == reflect.Struct {
			ep := reflect.ValueOf(v)
			if ep.IsValid() {
				sn := ep.MethodByName("String")
				if sn.IsValid() {
					rets := sn.Call([]reflect.Value{})
					if len(rets) > 0 {
						strRet := rets[0]
						vv = strRet.Interface().(string)
						verifyed = true
					}
				}
			}
		}else{
			vv = v
			verifyed = true
		}

		if verifyed && reflect.TypeOf(v).Kind() != reflect.Map {
			where = where + fmt.Sprintf(" AND `%s` = ? ",k)
			valList = append(valList,vv)
		}
	}

	for k,v := range whereMap {

		if reflect.TypeOf(v).Kind() == reflect.Map {

			vi := v.(map[string]interface{})

			t,ok1 := vi["type"]

			var vv interface{}

			ev,ok2 := vi["value"]
			verifyed := false

			if ok2 {
				if reflect.TypeOf(ev).Kind() == reflect.Struct {
					ep := reflect.ValueOf(ev)
					if ep.IsValid() {
						sn := ep.MethodByName("String")
						if sn.IsValid() {
							rets := sn.Call([]reflect.Value{})
							if len(rets) > 0 {
								strRet := rets[0]
								vv = strRet.Interface().(string)
								verifyed = true
							}
						}
					}
				}else{
					vv = ev
					verifyed = true
				}
			}

			if verifyed && ok1 && ok2 {

				switch t.(string) {

				case "=" 		: {

					where  = where + fmt.Sprintf(" AND `%s` = ? ",k)
					valList = append(valList,vv)

					break}

				case ">" 		: {

					where  = where + fmt.Sprintf(" AND `%s` > ? ",k)
					valList = append(valList,vv)

					break}
				case "<" 		: {

					where  = where + fmt.Sprintf(" AND `%s` < ? ",k)
					valList = append(valList,vv)

					break}
				case "not in"	: {

					valsStr := ""

					for j := 0; j < len(vv.([]interface{}));j++ {
						valsStr = valsStr + "?"

						if j < len(vv.([]interface{})) -1 {
							valsStr = valsStr + ","
						}
					}
					where  = where + fmt.Sprintf(" AND `%s` NOT IN(%s) ",k,valsStr)
					valList = append(valList,vv.([]interface{})...)

					break}

				case "in"		: {

					valsStr := ""

					for j := 0; j < len(vv.([]interface{}));j++ {
						valsStr = valsStr + "?"

						if j < len(vv.([]interface{})) -1 {
							valsStr = valsStr + ","
						}
					}
					where  = where + fmt.Sprintf(" AND `%s` NOT IN(%s) ",k,valsStr)
					valList = append(valList,vv.([]interface{})...)
					break
				}

				}
			}
		}
	}

	if len(where) > 2 {
		where = string([]byte(where)[6:])
	}

	return where,valList
}
<commit_msg>refix<commit_after>package utils

import (
	"reflect"
	"fmt"
	"github.com/weixinhost/litedb"
)

func ParseWhereMap(wheres interface{}) (string,[]interface{}){

	whereMap := make(map[string]interface{},0)
	valList := make([]interface{},0)

	if reflect.ValueOf(wheres).IsValid() {
		if reflect.TypeOf(wheres).Kind() == reflect.Map {
			whereMap = wheres.(map[string]interface{})
		}
	}

	where := "1 "

	for k,v := range whereMap {

		vs := litedb.ToStr(v)
		if reflect.TypeOf(v).Kind() != reflect.Map {
			where = where + fmt.Sprintf(" AND `%s` = ? ",k)
			valList = append(valList,vs)
		}
	}

	for k,v := range whereMap {

		if reflect.TypeOf(v).Kind() == reflect.Map {

			vi := v.(map[string]interface{})

			t,ok1 := vi["type"]

			vv,ok2 := vi["value"]

			if ok1 && ok2 {

				switch t.(string) {

				case "=" 		: {

					where  = where + fmt.Sprintf(" AND `%s` = ? ",k)
					valList = append(valList,litedb.ToStr(vv))

					break}

				case ">" 		: {

					where  = where + fmt.Sprintf(" AND `%s` > ? ",k)
					valList = append(valList,litedb.ToStr(vv))

					break}
				case "<" 		: {

					where  = where + fmt.Sprintf(" AND `%s` < ? ",k)
					valList = append(valList,litedb.ToStr(vv))

					break}
				case "not in"	: {

					valsStr := ""

					for j := 0; j < len(vv.([]interface{}));j++ {
						valsStr = valsStr + "?"

						if j < len(vv.([]interface{})) -1 {
							valsStr = valsStr + ","
						}
					}
					where  = where + fmt.Sprintf(" AND `%s` NOT IN(%s) ",k,valsStr)
					valList = append(valList,vv.([]interface{})...)

					break}

				case "in"		: {

					valsStr := ""

					for j := 0; j < len(vv.([]interface{}));j++ {
						valsStr = valsStr + "?"

						if j < len(vv.([]interface{})) -1 {
							valsStr = valsStr + ","
						}
					}
					where  = where + fmt.Sprintf(" AND `%s` NOT IN(%s) ",k,valsStr)
					valList = append(valList,vv.([]interface{})...)
					break
				}

				}
			}
		}
	}

	if len(where) > 2 {
		where = string([]byte(where)[6:])
	}

	return where,valList
}
<|endoftext|>


<commit_before>package utils

import (
	"bufio"
	"bytes"
	"compress/zlib"
	"errors"
	"fmt"
	"io"
	"log"
	"reflect"
	"regexp"
	"strings"
	"time"
	"unicode"
)

type Prompter interface {
	GetPrompt() string
}

type simplePrompter struct {
	prompt string
}

func (sp simplePrompter) GetPrompt() string {
	return sp.prompt
}

// SimpleRompter returns a Prompter that always returns the given string as its prompt
func SimplePrompter(prompt string) Prompter {
	var prompter simplePrompter
	prompter.prompt = prompt
	return prompter
}

func Write(conn io.Writer, text string) (int, error) {
	return WriteRaw(conn, []byte(text))
}

func WriteRaw(conn io.Writer, bytes []byte) (int, error) {
	return conn.Write(bytes)
}

func WriteLine(conn io.Writer, line string) (int, error) {
	return Write(conn, line+"\r\n")
}

// ClearLine sends the VT100 code for erasing the line followed by a carriage
// return to move the cursor back to the beginning of the line
func ClearLine(conn io.Writer) {
	clearline := "\x1B[2K"
	Write(conn, clearline+"\r")
}

func Simplify(str string) string {
	simpleStr := strings.TrimSpace(str)
	simpleStr = strings.ToLower(simpleStr)
	return simpleStr
}

func GetRawUserInputSuffix(conn io.ReadWriter, prompt string, suffix string) string {
	return GetRawUserInputSuffixP(conn, SimplePrompter(prompt), suffix)
}

func GetRawUserInputSuffixP(conn io.ReadWriter, prompter Prompter, suffix string) string {
	reader := bufio.NewReader(conn)

	for {
		Write(conn, prompter.GetPrompt())

		input, err := reader.ReadString('\n')
		input = strings.Trim(input, "\r\n")

		PanicIfError(err)

		Write(conn, suffix)

		if input == "x" || input == "X" {
			return ""
		} else if input != "" {
			return input
		}
	}
}

func GetRawUserInputP(conn io.ReadWriter, prompter Prompter) string {
	return GetRawUserInputSuffixP(conn, prompter, "")
}

func GetRawUserInput(conn io.ReadWriter, prompt string) string {
	return GetRawUserInputP(conn, SimplePrompter(prompt))
}

func GetUserInputP(conn io.ReadWriter, prompter Prompter) string {
	input := GetRawUserInputP(conn, prompter)
	return Simplify(input)
}

func GetUserInput(conn io.ReadWriter, prompt string) string {
	input := GetUserInputP(conn, SimplePrompter(prompt))
	return Simplify(input)
}

func HandleError(err error) {
	if err != nil {
		log.Fatalf("Error: %s", err)
	}
}

func FormatName(name string) string {
	if name == "" {
		return name
	}

	runes := []rune(Simplify(name))
	runes[0] = unicode.ToUpper(runes[0])
	return string(runes)
}

func PanicIfError(err error) {
	if err != nil {
		panic(err)
	}
}

func Argify(data string) (string, []string) {
	fields := strings.Fields(data)

	if len(fields) == 0 {
		return "", []string{}
	}

	arg1 := Simplify(fields[0])
	args := fields[1:]

	return arg1, args
}

func rowEmpty(row string) bool {
	for _, char := range row {
		if char != ' ' {
			return false
		}
	}
	return true
}

func TrimUpperRows(rows []string) []string {
	for _, row := range rows {
		if !rowEmpty(row) {
			break
		}

		rows = rows[1:]
	}

	return rows
}

func TrimLowerRows(rows []string) []string {
	for i := len(rows) - 1; i >= 0; i -= 1 {
		row := rows[i]
		if !rowEmpty(row) {
			break
		}
		rows = rows[:len(rows)-1]
	}

	return rows
}

func TrimEmptyRows(str string) string {
	rows := strings.Split(str, "\r\n")
	return strings.Join(TrimLowerRows(TrimUpperRows(rows)), "\r\n")
}

func ValidateName(name string) error {
	const MinSize = 3
	const MaxSize = 12

	if len(name) < MinSize || len(name) > MaxSize {
		return errors.New(fmt.Sprintf("Names must be between %v and %v letters long", MinSize, MaxSize))
	}

	regex := regexp.MustCompile("^[a-zA-Z0-9]*$")

	if !regex.MatchString(name) {
		return errors.New("Names may only contain letters or numbers (A-Z, 0-9)")
	}

	return nil
}

func MonitorChannel() {
	// TODO: See if there's a way to take in a generic channel and see how close it is to being full
}

// BestMatch searches the given list for the given pattern, the index of the
// longest match that starts with the given pattern is returned. Returns -1 if
// no match was found, -2 if the result is ambiguous. The search is case
// insensitive
func BestMatch(pattern string, searchList []string) int {
	pattern = strings.ToLower(pattern)

	index := -1

	for i, searchItem := range searchList {
		searchItem = strings.ToLower(searchItem)

		if searchItem == pattern {
			return i
		}

		if strings.HasPrefix(searchItem, pattern) {
			if index != -1 {
				return -2
			}

			index = i
		}
	}

	return index
}

func compress(data []byte) []byte {
	var b bytes.Buffer
	w := zlib.NewWriter(&b)
	w.Write(data)
	w.Close()
	return b.Bytes()
}

type WatchableReadWriter struct {
	rw       io.ReadWriter
	watchers []io.ReadWriter
}

func NewWatchableReadWriter(rw io.ReadWriter) *WatchableReadWriter {
	var watchable WatchableReadWriter
	watchable.rw = rw
	return &watchable
}

func (w *WatchableReadWriter) Read(p []byte) (int, error) {
	n, err := w.rw.Read(p)

	for _, watcher := range w.watchers {
		watcher.Write(p[:n])
	}

	return n, err
}

func (w *WatchableReadWriter) Write(p []byte) (int, error) {
	for _, watcher := range w.watchers {
		watcher.Write(p)
	}

	return w.rw.Write(p)
}

func (w *WatchableReadWriter) AddWatcher(rw io.ReadWriter) {
	w.watchers = append(w.watchers, rw)
}

func (w *WatchableReadWriter) RemoveWatcher(rw io.ReadWriter) {
	for i, watcher := range w.watchers {
		if watcher == rw {
			// TODO: Potential memory leak. See http://code.google.com/p/go-wiki/wiki/SliceTricks
			w.watchers = append(w.watchers[:i], w.watchers[i+1:]...)
			return
		}
	}
}

// FindMethod uses reflection to find an exported method with the given name on
// the given object. The reflect.Value is the value of the method that was
// found, such that Call be be invoked on it directly. The matching is
// case-inensitive.
// In order to be able to find methods that operate on the object pointer type,
// a pointer to an instance of an object should be given, rather than the
// object itself
func FindMethod(object interface{}, name string) (reflect.Value, bool) {
	objType := reflect.TypeOf(object)

	for i := 0; i < objType.NumMethod(); i++ {
		method := objType.Method(i)

		// An empty PkgPath here means the method is exported (reflect can't call unexported methods)
		if strings.ToLower(method.Name) == strings.ToLower(name) && method.PkgPath == "" {
			return reflect.ValueOf(object).MethodByName(method.Name), true
		}
	}

	return reflect.Value{}, false
}

func FindAndCallMethod(object interface{}, name string, a ...interface{}) bool {
	method, found := FindMethod(object, name)

	if found {
		vals := make([]reflect.Value, len(a))
		for i, arg := range a {
			vals[i] = reflect.ValueOf(arg)
		}

		method.Call(vals)
	}

	return found
}

// Throttler is a simple utility class that allows events to occur on a
// deterministic recurring basis. Every call to Sync() will block until the
// duration of the Throttler's interval has passed since the last call to
// Sync()
type Throttler struct {
	lastTime time.Time
	interval time.Duration
}

func NewThrottler(interval time.Duration) *Throttler {
	var throttler Throttler
	throttler.lastTime = time.Now()
	throttler.interval = interval
	return &throttler
}

func (self *Throttler) Sync() {
	diff := time.Since(self.lastTime)
	if diff < self.interval {
		time.Sleep(self.interval - diff)
	}
	self.lastTime = time.Now()
}

// vim: nocindent
<commit_msg>Use the new bufio.Scanner since it seems a little cleaner<commit_after>package utils

import (
	"bufio"
	"bytes"
	"compress/zlib"
	"errors"
	"fmt"
	"io"
	"log"
	"reflect"
	"regexp"
	"strings"
	"time"
	"unicode"
)

type Prompter interface {
	GetPrompt() string
}

type simplePrompter struct {
	prompt string
}

func (sp simplePrompter) GetPrompt() string {
	return sp.prompt
}

// SimpleRompter returns a Prompter that always returns the given string as its prompt
func SimplePrompter(prompt string) Prompter {
	var prompter simplePrompter
	prompter.prompt = prompt
	return prompter
}

func Write(conn io.Writer, text string) (int, error) {
	return WriteRaw(conn, []byte(text))
}

func WriteRaw(conn io.Writer, bytes []byte) (int, error) {
	return conn.Write(bytes)
}

func WriteLine(conn io.Writer, line string) (int, error) {
	return Write(conn, line+"\r\n")
}

// ClearLine sends the VT100 code for erasing the line followed by a carriage
// return to move the cursor back to the beginning of the line
func ClearLine(conn io.Writer) {
	clearline := "\x1B[2K"
	Write(conn, clearline+"\r")
}

func Simplify(str string) string {
	simpleStr := strings.TrimSpace(str)
	simpleStr = strings.ToLower(simpleStr)
	return simpleStr
}

func GetRawUserInputSuffix(conn io.ReadWriter, prompt string, suffix string) string {
	return GetRawUserInputSuffixP(conn, SimplePrompter(prompt), suffix)
}

func GetRawUserInputSuffixP(conn io.ReadWriter, prompter Prompter, suffix string) string {
	scanner := bufio.NewScanner(conn)

	for {
		Write(conn, prompter.GetPrompt())

		scanner.Scan()
		PanicIfError(scanner.Err())

		input := scanner.Text()
		Write(conn, suffix)

		if input == "x" || input == "X" {
			return ""
		} else if input != "" {
			return input
		}
	}
}

func GetRawUserInputP(conn io.ReadWriter, prompter Prompter) string {
	return GetRawUserInputSuffixP(conn, prompter, "")
}

func GetRawUserInput(conn io.ReadWriter, prompt string) string {
	return GetRawUserInputP(conn, SimplePrompter(prompt))
}

func GetUserInputP(conn io.ReadWriter, prompter Prompter) string {
	input := GetRawUserInputP(conn, prompter)
	return Simplify(input)
}

func GetUserInput(conn io.ReadWriter, prompt string) string {
	input := GetUserInputP(conn, SimplePrompter(prompt))
	return Simplify(input)
}

func HandleError(err error) {
	if err != nil {
		log.Fatalf("Error: %s", err)
	}
}

func FormatName(name string) string {
	if name == "" {
		return name
	}

	runes := []rune(Simplify(name))
	runes[0] = unicode.ToUpper(runes[0])
	return string(runes)
}

func PanicIfError(err error) {
	if err != nil {
		panic(err)
	}
}

func Argify(data string) (string, []string) {
	fields := strings.Fields(data)

	if len(fields) == 0 {
		return "", []string{}
	}

	arg1 := Simplify(fields[0])
	args := fields[1:]

	return arg1, args
}

func rowEmpty(row string) bool {
	for _, char := range row {
		if char != ' ' {
			return false
		}
	}
	return true
}

func TrimUpperRows(rows []string) []string {
	for _, row := range rows {
		if !rowEmpty(row) {
			break
		}

		rows = rows[1:]
	}

	return rows
}

func TrimLowerRows(rows []string) []string {
	for i := len(rows) - 1; i >= 0; i -= 1 {
		row := rows[i]
		if !rowEmpty(row) {
			break
		}
		rows = rows[:len(rows)-1]
	}

	return rows
}

func TrimEmptyRows(str string) string {
	rows := strings.Split(str, "\r\n")
	return strings.Join(TrimLowerRows(TrimUpperRows(rows)), "\r\n")
}

func ValidateName(name string) error {
	const MinSize = 3
	const MaxSize = 12

	if len(name) < MinSize || len(name) > MaxSize {
		return errors.New(fmt.Sprintf("Names must be between %v and %v letters long", MinSize, MaxSize))
	}

	regex := regexp.MustCompile("^[a-zA-Z0-9]*$")

	if !regex.MatchString(name) {
		return errors.New("Names may only contain letters or numbers (A-Z, 0-9)")
	}

	return nil
}

func MonitorChannel() {
	// TODO: See if there's a way to take in a generic channel and see how close it is to being full
}

// BestMatch searches the given list for the given pattern, the index of the
// longest match that starts with the given pattern is returned. Returns -1 if
// no match was found, -2 if the result is ambiguous. The search is case
// insensitive
func BestMatch(pattern string, searchList []string) int {
	pattern = strings.ToLower(pattern)

	index := -1

	for i, searchItem := range searchList {
		searchItem = strings.ToLower(searchItem)

		if searchItem == pattern {
			return i
		}

		if strings.HasPrefix(searchItem, pattern) {
			if index != -1 {
				return -2
			}

			index = i
		}
	}

	return index
}

func compress(data []byte) []byte {
	var b bytes.Buffer
	w := zlib.NewWriter(&b)
	w.Write(data)
	w.Close()
	return b.Bytes()
}

type WatchableReadWriter struct {
	rw       io.ReadWriter
	watchers []io.ReadWriter
}

func NewWatchableReadWriter(rw io.ReadWriter) *WatchableReadWriter {
	var watchable WatchableReadWriter
	watchable.rw = rw
	return &watchable
}

func (w *WatchableReadWriter) Read(p []byte) (int, error) {
	n, err := w.rw.Read(p)

	for _, watcher := range w.watchers {
		watcher.Write(p[:n])
	}

	return n, err
}

func (w *WatchableReadWriter) Write(p []byte) (int, error) {
	for _, watcher := range w.watchers {
		watcher.Write(p)
	}

	return w.rw.Write(p)
}

func (w *WatchableReadWriter) AddWatcher(rw io.ReadWriter) {
	w.watchers = append(w.watchers, rw)
}

func (w *WatchableReadWriter) RemoveWatcher(rw io.ReadWriter) {
	for i, watcher := range w.watchers {
		if watcher == rw {
			// TODO: Potential memory leak. See http://code.google.com/p/go-wiki/wiki/SliceTricks
			w.watchers = append(w.watchers[:i], w.watchers[i+1:]...)
			return
		}
	}
}

// FindMethod uses reflection to find an exported method with the given name on
// the given object. The reflect.Value is the value of the method that was
// found, such that Call be be invoked on it directly. The matching is
// case-inensitive.
// In order to be able to find methods that operate on the object pointer type,
// a pointer to an instance of an object should be given, rather than the
// object itself
func FindMethod(object interface{}, name string) (reflect.Value, bool) {
	objType := reflect.TypeOf(object)

	for i := 0; i < objType.NumMethod(); i++ {
		method := objType.Method(i)

		// An empty PkgPath here means the method is exported (reflect can't call unexported methods)
		if strings.ToLower(method.Name) == strings.ToLower(name) && method.PkgPath == "" {
			return reflect.ValueOf(object).MethodByName(method.Name), true
		}
	}

	return reflect.Value{}, false
}

func FindAndCallMethod(object interface{}, name string, a ...interface{}) bool {
	method, found := FindMethod(object, name)

	if found {
		vals := make([]reflect.Value, len(a))
		for i, arg := range a {
			vals[i] = reflect.ValueOf(arg)
		}

		method.Call(vals)
	}

	return found
}

// Throttler is a simple utility class that allows events to occur on a
// deterministic recurring basis. Every call to Sync() will block until the
// duration of the Throttler's interval has passed since the last call to
// Sync()
type Throttler struct {
	lastTime time.Time
	interval time.Duration
}

func NewThrottler(interval time.Duration) *Throttler {
	var throttler Throttler
	throttler.lastTime = time.Now()
	throttler.interval = interval
	return &throttler
}

func (self *Throttler) Sync() {
	diff := time.Since(self.lastTime)
	if diff < self.interval {
		time.Sleep(self.interval - diff)
	}
	self.lastTime = time.Now()
}

// vim: nocindent
<|endoftext|>