<commit_before>package shell_local

import (
	"io/ioutil"
	"os"
	"runtime"
	"strings"
	"testing"

	"github.com/hashicorp/packer/packer"
)

func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
	var _ packer.PostProcessor = new(PostProcessor)
}

func testConfig() map[string]interface{} {
	return map[string]interface{}{
		"inline": []interface{}{"foo", "bar"},
	}
}

func TestPostProcessor_Impl(t *testing.T) {
	var raw interface{}
	raw = &PostProcessor{}
	if _, ok := raw.(packer.PostProcessor); !ok {
		t.Fatalf("must be a post processor")
	}
}

func TestPostProcessorPrepare_Defaults(t *testing.T) {
	var p PostProcessor
	raws := testConfig()

	err := p.Configure(raws)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}

func TestPostProcessorPrepare_InlineShebang(t *testing.T) {
	raws := testConfig()

	delete(raws, "inline_shebang")
	p := new(PostProcessor)
	err := p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
	expected := ""
	if runtime.GOOS != "windows" {
		expected = "/bin/sh -e"
	}
	if p.config.InlineShebang != expected {
		t.Fatalf("bad value: %s", p.config.InlineShebang)
	}

	// Test with a good one
	raws["inline_shebang"] = "foo"
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	if p.config.InlineShebang != "foo" {
		t.Fatalf("bad value: %s", p.config.InlineShebang)
	}
}

func TestPostProcessorPrepare_InvalidKey(t *testing.T) {
	var p PostProcessor
	raws := testConfig()

	// Add a random key
	raws["i_should_not_be_valid"] = true
	err := p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestPostProcessorPrepare_Script(t *testing.T) {
	raws := testConfig()
	delete(raws, "inline")

	raws["script"] = "/this/should/not/exist"
	p := new(PostProcessor)
	err := p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a good one
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("error tempfile: %s", err)
	}
	defer os.Remove(tf.Name())

	raws["script"] = tf.Name()
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestPostProcessorPrepare_ExecuteCommand(t *testing.T) {
	// Check that passing a string will work (Backwards Compatibility)
	p := new(PostProcessor)
	raws := testConfig()
	raws["execute_command"] = "foo bar"
	err := p.Configure(raws)
	expected := []string{"sh", "-c", "foo bar"}
	if err != nil {
		t.Fatalf("should handle backwards compatibility: %s", err)
	}
	if strings.Compare(strings.Join(p.config.ExecuteCommand, " "), strings.Join(expected, " ")) != 0 {
		t.Fatalf("Did not get expected execute_command: expected: %#v; received %#v", expected, p.config.ExecuteCommand)
	}

	// Check that passing a list will work
	p = new(PostProcessor)
	raws = testConfig()
	raws["execute_command"] = []string{"foo", "bar"}
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should handle backwards compatibility: %s", err)
	}
	expected = []string{"foo", "bar"}
	if strings.Compare(strings.Join(p.config.ExecuteCommand, " "), strings.Join(expected, " ")) != 0 {
		t.Fatalf("Did not get expected execute_command: expected: %#v; received %#v", expected, p.config.ExecuteCommand)
	}

	// Check that default is as expected
	raws = testConfig()
	delete(raws, "execute_command")
	p = new(PostProcessor)
	p.Configure(raws)
	if runtime.GOOS != "windows" {
		expected = []string{"/bin/sh", "-c", "{{.Vars}}", "{{.Script}}"}
	} else {
		expected = []string{"cmd", "/C", "{{.Vars}}", "{{.Script}}"}
	}
	if strings.Compare(strings.Join(p.config.ExecuteCommand, " "), strings.Join(expected, " ")) != 0 {
		t.Fatalf("Did not get expected default: expected: %#v; received %#v", expected, p.config.ExecuteCommand)
	}
}

func TestPostProcessorPrepare_ScriptAndInline(t *testing.T) {
	var p PostProcessor
	raws := testConfig()

	// Error if no scripts/inline commands provided
	delete(raws, "inline")
	delete(raws, "script")
	delete(raws, "command")
	delete(raws, "scripts")
	err := p.Configure(raws)
	if err == nil {
		t.Fatalf("should error when no scripts/inline commands are provided")
	}

	// Test with both
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("error tempfile: %s", err)
	}
	defer os.Remove(tf.Name())

	raws["inline"] = []interface{}{"foo"}
	raws["script"] = tf.Name()
	err = p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestPostProcessorPrepare_ScriptAndScripts(t *testing.T) {
	var p PostProcessor
	raws := testConfig()

	// Test with both
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("error tempfile: %s", err)
	}
	defer os.Remove(tf.Name())

	raws["inline"] = []interface{}{"foo"}
	raws["scripts"] = []string{tf.Name()}
	err = p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestPostProcessorPrepare_Scripts(t *testing.T) {
	raws := testConfig()
	delete(raws, "inline")

	raws["scripts"] = []string{}
	p := new(PostProcessor)
	err := p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a good one
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("error tempfile: %s", err)
	}
	defer os.Remove(tf.Name())

	raws["scripts"] = []string{tf.Name()}
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestPostProcessorPrepare_EnvironmentVars(t *testing.T) {
	raws := testConfig()

	// Test with a bad case
	raws["environment_vars"] = []string{"badvar", "good=var"}
	p := new(PostProcessor)
	err := p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a trickier case
	raws["environment_vars"] = []string{"=bad"}
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a good case
	// Note: baz= is a real env variable, just empty
	raws["environment_vars"] = []string{"FOO=bar", "baz="}
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	// Test when the env variable value contains an equals sign
	raws["environment_vars"] = []string{"good=withequals=true"}
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	// Test when the env variable value starts with an equals sign
	raws["environment_vars"] = []string{"good==true"}
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}
<commit_msg>fix windows test<commit_after>package shell_local

import (
	"io/ioutil"
	"os"
	"runtime"
	"strings"
	"testing"

	"github.com/hashicorp/packer/packer"
)

func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
	var _ packer.PostProcessor = new(PostProcessor)
}

func testConfig() map[string]interface{} {
	return map[string]interface{}{
		"inline": []interface{}{"foo", "bar"},
	}
}

func TestPostProcessor_Impl(t *testing.T) {
	var raw interface{}
	raw = &PostProcessor{}
	if _, ok := raw.(packer.PostProcessor); !ok {
		t.Fatalf("must be a post processor")
	}
}

func TestPostProcessorPrepare_Defaults(t *testing.T) {
	var p PostProcessor
	raws := testConfig()

	err := p.Configure(raws)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}

func TestPostProcessorPrepare_InlineShebang(t *testing.T) {
	raws := testConfig()

	delete(raws, "inline_shebang")
	p := new(PostProcessor)
	err := p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
	expected := ""
	if runtime.GOOS != "windows" {
		expected = "/bin/sh -e"
	}
	if p.config.InlineShebang != expected {
		t.Fatalf("bad value: %s", p.config.InlineShebang)
	}

	// Test with a good one
	raws["inline_shebang"] = "foo"
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	if p.config.InlineShebang != "foo" {
		t.Fatalf("bad value: %s", p.config.InlineShebang)
	}
}

func TestPostProcessorPrepare_InvalidKey(t *testing.T) {
	var p PostProcessor
	raws := testConfig()

	// Add a random key
	raws["i_should_not_be_valid"] = true
	err := p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestPostProcessorPrepare_Script(t *testing.T) {
	raws := testConfig()
	delete(raws, "inline")

	raws["script"] = "/this/should/not/exist"
	p := new(PostProcessor)
	err := p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a good one
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("error tempfile: %s", err)
	}
	defer os.Remove(tf.Name())

	raws["script"] = tf.Name()
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestPostProcessorPrepare_ExecuteCommand(t *testing.T) {
	// Check that passing a string will work (Backwards Compatibility)
	p := new(PostProcessor)
	raws := testConfig()
	raws["execute_command"] = "foo bar"
	err := p.Configure(raws)
	expected := []string{"sh", "-c", "foo bar"}
	if err != nil {
		t.Fatalf("should handle backwards compatibility: %s", err)
	}
	if strings.Compare(strings.Join(p.config.ExecuteCommand, " "), strings.Join(expected, " ")) != 0 {
		t.Fatalf("Did not get expected execute_command: expected: %#v; received %#v", expected, p.config.ExecuteCommand)
	}

	// Check that passing a list will work
	p = new(PostProcessor)
	raws = testConfig()
	raws["execute_command"] = []string{"foo", "bar"}
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should handle backwards compatibility: %s", err)
	}
	expected = []string{"foo", "bar"}
	if strings.Compare(strings.Join(p.config.ExecuteCommand, " "), strings.Join(expected, " ")) != 0 {
		t.Fatalf("Did not get expected execute_command: expected: %#v; received %#v", expected, p.config.ExecuteCommand)
	}

	// Check that default is as expected
	raws = testConfig()
	delete(raws, "execute_command")
	p = new(PostProcessor)
	p.Configure(raws)
	if runtime.GOOS != "windows" {
		expected = []string{"/bin/sh", "-c", "{{.Vars}}", "{{.Script}}"}
	} else {
		expected = []string{"cmd", "/V", "/C", "{{.Vars}}", "call", "{{.Script}}"}
	}
	if strings.Compare(strings.Join(p.config.ExecuteCommand, " "), strings.Join(expected, " ")) != 0 {
		t.Fatalf("Did not get expected default: expected: %#v; received %#v", expected, p.config.ExecuteCommand)
	}
}

func TestPostProcessorPrepare_ScriptAndInline(t *testing.T) {
	var p PostProcessor
	raws := testConfig()

	// Error if no scripts/inline commands provided
	delete(raws, "inline")
	delete(raws, "script")
	delete(raws, "command")
	delete(raws, "scripts")
	err := p.Configure(raws)
	if err == nil {
		t.Fatalf("should error when no scripts/inline commands are provided")
	}

	// Test with both
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("error tempfile: %s", err)
	}
	defer os.Remove(tf.Name())

	raws["inline"] = []interface{}{"foo"}
	raws["script"] = tf.Name()
	err = p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestPostProcessorPrepare_ScriptAndScripts(t *testing.T) {
	var p PostProcessor
	raws := testConfig()

	// Test with both
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("error tempfile: %s", err)
	}
	defer os.Remove(tf.Name())

	raws["inline"] = []interface{}{"foo"}
	raws["scripts"] = []string{tf.Name()}
	err = p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}
}

func TestPostProcessorPrepare_Scripts(t *testing.T) {
	raws := testConfig()
	delete(raws, "inline")

	raws["scripts"] = []string{}
	p := new(PostProcessor)
	err := p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a good one
	tf, err := ioutil.TempFile("", "packer")
	if err != nil {
		t.Fatalf("error tempfile: %s", err)
	}
	defer os.Remove(tf.Name())

	raws["scripts"] = []string{tf.Name()}
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}

func TestPostProcessorPrepare_EnvironmentVars(t *testing.T) {
	raws := testConfig()

	// Test with a bad case
	raws["environment_vars"] = []string{"badvar", "good=var"}
	p := new(PostProcessor)
	err := p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a trickier case
	raws["environment_vars"] = []string{"=bad"}
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err == nil {
		t.Fatal("should have error")
	}

	// Test with a good case
	// Note: baz= is a real env variable, just empty
	raws["environment_vars"] = []string{"FOO=bar", "baz="}
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	// Test when the env variable value contains an equals sign
	raws["environment_vars"] = []string{"good=withequals=true"}
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}

	// Test when the env variable value starts with an equals sign
	raws["environment_vars"] = []string{"good==true"}
	p = new(PostProcessor)
	err = p.Configure(raws)
	if err != nil {
		t.Fatalf("should not have error: %s", err)
	}
}
<|endoftext|>


<commit_before>package router

import (
	"bytes"
	"github.com/apex/log"
	"github.com/gin-gonic/gin"
	"github.com/pterodactyl/wings/config"
	"github.com/pterodactyl/wings/installer"
	"github.com/pterodactyl/wings/server"
	"github.com/pterodactyl/wings/system"
	"net/http"
)

// Returns information about the system that wings is running on.
func getSystemInformation(c *gin.Context) {
	i, err := system.GetSystemInformation()
	if err != nil {
		TrackedError(err).AbortWithServerError(c)

		return
	}

	c.JSON(http.StatusOK, i)
}

// Returns all of the servers that are registered and configured correctly on
// this wings instance.
func getAllServers(c *gin.Context) {
	c.JSON(http.StatusOK, server.GetServers().All())
}

// Creates a new server on the wings daemon and begins the installation process
// for it.
func postCreateServer(c *gin.Context) {
	buf := bytes.Buffer{}
	buf.ReadFrom(c.Request.Body)

	install, err := installer.New(buf.Bytes())
	if err != nil {
		if installer.IsValidationError(err) {
			c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
				"error": "The data provided in the request could not be validated.",
			})
			return
		}

		TrackedError(err).AbortWithServerError(c)
		return
	}

	// Plop that server instance onto the request so that it can be referenced in
	// requests from here-on out.
	server.GetServers().Add(install.Server())

	// Begin the installation process in the background to not block the request
	// cycle. If there are any errors they will be logged and communicated back
	// to the Panel where a reinstall may take place.
	go func(i *installer.Installer) {
		i.Execute()

		if err := i.Server().Install(); err != nil {
			log.WithFields(log.Fields{"server": i.Uuid(), "error": err}).Error("failed to run install process for server")
		}
	}(install)

	c.Status(http.StatusAccepted)
}

// Updates the running configuration for this daemon instance.
func postUpdateConfiguration(c *gin.Context) {
	// A backup of the configuration for error purposes.
	ccopy := *config.Get()
	// A copy of the configuration we're using to bind the data recevied into.
	cfg := *config.Get()

	// BindJSON sends 400 if the request fails, all we need to do is return
	if err := c.BindJSON(&cfg); err != nil {
		return
	}

	config.Set(&cfg)
	if err := config.Get().WriteToDisk(); err != nil {
		// If there was an error writing to the disk, revert back to the configuration we had
		// before this code was run.
		config.Set(&ccopy)

		TrackedError(err).AbortWithServerError(c)
		return
	}

	c.Status(http.StatusNoContent)
}
<commit_msg>Don't obliterate custom SSL locations if defined<commit_after>package router

import (
	"bytes"
	"github.com/apex/log"
	"github.com/gin-gonic/gin"
	"github.com/pterodactyl/wings/config"
	"github.com/pterodactyl/wings/installer"
	"github.com/pterodactyl/wings/server"
	"github.com/pterodactyl/wings/system"
	"net/http"
	"strings"
)

// Returns information about the system that wings is running on.
func getSystemInformation(c *gin.Context) {
	i, err := system.GetSystemInformation()
	if err != nil {
		TrackedError(err).AbortWithServerError(c)

		return
	}

	c.JSON(http.StatusOK, i)
}

// Returns all of the servers that are registered and configured correctly on
// this wings instance.
func getAllServers(c *gin.Context) {
	c.JSON(http.StatusOK, server.GetServers().All())
}

// Creates a new server on the wings daemon and begins the installation process
// for it.
func postCreateServer(c *gin.Context) {
	buf := bytes.Buffer{}
	buf.ReadFrom(c.Request.Body)

	install, err := installer.New(buf.Bytes())
	if err != nil {
		if installer.IsValidationError(err) {
			c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
				"error": "The data provided in the request could not be validated.",
			})
			return
		}

		TrackedError(err).AbortWithServerError(c)
		return
	}

	// Plop that server instance onto the request so that it can be referenced in
	// requests from here-on out.
	server.GetServers().Add(install.Server())

	// Begin the installation process in the background to not block the request
	// cycle. If there are any errors they will be logged and communicated back
	// to the Panel where a reinstall may take place.
	go func(i *installer.Installer) {
		i.Execute()

		if err := i.Server().Install(); err != nil {
			log.WithFields(log.Fields{"server": i.Uuid(), "error": err}).Error("failed to run install process for server")
		}
	}(install)

	c.Status(http.StatusAccepted)
}

// Updates the running configuration for this daemon instance.
func postUpdateConfiguration(c *gin.Context) {
	// A backup of the configuration for error purposes.
	ccopy := *config.Get()
	// A copy of the configuration we're using to bind the data recevied into.
	cfg := *config.Get()

	// BindJSON sends 400 if the request fails, all we need to do is return
	if err := c.BindJSON(&cfg); err != nil {
		return
	}

	// Keep the SSL certificates the same since the Panel will send through Lets Encrypt
	// default locations. However, if we picked a different location manually we don't
	// want to override that.
	//
	// If you pass through manual locations in the API call this logic will be skipped.
	if strings.HasPrefix(cfg.Api.Ssl.KeyFile, "/etc/letsencrypt/live/") {
		cfg.Api.Ssl.KeyFile = ccopy.Api.Ssl.KeyFile
		cfg.Api.Ssl.CertificateFile = ccopy.Api.Ssl.CertificateFile
	}

	config.Set(&cfg)
	if err := config.Get().WriteToDisk(); err != nil {
		// If there was an error writing to the disk, revert back to the configuration we had
		// before this code was run.
		config.Set(&ccopy)

		TrackedError(err).AbortWithServerError(c)
		return
	}

	c.Status(http.StatusNoContent)
}
<|endoftext|>


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

package repo

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"github.com/Unknwon/com"

	git "github.com/gogits/git-module"
	api "github.com/gogits/go-gogs-client"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/auth"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/context"
	"github.com/gogits/gogs/modules/setting"
)

const (
	HOOKS        base.TplName = "repo/settings/hooks"
	HOOK_NEW     base.TplName = "repo/settings/hook_new"
	ORG_HOOK_NEW base.TplName = "org/settings/hook_new"
)

func Webhooks(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repo.settings.hooks")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["BaseLink"] = ctx.Repo.RepoLink
	ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://github.com/gogits/go-gogs-client/wiki/Repositories-Webhooks")

	ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID)
	if err != nil {
		ctx.Handle(500, "GetWebhooksByRepoID", err)
		return
	}
	ctx.Data["Webhooks"] = ws

	ctx.HTML(200, HOOKS)
}

type OrgRepoCtx struct {
	OrgID       int64
	RepoID      int64
	Link        string
	NewTemplate base.TplName
}

// getOrgRepoCtx determines whether this is a repo context or organization context.
func getOrgRepoCtx(ctx *context.Context) (*OrgRepoCtx, error) {
	if len(ctx.Repo.RepoLink) > 0 {
		return &OrgRepoCtx{
			RepoID:      ctx.Repo.Repository.ID,
			Link:        ctx.Repo.RepoLink,
			NewTemplate: HOOK_NEW,
		}, nil
	}

	if len(ctx.Org.OrgLink) > 0 {
		return &OrgRepoCtx{
			OrgID:       ctx.Org.Organization.ID,
			Link:        ctx.Org.OrgLink,
			NewTemplate: ORG_HOOK_NEW,
		}, nil
	}

	return nil, errors.New("Unable to set OrgRepo context")
}

func checkHookType(ctx *context.Context) string {
	hookType := strings.ToLower(ctx.Params(":type"))
	if !com.IsSliceContainsStr(setting.Webhook.Types, hookType) {
		ctx.Handle(404, "checkHookType", nil)
		return ""
	}
	return hookType
}

func WebhooksNew(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksNew"] = true
	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}

	orCtx, err := getOrgRepoCtx(ctx)
	if err != nil {
		ctx.Handle(500, "getOrgRepoCtx", err)
		return
	}

	ctx.Data["HookType"] = checkHookType(ctx)
	if ctx.Written() {
		return
	}
	ctx.Data["BaseLink"] = orCtx.Link

	ctx.HTML(200, orCtx.NewTemplate)
}

func ParseHookEvent(form auth.WebhookForm) *models.HookEvent {
	return &models.HookEvent{
		PushOnly:       form.PushOnly(),
		SendEverything: form.SendEverything(),
		ChooseEvents:   form.ChooseEvents(),
		HookEvents: models.HookEvents{
			Create:      form.Create,
			Push:        form.Push,
			PullRequest: form.PullRequest,
		},
	}
}

func WebHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) {
	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksNew"] = true
	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
	ctx.Data["HookType"] = "gogs"

	orCtx, err := getOrgRepoCtx(ctx)
	if err != nil {
		ctx.Handle(500, "getOrgRepoCtx", err)
		return
	}
	ctx.Data["BaseLink"] = orCtx.Link

	if ctx.HasError() {
		ctx.HTML(200, orCtx.NewTemplate)
		return
	}

	contentType := models.JSON
	if models.HookContentType(form.ContentType) == models.FORM {
		contentType = models.FORM
	}

	w := &models.Webhook{
		RepoID:       orCtx.RepoID,
		URL:          form.PayloadURL,
		ContentType:  contentType,
		Secret:       form.Secret,
		HookEvent:    ParseHookEvent(form.WebhookForm),
		IsActive:     form.Active,
		HookTaskType: models.GOGS,
		OrgID:        orCtx.OrgID,
	}
	if err := w.UpdateEvent(); err != nil {
		ctx.Handle(500, "UpdateEvent", err)
		return
	} else if err := models.CreateWebhook(w); err != nil {
		ctx.Handle(500, "CreateWebhook", err)
		return
	}

	ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
	ctx.Redirect(orCtx.Link + "/settings/hooks")
}

func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
	ctx.Data["Title"] = ctx.Tr("repo.settings")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksNew"] = true
	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}

	orCtx, err := getOrgRepoCtx(ctx)
	if err != nil {
		ctx.Handle(500, "getOrgRepoCtx", err)
		return
	}

	if ctx.HasError() {
		ctx.HTML(200, orCtx.NewTemplate)
		return
	}

	meta, err := json.Marshal(&models.SlackMeta{
		Channel:  form.Channel,
		Username: form.Username,
		IconURL:  form.IconURL,
		Color:    form.Color,
	})
	if err != nil {
		ctx.Handle(500, "Marshal", err)
		return
	}

	w := &models.Webhook{
		RepoID:       orCtx.RepoID,
		URL:          form.PayloadURL,
		ContentType:  models.JSON,
		HookEvent:    ParseHookEvent(form.WebhookForm),
		IsActive:     form.Active,
		HookTaskType: models.SLACK,
		Meta:         string(meta),
		OrgID:        orCtx.OrgID,
	}
	if err := w.UpdateEvent(); err != nil {
		ctx.Handle(500, "UpdateEvent", err)
		return
	} else if err := models.CreateWebhook(w); err != nil {
		ctx.Handle(500, "CreateWebhook", err)
		return
	}

	ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
	ctx.Redirect(orCtx.Link + "/settings/hooks")
}

func checkWebhook(ctx *context.Context) (*OrgRepoCtx, *models.Webhook) {
	ctx.Data["RequireHighlightJS"] = true

	orCtx, err := getOrgRepoCtx(ctx)
	if err != nil {
		ctx.Handle(500, "getOrgRepoCtx", err)
		return nil, nil
	}
	ctx.Data["BaseLink"] = orCtx.Link

	var w *models.Webhook
	if orCtx.RepoID > 0 {
		w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
	} else {
		w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
	}
	if err != nil {
		if models.IsErrWebhookNotExist(err) {
			ctx.Handle(404, "GetWebhookByID", nil)
		} else {
			ctx.Handle(500, "GetWebhookByID", err)
		}
		return nil, nil
	}

	switch w.HookTaskType {
	case models.SLACK:
		ctx.Data["SlackHook"] = w.GetSlackHook()
		ctx.Data["HookType"] = "slack"
	default:
		ctx.Data["HookType"] = "gogs"
	}

	ctx.Data["History"], err = w.History(1)
	if err != nil {
		ctx.Handle(500, "History", err)
	}
	return orCtx, w
}

func WebHooksEdit(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksEdit"] = true

	orCtx, w := checkWebhook(ctx)
	if ctx.Written() {
		return
	}
	ctx.Data["Webhook"] = w

	ctx.HTML(200, orCtx.NewTemplate)
}

func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) {
	ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksEdit"] = true

	orCtx, w := checkWebhook(ctx)
	if ctx.Written() {
		return
	}
	ctx.Data["Webhook"] = w

	if ctx.HasError() {
		ctx.HTML(200, orCtx.NewTemplate)
		return
	}

	contentType := models.JSON
	if models.HookContentType(form.ContentType) == models.FORM {
		contentType = models.FORM
	}

	w.URL = form.PayloadURL
	w.ContentType = contentType
	w.Secret = form.Secret
	w.HookEvent = ParseHookEvent(form.WebhookForm)
	w.IsActive = form.Active
	if err := w.UpdateEvent(); err != nil {
		ctx.Handle(500, "UpdateEvent", err)
		return
	} else if err := models.UpdateWebhook(w); err != nil {
		ctx.Handle(500, "WebHooksEditPost", err)
		return
	}

	ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
	ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
}

func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
	ctx.Data["Title"] = ctx.Tr("repo.settings")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksEdit"] = true

	orCtx, w := checkWebhook(ctx)
	if ctx.Written() {
		return
	}
	ctx.Data["Webhook"] = w

	if ctx.HasError() {
		ctx.HTML(200, orCtx.NewTemplate)
		return
	}

	meta, err := json.Marshal(&models.SlackMeta{
		Channel:  form.Channel,
		Username: form.Username,
		IconURL:  form.IconURL,
		Color:    form.Color,
	})
	if err != nil {
		ctx.Handle(500, "Marshal", err)
		return
	}

	w.URL = form.PayloadURL
	w.Meta = string(meta)
	w.HookEvent = ParseHookEvent(form.WebhookForm)
	w.IsActive = form.Active
	if err := w.UpdateEvent(); err != nil {
		ctx.Handle(500, "UpdateEvent", err)
		return
	} else if err := models.UpdateWebhook(w); err != nil {
		ctx.Handle(500, "UpdateWebhook", err)
		return
	}

	ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
	ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
}

func TestWebhook(ctx *context.Context) {
	// Grab latest commit or fake one if it's empty repository.
	commit := ctx.Repo.Commit
	if commit == nil {
		ghost := models.NewGhostUser()
		commit = &git.Commit{
			ID:            git.MustIDFromString(git.EMPTY_SHA),
			Author:        ghost.NewGitSig(),
			Committer:     ghost.NewGitSig(),
			CommitMessage: "This is a fake commit",
		}
	}

	apiUser := ctx.User.APIFormat()
	p := &api.PushPayload{
		Ref:    git.BRANCH_PREFIX + ctx.Repo.Repository.DefaultBranch,
		Before: commit.ID.String(),
		After:  commit.ID.String(),
		Commits: []*api.PayloadCommit{
			{
				ID:      commit.ID.String(),
				Message: commit.Message(),
				URL:     ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
				Author: &api.PayloadUser{
					Name:  commit.Author.Name,
					Email: commit.Author.Email,
				},
				Committer: &api.PayloadUser{
					Name:  commit.Committer.Name,
					Email: commit.Committer.Email,
				},
			},
		},
		Repo:   ctx.Repo.Repository.APIFormat(nil),
		Pusher: apiUser,
		Sender: apiUser,
	}
	if err := models.PrepareWebhooks(ctx.Repo.Repository, models.HOOK_EVENT_PUSH, p); err != nil {
		ctx.Flash.Error("PrepareWebhooks: " + err.Error())
		ctx.Status(500)
	} else {
		go models.HookQueue.Add(ctx.Repo.Repository.ID)
		ctx.Flash.Info(ctx.Tr("repo.settings.webhook.test_delivery_success"))
		ctx.Status(200)
	}
}

func DeleteWebhook(ctx *context.Context) {
	if err := models.DeleteWebhookOfRepoByID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil {
		ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
	} else {
		ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
	}

	ctx.JSON(200, map[string]interface{}{
		"redirect": ctx.Repo.RepoLink + "/settings/hooks",
	})
}
<commit_msg>webhook: match email with real user in database for test delivery (#3652)<commit_after>// Copyright 2015 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repo

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"github.com/Unknwon/com"

	git "github.com/gogits/git-module"
	api "github.com/gogits/go-gogs-client"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/auth"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/context"
	"github.com/gogits/gogs/modules/setting"
)

const (
	HOOKS        base.TplName = "repo/settings/hooks"
	HOOK_NEW     base.TplName = "repo/settings/hook_new"
	ORG_HOOK_NEW base.TplName = "org/settings/hook_new"
)

func Webhooks(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repo.settings.hooks")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["BaseLink"] = ctx.Repo.RepoLink
	ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://github.com/gogits/go-gogs-client/wiki/Repositories-Webhooks")

	ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID)
	if err != nil {
		ctx.Handle(500, "GetWebhooksByRepoID", err)
		return
	}
	ctx.Data["Webhooks"] = ws

	ctx.HTML(200, HOOKS)
}

type OrgRepoCtx struct {
	OrgID       int64
	RepoID      int64
	Link        string
	NewTemplate base.TplName
}

// getOrgRepoCtx determines whether this is a repo context or organization context.
func getOrgRepoCtx(ctx *context.Context) (*OrgRepoCtx, error) {
	if len(ctx.Repo.RepoLink) > 0 {
		return &OrgRepoCtx{
			RepoID:      ctx.Repo.Repository.ID,
			Link:        ctx.Repo.RepoLink,
			NewTemplate: HOOK_NEW,
		}, nil
	}

	if len(ctx.Org.OrgLink) > 0 {
		return &OrgRepoCtx{
			OrgID:       ctx.Org.Organization.ID,
			Link:        ctx.Org.OrgLink,
			NewTemplate: ORG_HOOK_NEW,
		}, nil
	}

	return nil, errors.New("Unable to set OrgRepo context")
}

func checkHookType(ctx *context.Context) string {
	hookType := strings.ToLower(ctx.Params(":type"))
	if !com.IsSliceContainsStr(setting.Webhook.Types, hookType) {
		ctx.Handle(404, "checkHookType", nil)
		return ""
	}
	return hookType
}

func WebhooksNew(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksNew"] = true
	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}

	orCtx, err := getOrgRepoCtx(ctx)
	if err != nil {
		ctx.Handle(500, "getOrgRepoCtx", err)
		return
	}

	ctx.Data["HookType"] = checkHookType(ctx)
	if ctx.Written() {
		return
	}
	ctx.Data["BaseLink"] = orCtx.Link

	ctx.HTML(200, orCtx.NewTemplate)
}

func ParseHookEvent(form auth.WebhookForm) *models.HookEvent {
	return &models.HookEvent{
		PushOnly:       form.PushOnly(),
		SendEverything: form.SendEverything(),
		ChooseEvents:   form.ChooseEvents(),
		HookEvents: models.HookEvents{
			Create:      form.Create,
			Push:        form.Push,
			PullRequest: form.PullRequest,
		},
	}
}

func WebHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) {
	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksNew"] = true
	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
	ctx.Data["HookType"] = "gogs"

	orCtx, err := getOrgRepoCtx(ctx)
	if err != nil {
		ctx.Handle(500, "getOrgRepoCtx", err)
		return
	}
	ctx.Data["BaseLink"] = orCtx.Link

	if ctx.HasError() {
		ctx.HTML(200, orCtx.NewTemplate)
		return
	}

	contentType := models.JSON
	if models.HookContentType(form.ContentType) == models.FORM {
		contentType = models.FORM
	}

	w := &models.Webhook{
		RepoID:       orCtx.RepoID,
		URL:          form.PayloadURL,
		ContentType:  contentType,
		Secret:       form.Secret,
		HookEvent:    ParseHookEvent(form.WebhookForm),
		IsActive:     form.Active,
		HookTaskType: models.GOGS,
		OrgID:        orCtx.OrgID,
	}
	if err := w.UpdateEvent(); err != nil {
		ctx.Handle(500, "UpdateEvent", err)
		return
	} else if err := models.CreateWebhook(w); err != nil {
		ctx.Handle(500, "CreateWebhook", err)
		return
	}

	ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
	ctx.Redirect(orCtx.Link + "/settings/hooks")
}

func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
	ctx.Data["Title"] = ctx.Tr("repo.settings")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksNew"] = true
	ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}

	orCtx, err := getOrgRepoCtx(ctx)
	if err != nil {
		ctx.Handle(500, "getOrgRepoCtx", err)
		return
	}

	if ctx.HasError() {
		ctx.HTML(200, orCtx.NewTemplate)
		return
	}

	meta, err := json.Marshal(&models.SlackMeta{
		Channel:  form.Channel,
		Username: form.Username,
		IconURL:  form.IconURL,
		Color:    form.Color,
	})
	if err != nil {
		ctx.Handle(500, "Marshal", err)
		return
	}

	w := &models.Webhook{
		RepoID:       orCtx.RepoID,
		URL:          form.PayloadURL,
		ContentType:  models.JSON,
		HookEvent:    ParseHookEvent(form.WebhookForm),
		IsActive:     form.Active,
		HookTaskType: models.SLACK,
		Meta:         string(meta),
		OrgID:        orCtx.OrgID,
	}
	if err := w.UpdateEvent(); err != nil {
		ctx.Handle(500, "UpdateEvent", err)
		return
	} else if err := models.CreateWebhook(w); err != nil {
		ctx.Handle(500, "CreateWebhook", err)
		return
	}

	ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
	ctx.Redirect(orCtx.Link + "/settings/hooks")
}

func checkWebhook(ctx *context.Context) (*OrgRepoCtx, *models.Webhook) {
	ctx.Data["RequireHighlightJS"] = true

	orCtx, err := getOrgRepoCtx(ctx)
	if err != nil {
		ctx.Handle(500, "getOrgRepoCtx", err)
		return nil, nil
	}
	ctx.Data["BaseLink"] = orCtx.Link

	var w *models.Webhook
	if orCtx.RepoID > 0 {
		w, err = models.GetWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
	} else {
		w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
	}
	if err != nil {
		if models.IsErrWebhookNotExist(err) {
			ctx.Handle(404, "GetWebhookByID", nil)
		} else {
			ctx.Handle(500, "GetWebhookByID", err)
		}
		return nil, nil
	}

	switch w.HookTaskType {
	case models.SLACK:
		ctx.Data["SlackHook"] = w.GetSlackHook()
		ctx.Data["HookType"] = "slack"
	default:
		ctx.Data["HookType"] = "gogs"
	}

	ctx.Data["History"], err = w.History(1)
	if err != nil {
		ctx.Handle(500, "History", err)
	}
	return orCtx, w
}

func WebHooksEdit(ctx *context.Context) {
	ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksEdit"] = true

	orCtx, w := checkWebhook(ctx)
	if ctx.Written() {
		return
	}
	ctx.Data["Webhook"] = w

	ctx.HTML(200, orCtx.NewTemplate)
}

func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) {
	ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksEdit"] = true

	orCtx, w := checkWebhook(ctx)
	if ctx.Written() {
		return
	}
	ctx.Data["Webhook"] = w

	if ctx.HasError() {
		ctx.HTML(200, orCtx.NewTemplate)
		return
	}

	contentType := models.JSON
	if models.HookContentType(form.ContentType) == models.FORM {
		contentType = models.FORM
	}

	w.URL = form.PayloadURL
	w.ContentType = contentType
	w.Secret = form.Secret
	w.HookEvent = ParseHookEvent(form.WebhookForm)
	w.IsActive = form.Active
	if err := w.UpdateEvent(); err != nil {
		ctx.Handle(500, "UpdateEvent", err)
		return
	} else if err := models.UpdateWebhook(w); err != nil {
		ctx.Handle(500, "WebHooksEditPost", err)
		return
	}

	ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
	ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
}

func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
	ctx.Data["Title"] = ctx.Tr("repo.settings")
	ctx.Data["PageIsSettingsHooks"] = true
	ctx.Data["PageIsSettingsHooksEdit"] = true

	orCtx, w := checkWebhook(ctx)
	if ctx.Written() {
		return
	}
	ctx.Data["Webhook"] = w

	if ctx.HasError() {
		ctx.HTML(200, orCtx.NewTemplate)
		return
	}

	meta, err := json.Marshal(&models.SlackMeta{
		Channel:  form.Channel,
		Username: form.Username,
		IconURL:  form.IconURL,
		Color:    form.Color,
	})
	if err != nil {
		ctx.Handle(500, "Marshal", err)
		return
	}

	w.URL = form.PayloadURL
	w.Meta = string(meta)
	w.HookEvent = ParseHookEvent(form.WebhookForm)
	w.IsActive = form.Active
	if err := w.UpdateEvent(); err != nil {
		ctx.Handle(500, "UpdateEvent", err)
		return
	} else if err := models.UpdateWebhook(w); err != nil {
		ctx.Handle(500, "UpdateWebhook", err)
		return
	}

	ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
	ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
}

func TestWebhook(ctx *context.Context) {
	var authorUsername, committerUsername string

	// Grab latest commit or fake one if it's empty repository.
	commit := ctx.Repo.Commit
	if commit == nil {
		ghost := models.NewGhostUser()
		commit = &git.Commit{
			ID:            git.MustIDFromString(git.EMPTY_SHA),
			Author:        ghost.NewGitSig(),
			Committer:     ghost.NewGitSig(),
			CommitMessage: "This is a fake commit",
		}
		authorUsername = ghost.Name
		committerUsername = ghost.Name
	} else {
		// Try to match email with a real user.
		author, err := models.GetUserByEmail(commit.Author.Email)
		if err == nil {
			authorUsername = author.Name
		} else if !models.IsErrUserNotExist(err) {
			ctx.Flash.Error(fmt.Sprintf("GetUserByEmail.(author) [%s]: %v", commit.Author.Email, err))
			ctx.Status(500)
			return
		}

		committer, err := models.GetUserByEmail(commit.Committer.Email)
		if err == nil {
			committerUsername = committer.Name
		} else if !models.IsErrUserNotExist(err) {
			ctx.Flash.Error(fmt.Sprintf("GetUserByEmail.(committer) [%s]: %v", commit.Committer.Email, err))
			ctx.Status(500)
			return
		}
	}

	apiUser := ctx.User.APIFormat()
	p := &api.PushPayload{
		Ref:    git.BRANCH_PREFIX + ctx.Repo.Repository.DefaultBranch,
		Before: commit.ID.String(),
		After:  commit.ID.String(),
		Commits: []*api.PayloadCommit{
			{
				ID:      commit.ID.String(),
				Message: commit.Message(),
				URL:     ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
				Author: &api.PayloadUser{
					Name:     commit.Author.Name,
					Email:    commit.Author.Email,
					UserName: authorUsername,
				},
				Committer: &api.PayloadUser{
					Name:     commit.Committer.Name,
					Email:    commit.Committer.Email,
					UserName: committerUsername,
				},
			},
		},
		Repo:   ctx.Repo.Repository.APIFormat(nil),
		Pusher: apiUser,
		Sender: apiUser,
	}
	if err := models.PrepareWebhooks(ctx.Repo.Repository, models.HOOK_EVENT_PUSH, p); err != nil {
		ctx.Flash.Error("PrepareWebhooks: " + err.Error())
		ctx.Status(500)
	} else {
		go models.HookQueue.Add(ctx.Repo.Repository.ID)
		ctx.Flash.Info(ctx.Tr("repo.settings.webhook.test_delivery_success"))
		ctx.Status(200)
	}
}

func DeleteWebhook(ctx *context.Context) {
	if err := models.DeleteWebhookOfRepoByID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil {
		ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
	} else {
		ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
	}

	ctx.JSON(200, map[string]interface{}{
		"redirect": ctx.Repo.RepoLink + "/settings/hooks",
	})
}
<|endoftext|>


<commit_before>// Copyright 2014, The Serviced Authors. All rights reserved.
// Use of this source code is governed by a
// license that can be found in the LICENSE file.

package agent

import (
	"github.com/zenoss/serviced/domain/host"
	"github.com/zenoss/serviced/utils"

	"testing"
)

func TestGetInfo(t *testing.T) {

	ip, err := utils.GetIPAddress()
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}

	agent := NewServer()
	h := host.New()
	request := BuildHostRequest{IP: "", PoolID: "testpool", IPResources: make([]string, 0)}

	err = agent.BuildHost(request, h)
	if err != nil {
		t.Errorf("Unexpected error %v", err)
	}
	if len(h.IPs) != 1 {
		t.Errorf("Unexpected result %v", h.IPs)
	}
	if h.IPAddr != ip {
		t.Errorf("Expected ip %v, got %v", ip, h.IPs)
	}
	if h.IPs[0].IPAddress != ip {
		t.Errorf("Expected ip %v, got %v", ip, h.IPs)
	}

	request = BuildHostRequest{IP: "127.0.0.1", PoolID: "testpool", IPResources: []string{}}

	err = agent.BuildHost(request, h)
	if err == nil || err.Error() != "loopback address 127.0.0.1 cannot be used to register a host" {
		t.Errorf("Unexpected error %v", err)
	}

	request = BuildHostRequest{IP: "", PoolID: "testpool", IPResources: []string{"127.0.0.1"}}

	err = agent.BuildHost(request, h)
	if err == nil || err.Error() != "loopback address 127.0.0.1 cannot be used as an IP Resource" {
		t.Errorf("Unexpected error %v", err)
	}

}
<commit_msg>fix rpc test<commit_after>// Copyright 2014, The Serviced Authors. All rights reserved.
// Use of this source code is governed by a
// license that can be found in the LICENSE file.

package agent

import (
	"github.com/zenoss/serviced/domain/host"
	"github.com/zenoss/serviced/utils"

	"strings"
	"testing"
)

func TestGetInfo(t *testing.T) {

	ip, err := utils.GetIPAddress()
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}

	staticIPs := []string{ip}
	agent := NewServer(staticIPs)
	h := host.New()
	request := BuildHostRequest{IP: "", PoolID: "testpool"}

	err = agent.BuildHost(request, h)
	if err != nil && !strings.Contains(err.Error(), "not valid for this host") {
		t.Fatalf("Unexpected error %v", err)
	}
	if len(h.IPs) != 0 {
		t.Fatalf("Unexpected result %v (%d)", h.IPs, len(h.IPs))
	}

	request = BuildHostRequest{IP: "127.0.0.1", PoolID: "testpool"}

	err = agent.BuildHost(request, h)
	if err == nil || err.Error() != "loopback address 127.0.0.1 cannot be used to register a host" {
		t.Fatalf("Unexpected error %v", err)
	}

	request = BuildHostRequest{IP: "", PoolID: "testpool"}

	err = agent.BuildHost(request, h)
	if err == nil || !strings.Contains(err.Error(), "not valid for this host") {
		t.Errorf("Unexpected error %v", err)
	}

}
<|endoftext|>


<commit_before>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * rpc/websocket_client.go                                *
 *                                                        *
 * hprose websocket client for Go.                        *
 *                                                        *
 * LastModified: Sep 30, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package rpc

import (
	"crypto/tls"
	"net/http"
	"net/url"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gorilla/websocket"
)

type websocketReqeust struct {
	id   uint32
	data []byte
}

type websocketResponse struct {
	data []byte
	err  error
}

// WebSocketClient is hprose websocket client
type WebSocketClient struct {
	*BaseClient
	*http.Header
	MaxConcurrentRequests int
	dialer                *websocket.Dialer
	cond                  *sync.Cond
	conn                  *websocket.Conn
	nextid                uint32
	requests              chan websocketReqeust
	responses             map[uint32]chan websocketResponse
}

// NewWebSocketClient is the constructor of WebSocketClient
func NewWebSocketClient(uri ...string) (client *WebSocketClient) {
	client = new(WebSocketClient)
	client.BaseClient = NewBaseClient()
	client.Header = new(http.Header)
	client.MaxConcurrentRequests = 10
	client.dialer = new(websocket.Dialer)
	client.cond = sync.NewCond(new(sync.Mutex))
	client.SetURIList(uri)
	client.SendAndReceive = client.sendAndReceive
	return
}

func newWebSocketClient(uri ...string) Client {
	return NewWebSocketClient(uri...)
}

func checkWebSocketAddresses(client Client, uriList []string) {
	for _, uri := range uriList {
		if u, err := url.Parse(uri); err == nil {
			if u.Scheme != "ws" && u.Scheme != "wss" {
				panic("This client desn't support " + u.Scheme + " scheme.")
			}
			if u.Scheme == "wss" {
				client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
			}
		}
	}
}

// SetURIList set a list of server addresses
func (client *WebSocketClient) SetURIList(uriList []string) {
	checkWebSocketAddresses(client, uriList)
	client.BaseClient.SetURIList(uriList)
}

func (client *WebSocketClient) close(err error) {
	client.cond.L.Lock()
	if err != nil && client.responses != nil {
		for _, response := range client.responses {
			response <- websocketResponse{nil, err}
		}
	}
	client.responses = nil
	if client.conn != nil {
		client.conn.Close()
		client.conn = nil
	}
	client.cond.Broadcast()
	client.cond.L.Unlock()
}

// Close the client
func (client *WebSocketClient) Close() {
	client.close(errClientIsAlreadyClosed)
}

// TLSClientConfig returns the tls.Config in hprose client
func (client *WebSocketClient) TLSClientConfig() *tls.Config {
	return client.dialer.TLSClientConfig
}

// SetTLSClientConfig sets the tls.Config
func (client *WebSocketClient) SetTLSClientConfig(config *tls.Config) {
	client.dialer.TLSClientConfig = config
}

func (client *WebSocketClient) sendLoop() {
	conn := client.conn
	for request := range client.requests {
		err := conn.WriteMessage(websocket.BinaryMessage, request.data)
		if err != nil {
			client.close(err)
			break
		}
	}
	client.requests = nil
}

func (client *WebSocketClient) recvLoop() {
	conn := client.conn
	count := client.MaxConcurrentRequests
	for {
		msgType, data, err := conn.ReadMessage()
		if err != nil {
			client.close(err)
			break
		}
		if msgType == websocket.BinaryMessage {
			id := toUint32(data)
			client.cond.L.Lock()
			client.responses[id] <- websocketResponse{data[4:], nil}
			delete(client.responses, id)
			if len(client.responses) < count {
				client.cond.Signal()
			}
			client.cond.L.Unlock()
		}
	}
	close(client.requests)
}

func (client *WebSocketClient) getConn(uri string) (err error) {
	if client.conn == nil {
		client.conn, _, err = client.dialer.Dial(uri, *client.Header)
		if err != nil {
			return err
		}
		count := client.MaxConcurrentRequests
		client.requests = make(chan websocketReqeust, count)
		client.responses = make(map[uint32]chan websocketResponse, count)
		go client.sendLoop()
		go client.recvLoop()
	}
	return nil
}

func (client *WebSocketClient) sendAndReceive(
	data []byte, context *ClientContext) ([]byte, error) {
	id := atomic.AddUint32(&client.nextid, 1)
	buf := make([]byte, len(data)+4)
	fromUint32(buf, id)
	copy(buf[4:], data)
	response := make(chan websocketResponse)
	client.cond.L.Lock()
	for {
		if len(client.responses) < client.MaxConcurrentRequests {
			break
		}
		client.cond.Wait()
	}
	if err := client.getConn(client.uri); err != nil {
		client.cond.L.Unlock()
		return nil, err
	}
	client.responses[id] = response
	client.cond.L.Unlock()
	client.requests <- websocketReqeust{id, buf}
	select {
	case resp := <-response:
		return resp.data, resp.err
	case <-time.After(client.timeout):
		return nil, ErrTimeout
	}
}
<commit_msg>Fixed timeout<commit_after>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * rpc/websocket_client.go                                *
 *                                                        *
 * hprose websocket client for Go.                        *
 *                                                        *
 * LastModified: Sep 30, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package rpc

import (
	"crypto/tls"
	"net/http"
	"net/url"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gorilla/websocket"
)

type websocketReqeust struct {
	id   uint32
	data []byte
}

type websocketResponse struct {
	data []byte
	err  error
}

// WebSocketClient is hprose websocket client
type WebSocketClient struct {
	*BaseClient
	*http.Header
	MaxConcurrentRequests int
	dialer                *websocket.Dialer
	cond                  *sync.Cond
	conn                  *websocket.Conn
	nextid                uint32
	requests              chan websocketReqeust
	responses             map[uint32]chan websocketResponse
}

// NewWebSocketClient is the constructor of WebSocketClient
func NewWebSocketClient(uri ...string) (client *WebSocketClient) {
	client = new(WebSocketClient)
	client.BaseClient = NewBaseClient()
	client.Header = new(http.Header)
	client.MaxConcurrentRequests = 10
	client.dialer = new(websocket.Dialer)
	client.cond = sync.NewCond(new(sync.Mutex))
	client.SetURIList(uri)
	client.SendAndReceive = client.sendAndReceive
	return
}

func newWebSocketClient(uri ...string) Client {
	return NewWebSocketClient(uri...)
}

func checkWebSocketAddresses(client Client, uriList []string) {
	for _, uri := range uriList {
		if u, err := url.Parse(uri); err == nil {
			if u.Scheme != "ws" && u.Scheme != "wss" {
				panic("This client desn't support " + u.Scheme + " scheme.")
			}
			if u.Scheme == "wss" {
				client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
			}
		}
	}
}

// SetURIList set a list of server addresses
func (client *WebSocketClient) SetURIList(uriList []string) {
	checkWebSocketAddresses(client, uriList)
	client.BaseClient.SetURIList(uriList)
}

func (client *WebSocketClient) close(err error) {
	client.cond.L.Lock()
	if err != nil && client.responses != nil {
		for _, response := range client.responses {
			response <- websocketResponse{nil, err}
		}
	}
	client.responses = nil
	if client.conn != nil {
		client.conn.Close()
		client.conn = nil
	}
	client.cond.Broadcast()
	client.cond.L.Unlock()
}

// Close the client
func (client *WebSocketClient) Close() {
	client.close(errClientIsAlreadyClosed)
}

// TLSClientConfig returns the tls.Config in hprose client
func (client *WebSocketClient) TLSClientConfig() *tls.Config {
	return client.dialer.TLSClientConfig
}

// SetTLSClientConfig sets the tls.Config
func (client *WebSocketClient) SetTLSClientConfig(config *tls.Config) {
	client.dialer.TLSClientConfig = config
}

func (client *WebSocketClient) sendLoop() {
	conn := client.conn
	for request := range client.requests {
		err := conn.WriteMessage(websocket.BinaryMessage, request.data)
		if err != nil {
			client.close(err)
			break
		}
	}
	client.requests = nil
}

func (client *WebSocketClient) recvLoop() {
	conn := client.conn
	count := client.MaxConcurrentRequests
	for {
		msgType, data, err := conn.ReadMessage()
		if err != nil {
			client.close(err)
			break
		}
		if msgType == websocket.BinaryMessage {
			id := toUint32(data)
			client.cond.L.Lock()
			response := client.responses[id]
			if response != nil {
				response <- websocketResponse{data[4:], nil}
				delete(client.responses, id)
			}
			if len(client.responses) < count {
				client.cond.Signal()
			}
			client.cond.L.Unlock()
		}
	}
	close(client.requests)
}

func (client *WebSocketClient) getConn(uri string) (err error) {
	if client.conn == nil {
		client.conn, _, err = client.dialer.Dial(uri, *client.Header)
		if err != nil {
			return err
		}
		count := client.MaxConcurrentRequests
		client.requests = make(chan websocketReqeust, count)
		client.responses = make(map[uint32]chan websocketResponse, count)
		go client.sendLoop()
		go client.recvLoop()
	}
	return nil
}

func (client *WebSocketClient) sendAndReceive(
	data []byte, context *ClientContext) ([]byte, error) {
	id := atomic.AddUint32(&client.nextid, 1)
	buf := make([]byte, len(data)+4)
	fromUint32(buf, id)
	copy(buf[4:], data)
	response := make(chan websocketResponse)
	client.cond.L.Lock()
	for {
		if len(client.responses) < client.MaxConcurrentRequests {
			break
		}
		client.cond.Wait()
	}
	if err := client.getConn(client.uri); err != nil {
		client.cond.L.Unlock()
		return nil, err
	}
	client.responses[id] = response
	client.cond.L.Unlock()
	client.requests <- websocketReqeust{id, buf}
	select {
	case resp := <-response:
		return resp.data, resp.err
	case <-time.After(client.timeout):
		client.cond.L.Lock()
		delete(client.responses, id)
		if len(client.responses) < client.MaxConcurrentRequests {
			client.cond.Signal()
		}
		client.cond.L.Unlock()
		return nil, ErrTimeout
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"database/sql"
	"fmt"
	"io"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"github.com/kelseyhightower/envconfig"

	"nimona.io/internal/net"
	"nimona.io/internal/version"
	"nimona.io/pkg/blob"
	"nimona.io/pkg/context"
	"nimona.io/pkg/crypto"
	"nimona.io/pkg/errors"
	"nimona.io/pkg/hyperspace/resolver"
	"nimona.io/pkg/localpeer"
	"nimona.io/pkg/log"
	"nimona.io/pkg/network"
	"nimona.io/pkg/object"
	"nimona.io/pkg/objectmanager"
	"nimona.io/pkg/objectstore"
	"nimona.io/pkg/peer"
	"nimona.io/pkg/sqlobjectstore"
)

type fileTransfer struct {
	local         localpeer.LocalPeer
	objectmanager objectmanager.ObjectManager
	objectstore   objectstore.Store
	blobmanager   blob.Manager
	resolver      resolver.Resolver
	listener      net.Listener
	config        *config
}

// nolint: lll
type config struct {
	Peer struct {
		PrivateKey  crypto.PrivateKey `envconfig:"PRIVATE_KEY"`
		BindAddress string            `envconfig:"BIND_ADDRESS" default:"0.0.0.0:0"`
		Bootstraps  []peer.Shorthand  `envconfig:"BOOTSTRAPS"`
	} `envconfig:"PEER"`
	ReceivedFolder string `envconfig:"RECEIVED_FOLDER" default:"received_files"`
}

type fileUnloaded struct {
	Metadata object.Metadata `nimona:"metadata:m,omitempty"`
	BlobHash object.Hash     `nimona:"blob:r,omitempty"`
}

func (e *fileUnloaded) Type() string {
	return "nimona.io/File"
}

func main() {
	args := os.Args
	if len(args) != 3 {
		fmt.Printf("usage: %s <get/serve> <hash/file>\n", args[0])
		return
	}

	ctx := context.New(
		context.WithCorrelationID("nimona"),
	)
	logger := log.FromContext(ctx).With(
		log.String("build.version", version.Version),
		log.String("build.commit", version.Commit),
		log.String("build.timestamp", version.Date),
	)

	cfg := &config{}
	if err := envconfig.Process("nimona", cfg); err != nil {
		logger.Fatal("error processing config", log.Error(err))
	}

	ft, err := newFileTransfer(ctx, cfg, logger)
	if err != nil {
		logger.Fatal("error initializing", log.Error(err))
	}
	defer ft.close()

	command := args[1]
	param := args[2]

	switch command {
	case "get":
		ft.get(ctx, object.Hash(param))
	case "serve":
		ft.serve(ctx, param)
	default:
		fmt.Println("command not supported")
		return
	}

}

func (ft *fileTransfer) serve(
	ctx context.Context,
	filePath string,
) {
	fileName := filepath.Base(filePath)

	start := time.Now()

	blobUnl, err := ft.blobmanager.ImportFromFile(ctx, filePath)
	if err != nil {
		fmt.Println("failed to import blob", err)
		return
	}

	fl := &File{
		Name: fileName,
		Blob: blobUnl.ToObject().Hash(),
	}

	if _, err := ft.objectmanager.Put(ctx, fl.ToObject()); err != nil {
		fmt.Println("failed to store blob", err)
		return
	}
	fmt.Println(">> imported in", time.Now().Sub(start))
	fmt.Println(">> blob hash:", blobUnl.ToObject().Hash())
	fmt.Println(">> file hash:", fl.ToObject().Hash())

	// os.Exit(1)
	// register for termination signals
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// and wait for one
	<-sigs
}

func (ft *fileTransfer) findAndRequest(
	ctx context.Context,
	hash object.Hash,
) (
	*object.Object,
	error,
) {
	peers, err := ft.resolver.Lookup(ctx, resolver.LookupByContentHash(hash))
	if err != nil {
		return nil, err
	}

	if len(peers) == 0 {
		return nil, errors.New("no providers found")
	}

	obj, err := ft.objectmanager.Request(ctx, hash, peers[0], true)
	if err != nil {
		return nil, err
	}

	return obj, nil
}

func (ft *fileTransfer) get(
	ctx context.Context,
	hash object.Hash,
) {
	fmt.Println("getting file:", hash)

	obj, err := ft.findAndRequest(ctx, hash)
	if err != nil {
		fmt.Println("failed to request file: ", err)
		return
	}

	fl := &File{}
	if err := fl.FromObject(obj); err != nil {
		fmt.Println("object not of type file: ", err)
		return
	}

	flun := &fileUnloaded{}

	err = object.Decode(obj, flun)

	fmt.Println("getting blob:", flun.BlobHash)
	bl, err := ft.blobmanager.Request(ctx, flun.BlobHash)
	if err != nil {
		fmt.Println("failed to request file:", err)
	}

	_ = os.MkdirAll(ft.config.ReceivedFolder, os.ModePerm)
	f, err := os.Create(filepath.Join(ft.config.ReceivedFolder, fl.Name))
	if err != nil {
		fmt.Println("failed to create file:", err)
		return
	}

	fmt.Println("writing file:", fl.Name)
	r := blob.FromBlob(bl)
	bf := bufio.NewReader(r)
	if _, err := io.Copy(f, bf); err != nil {
		fmt.Println("failed to write to file:", err)
	}

	fmt.Println("done")

}

func (ft *fileTransfer) close() {
	if ft.listener != nil {
		ft.listener.Close()
	}
}

func newFileTransfer(
	ctx context.Context,
	cfg *config,
	logger log.Logger,
) (*fileTransfer, error) {
	ft := &fileTransfer{}
	ft.config = cfg
	// construct local peer
	local := localpeer.New()
	// attach peer private key from config
	local.PutPrimaryPeerKey(cfg.Peer.PrivateKey)
	local.PutContentTypes(
		new(File).Type(),
		new(blob.Blob).Type(),
		new(blob.Chunk).Type(),
	)
	ft.local = local

	// construct new network
	net := network.New(
		ctx,
		network.WithLocalPeer(local),
	)

	if cfg.Peer.BindAddress != "" {
		// start listening
		lis, err := net.Listen(
			ctx,
			cfg.Peer.BindAddress,
			network.ListenOnLocalIPs,
			network.ListenOnPrivateIPs,
		)
		if err != nil {
			logger.Fatal("error while listening", log.Error(err))
		}
		ft.listener = lis
	}

	// make sure we have some bootstrap peers to start with
	if len(cfg.Peer.Bootstraps) == 0 {
		cfg.Peer.Bootstraps = []peer.Shorthand{
			"ed25519.CJi6yjjXuNBFDoYYPrp697d6RmpXeW8ZUZPmEce9AgEc@tcps:asimov.bootstrap.nimona.io:22581",
			"ed25519.6fVWVAK2DVGxBhtVBvzNWNKBWk9S83aQrAqGJfrxr75o@tcps:egan.bootstrap.nimona.io:22581",
			"ed25519.7q7YpmPNQmvSCEBWW8ENw8XV8MHzETLostJTYKeaRTcL@tcps:sloan.bootstrap.nimona.io:22581",
		}
	}

	// convert shorthands into peers
	bootstrapPeers := []*peer.ConnectionInfo{}
	for _, s := range cfg.Peer.Bootstraps {
		bootstrapPeer, err := s.ConnectionInfo()
		if err != nil {
			logger.Fatal("error parsing bootstrap peer", log.Error(err))
		}
		bootstrapPeers = append(bootstrapPeers, bootstrapPeer)
	}

	// construct new resolver
	res := resolver.New(
		ctx,
		net,
		resolver.WithBoostrapPeers(bootstrapPeers...),
	)
	ft.resolver = res

	logger = logger.With(
		log.String("peer.privateKey", local.GetPrimaryPeerKey().String()),
		log.String("peer.publicKey", local.GetPrimaryPeerKey().PublicKey().String()),
		log.Strings("peer.addresses", local.GetAddresses()),
	)

	// construct object store
	db, err := sql.Open("sqlite3", "file_transfer.db")
	if err != nil {
		logger.Fatal("error opening sql file", log.Error(err))
	}

	str, err := sqlobjectstore.New(db)
	if err != nil {
		logger.Fatal("error starting sql store", log.Error(err))
	}
	ft.objectstore = str

	// construct object manager
	man := objectmanager.New(
		ctx,
		net,
		res,
		str,
	)
	ft.objectmanager = man

	// construct blob manager
	bm := blob.NewManager(
		ctx,
		blob.WithObjectManager(man),
		blob.WithResolver(res),
	)
	ft.blobmanager = bm

	return ft, nil

}
<commit_msg>feat(examples/files): add bootstraps as relays<commit_after>package main

import (
	"bufio"
	"database/sql"
	"fmt"
	"io"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"github.com/kelseyhightower/envconfig"

	"nimona.io/internal/net"
	"nimona.io/internal/version"
	"nimona.io/pkg/blob"
	"nimona.io/pkg/context"
	"nimona.io/pkg/crypto"
	"nimona.io/pkg/errors"
	"nimona.io/pkg/hyperspace/resolver"
	"nimona.io/pkg/localpeer"
	"nimona.io/pkg/log"
	"nimona.io/pkg/network"
	"nimona.io/pkg/object"
	"nimona.io/pkg/objectmanager"
	"nimona.io/pkg/objectstore"
	"nimona.io/pkg/peer"
	"nimona.io/pkg/sqlobjectstore"
)

type fileTransfer struct {
	local         localpeer.LocalPeer
	objectmanager objectmanager.ObjectManager
	objectstore   objectstore.Store
	blobmanager   blob.Manager
	resolver      resolver.Resolver
	listener      net.Listener
	config        *config
}

// nolint: lll
type config struct {
	Peer struct {
		PrivateKey  crypto.PrivateKey `envconfig:"PRIVATE_KEY"`
		BindAddress string            `envconfig:"BIND_ADDRESS" default:"0.0.0.0:0"`
		Bootstraps  []peer.Shorthand  `envconfig:"BOOTSTRAPS"`
	} `envconfig:"PEER"`
	ReceivedFolder string `envconfig:"RECEIVED_FOLDER" default:"received_files"`
}

type fileUnloaded struct {
	Metadata object.Metadata `nimona:"metadata:m,omitempty"`
	BlobHash object.Hash     `nimona:"blob:r,omitempty"`
}

func (e *fileUnloaded) Type() string {
	return "nimona.io/File"
}

func main() {
	args := os.Args
	if len(args) != 3 {
		fmt.Printf("usage: %s <get/serve> <hash/file>\n", args[0])
		return
	}

	ctx := context.New(
		context.WithCorrelationID("nimona"),
	)
	logger := log.FromContext(ctx).With(
		log.String("build.version", version.Version),
		log.String("build.commit", version.Commit),
		log.String("build.timestamp", version.Date),
	)

	cfg := &config{}
	if err := envconfig.Process("nimona", cfg); err != nil {
		logger.Fatal("error processing config", log.Error(err))
	}

	ft, err := newFileTransfer(ctx, cfg, logger)
	if err != nil {
		logger.Fatal("error initializing", log.Error(err))
	}
	defer ft.close()

	command := args[1]
	param := args[2]

	switch command {
	case "get":
		ft.get(ctx, object.Hash(param))
	case "serve":
		ft.serve(ctx, param)
	default:
		fmt.Println("command not supported")
		return
	}

}

func (ft *fileTransfer) serve(
	ctx context.Context,
	filePath string,
) {
	fileName := filepath.Base(filePath)

	start := time.Now()

	blobUnl, err := ft.blobmanager.ImportFromFile(ctx, filePath)
	if err != nil {
		fmt.Println("failed to import blob", err)
		return
	}

	fl := &File{
		Name: fileName,
		Blob: blobUnl.ToObject().Hash(),
	}

	if _, err := ft.objectmanager.Put(ctx, fl.ToObject()); err != nil {
		fmt.Println("failed to store blob", err)
		return
	}
	fmt.Println(">> imported in", time.Now().Sub(start))
	fmt.Println(">> blob hash:", blobUnl.ToObject().Hash())
	fmt.Println(">> file hash:", fl.ToObject().Hash())

	// os.Exit(1)
	// register for termination signals
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// and wait for one
	<-sigs
}

func (ft *fileTransfer) findAndRequest(
	ctx context.Context,
	hash object.Hash,
) (
	*object.Object,
	error,
) {
	peers, err := ft.resolver.Lookup(ctx, resolver.LookupByContentHash(hash))
	if err != nil {
		return nil, err
	}

	if len(peers) == 0 {
		return nil, errors.New("no providers found")
	}

	obj, err := ft.objectmanager.Request(ctx, hash, peers[0], true)
	if err != nil {
		return nil, err
	}

	return obj, nil
}

func (ft *fileTransfer) get(
	ctx context.Context,
	hash object.Hash,
) {
	fmt.Println("getting file:", hash)

	obj, err := ft.findAndRequest(ctx, hash)
	if err != nil {
		fmt.Println("failed to request file: ", err)
		return
	}

	fl := &File{}
	if err := fl.FromObject(obj); err != nil {
		fmt.Println("object not of type file: ", err)
		return
	}

	flun := &fileUnloaded{}

	err = object.Decode(obj, flun)

	fmt.Println("getting blob:", flun.BlobHash)
	bl, err := ft.blobmanager.Request(ctx, flun.BlobHash)
	if err != nil {
		fmt.Println("failed to request file:", err)
	}

	_ = os.MkdirAll(ft.config.ReceivedFolder, os.ModePerm)
	f, err := os.Create(filepath.Join(ft.config.ReceivedFolder, fl.Name))
	if err != nil {
		fmt.Println("failed to create file:", err)
		return
	}

	fmt.Println("writing file:", fl.Name)
	r := blob.FromBlob(bl)
	bf := bufio.NewReader(r)
	if _, err := io.Copy(f, bf); err != nil {
		fmt.Println("failed to write to file:", err)
	}

	fmt.Println("done")

}

func (ft *fileTransfer) close() {
	if ft.listener != nil {
		ft.listener.Close()
	}
}

func newFileTransfer(
	ctx context.Context,
	cfg *config,
	logger log.Logger,
) (*fileTransfer, error) {
	ft := &fileTransfer{}
	ft.config = cfg
	// construct local peer
	local := localpeer.New()
	// attach peer private key from config
	local.PutPrimaryPeerKey(cfg.Peer.PrivateKey)
	local.PutContentTypes(
		new(File).Type(),
		new(blob.Blob).Type(),
		new(blob.Chunk).Type(),
	)
	ft.local = local

	// construct new network
	net := network.New(
		ctx,
		network.WithLocalPeer(local),
	)

	if cfg.Peer.BindAddress != "" {
		// start listening
		lis, err := net.Listen(
			ctx,
			cfg.Peer.BindAddress,
			network.ListenOnLocalIPs,
			network.ListenOnPrivateIPs,
		)
		if err != nil {
			logger.Fatal("error while listening", log.Error(err))
		}
		ft.listener = lis
	}

	// make sure we have some bootstrap peers to start with
	if len(cfg.Peer.Bootstraps) == 0 {
		cfg.Peer.Bootstraps = []peer.Shorthand{
			"ed25519.CJi6yjjXuNBFDoYYPrp697d6RmpXeW8ZUZPmEce9AgEc@tcps:asimov.bootstrap.nimona.io:22581",
			"ed25519.6fVWVAK2DVGxBhtVBvzNWNKBWk9S83aQrAqGJfrxr75o@tcps:egan.bootstrap.nimona.io:22581",
			"ed25519.7q7YpmPNQmvSCEBWW8ENw8XV8MHzETLostJTYKeaRTcL@tcps:sloan.bootstrap.nimona.io:22581",
		}
	}

	// convert shorthands into peers
	bootstrapPeers := []*peer.ConnectionInfo{}
	for _, s := range cfg.Peer.Bootstraps {
		bootstrapPeer, err := s.ConnectionInfo()
		if err != nil {
			logger.Fatal("error parsing bootstrap peer", log.Error(err))
		}
		bootstrapPeers = append(bootstrapPeers, bootstrapPeer)
	}

	// add bootstrap peers as relays
	local.PutRelays(bootstrapPeers...)

	// construct new resolver
	res := resolver.New(
		ctx,
		net,
		resolver.WithBoostrapPeers(bootstrapPeers...),
	)
	ft.resolver = res

	logger = logger.With(
		log.String("peer.privateKey", local.GetPrimaryPeerKey().String()),
		log.String("peer.publicKey", local.GetPrimaryPeerKey().PublicKey().String()),
		log.Strings("peer.addresses", local.GetAddresses()),
	)

	// construct object store
	db, err := sql.Open("sqlite3", "file_transfer.db")
	if err != nil {
		logger.Fatal("error opening sql file", log.Error(err))
	}

	str, err := sqlobjectstore.New(db)
	if err != nil {
		logger.Fatal("error starting sql store", log.Error(err))
	}
	ft.objectstore = str

	// construct object manager
	man := objectmanager.New(
		ctx,
		net,
		res,
		str,
	)
	ft.objectmanager = man

	// construct blob manager
	bm := blob.NewManager(
		ctx,
		blob.WithObjectManager(man),
		blob.WithResolver(res),
	)
	ft.blobmanager = bm

	return ft, nil

}
<|endoftext|>


<commit_before>// Package flake provides a very simple Twitter Snowflake generator and parser.
// You can optionally set a custom epoch for you use.
package flake

import (
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	timeBits = 41
	nodeBits = 10
	stepBits = 12

	timeMask int64 = -1 ^ (-1 << timeBits)
	nodeMask int64 = -1 ^ (-1 << nodeBits)
	stepMask int64 = -1 ^ (-1 << stepBits)

	nodeMax = -1 ^ (-1 << nodeBits)

	timeShift uint8 = nodeBits + stepBits
	nodeShift uint8 = stepBits
)

// Epoch is set to the twitter snowflake epoch of 2006-03-21:20:50:14 GMT
// You may customize this to set a different epoch for your application.
var Epoch int64 = 1288834974657

// A Node struct holds the basic information needed for a flake generator node
type Node struct {
	sync.Mutex
	time int64
	node int64
	step int64
}

// An ID is a custom type used for a snowflake ID.  This is used so we can
// attach methods onto the ID.
type ID int64

// NewNode returns a new Flake node that can be used to generate flake IDs
func NewNode(node int64) (*Node, error) {

	if node < 0 || node > nodeMax {
		return nil, fmt.Errorf("Node number must be between 0 and 1023")
	}

	return &Node{
		time: 0,
		node: node,
		step: 0,
	}, nil
}

// Generate creates and returns a unique snowflake ID
func (n *Node) Generate() (ID, error) {

	n.Lock()
	defer n.Unlock()

	now := time.Now().UnixNano() / 1000000

	if n.time == now {
		n.step = (n.step + 1) & stepMask

		if n.step == 0 {
			for now <= n.time {
				now = time.Now().UnixNano() / 1000000
			}
		}
	} else {
		n.step = 0
	}

	n.time = now

	return ID((now-Epoch)<<timeShift |
		(n.node << nodeShift) |
		(n.step),
	), nil
}

// Int64 returns an int64 of the snowflake ID
func (f ID) Int64() int64 {
	return int64(f)
}

// String returns a string of the snowflake ID
func (f ID) String() string {
	return fmt.Sprintf("%d", f)
}

// Base2 returns a string base2 of the snowflake ID
func (f ID) Base2() string {
	return strconv.FormatInt(int64(f), 2)
}

// Base36 returns a base36 string of the snowflake ID
func (f ID) Base36() string {
	return strconv.FormatInt(int64(f), 36)
}

// Base64 returns a base64 string of the snowflake ID
func (f ID) Base64() string {
	return base64.StdEncoding.EncodeToString(f.Bytes())
}

// Bytes returns a byte array of the snowflake ID
func (f ID) Bytes() []byte {
	return []byte(f.String())
}

// Time returns an int64 unix timestamp of the snowflake ID time
func (f ID) Time() int64 {
	return (int64(f) >> 22) + Epoch
}

// Node returns an int64 of the snowflake ID node number
func (f ID) Node() int64 {
	return int64(f) & 0x00000000003FF000 >> 12
}

// Step returns an int64 of the snowflake step (or sequence) number
func (f ID) Step() int64 {
	return int64(f) & 0x0000000000000FFF
}

// MarshalJSON returns a json byte array string of the snowflake ID.
func (f ID) MarshalJSON() ([]byte, error) {
	return []byte(`"` + f.String() + `"`), nil
}

// UnmarshalJSON converts a json byte array of a snowflake ID into an ID type.
func (f *ID) UnmarshalJSON(b []byte) error {

	s := strings.Replace(string(b), `"`, ``, 2)
	i, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return err
	}

	*f = ID(i)

	return nil
}
<commit_msg>Shorten package comment<commit_after>// Package flake provides a very simple Twitter snowflake generator and parser.
package flake

import (
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	timeBits = 41
	nodeBits = 10
	stepBits = 12

	timeMask int64 = -1 ^ (-1 << timeBits)
	nodeMask int64 = -1 ^ (-1 << nodeBits)
	stepMask int64 = -1 ^ (-1 << stepBits)

	nodeMax = -1 ^ (-1 << nodeBits)

	timeShift uint8 = nodeBits + stepBits
	nodeShift uint8 = stepBits
)

// Epoch is set to the twitter snowflake epoch of 2006-03-21:20:50:14 GMT
// You may customize this to set a different epoch for your application.
var Epoch int64 = 1288834974657

// A Node struct holds the basic information needed for a flake generator node
type Node struct {
	sync.Mutex
	time int64
	node int64
	step int64
}

// An ID is a custom type used for a snowflake ID.  This is used so we can
// attach methods onto the ID.
type ID int64

// NewNode returns a new Flake node that can be used to generate flake IDs
func NewNode(node int64) (*Node, error) {

	if node < 0 || node > nodeMax {
		return nil, fmt.Errorf("Node number must be between 0 and 1023")
	}

	return &Node{
		time: 0,
		node: node,
		step: 0,
	}, nil
}

// Generate creates and returns a unique snowflake ID
func (n *Node) Generate() (ID, error) {

	n.Lock()
	defer n.Unlock()

	now := time.Now().UnixNano() / 1000000

	if n.time == now {
		n.step = (n.step + 1) & stepMask

		if n.step == 0 {
			for now <= n.time {
				now = time.Now().UnixNano() / 1000000
			}
		}
	} else {
		n.step = 0
	}

	n.time = now

	return ID((now-Epoch)<<timeShift |
		(n.node << nodeShift) |
		(n.step),
	), nil
}

// Int64 returns an int64 of the snowflake ID
func (f ID) Int64() int64 {
	return int64(f)
}

// String returns a string of the snowflake ID
func (f ID) String() string {
	return fmt.Sprintf("%d", f)
}

// Base2 returns a string base2 of the snowflake ID
func (f ID) Base2() string {
	return strconv.FormatInt(int64(f), 2)
}

// Base36 returns a base36 string of the snowflake ID
func (f ID) Base36() string {
	return strconv.FormatInt(int64(f), 36)
}

// Base64 returns a base64 string of the snowflake ID
func (f ID) Base64() string {
	return base64.StdEncoding.EncodeToString(f.Bytes())
}

// Bytes returns a byte array of the snowflake ID
func (f ID) Bytes() []byte {
	return []byte(f.String())
}

// Time returns an int64 unix timestamp of the snowflake ID time
func (f ID) Time() int64 {
	return (int64(f) >> 22) + Epoch
}

// Node returns an int64 of the snowflake ID node number
func (f ID) Node() int64 {
	return int64(f) & 0x00000000003FF000 >> 12
}

// Step returns an int64 of the snowflake step (or sequence) number
func (f ID) Step() int64 {
	return int64(f) & 0x0000000000000FFF
}

// MarshalJSON returns a json byte array string of the snowflake ID.
func (f ID) MarshalJSON() ([]byte, error) {
	return []byte(`"` + f.String() + `"`), nil
}

// UnmarshalJSON converts a json byte array of a snowflake ID into an ID type.
func (f *ID) UnmarshalJSON(b []byte) error {

	s := strings.Replace(string(b), `"`, ``, 2)
	i, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return err
	}

	*f = ID(i)

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package gopacket

import (
	"fmt"
	"strconv"
)

// Endpoint is the set of bytes used to address packets at various layers.
// See LinkLayer, NetworkLayer, and TransportLayer specifications.
// Endpoints are usable as map keys.
type Endpoint struct {
	typ EndpointType
	raw string
}

// EndpointType returns the endpoint type associated with this endpoint.
func (e Endpoint) EndpointType() EndpointType { return e.typ }

// Raw returns the raw bytes of this endpoint.  These aren't human-readable
// most of the time, but they are faster than calling String.
func (e Endpoint) Raw() []byte { return []byte(e.raw) }

// LessThan provides a stable ordering for all endpoints.  It sorts first based
// on the EndpointType of an endpoint, then based on the raw bytes of that endpoint.
// For some endpoints, the actual comparison may not make sense, however this
// ordering does provide useful information for most Endpoint types.
// Ordering is based first on endpoint type, then on raw endpoint bytes.
// Endpoint bytes are sorted lexigraphically.
func (a Endpoint) LessThan(b Endpoint) bool {
	return a.typ < b.typ || (a.typ == b.typ && a.raw < b.raw)
}

func fnvHash(s string) (h uint64) {
	h = fnvBasis
	for i := 0; i < len(s); i++ {
		h ^= uint64(s[i])
		h *= fnvPrime
	}
	return
}

const fnvBasis = 14695981039346656037
const fnvPrime = 1099511628211

// FastHash provides a quick hashing function for an endpoint, useful if you'd
// like to split up endpoints by modulos or other load-balancing techniques.
// It uses a variant of Fowler-Noll-Vo hashing.
//
// The output of FastHash is not guaranteed to remain the same through future
// code revisions, so should not be used to key values in persistent storage.
func (a Endpoint) FastHash() (h uint64) {
	h = fnvHash(a.raw)
	h ^= uint64(a.typ)
	h *= fnvPrime
	return
}

// NewEndpoint creates a new Endpoint object.
func NewEndpoint(typ EndpointType, raw []byte) Endpoint {
	return Endpoint{typ, string(raw)}
}

// EndpointTypeMetadata is used to register a new endpoint type.
type EndpointTypeMetadata struct {
	// Name is the string returned by an EndpointType's String function.
	Name string
	// Formatter is called from an Endpoint's String function to format the raw
	// bytes in an Endpoint into a human-readable string.
	Formatter func([]byte) string
}

// EndpointType is the type of a gopacket Endpoint.  This type determines how
// the bytes stored in the endpoint should be interpreted.
type EndpointType int64

var endpointTypes = map[EndpointType]EndpointTypeMetadata{}

// RegisterEndpointType creates a new EndpointType and registers it globally.
// It MUST be passed a unique number, or it will panic.  Numbers 0-999 are
// reserved for gopacket's use.
func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType {
	t := EndpointType(num)
	if _, ok := endpointTypes[t]; ok {
		panic("Endpoint type number already in use")
	}
	endpointTypes[t] = meta
	return t
}

func (e EndpointType) String() string {
	if t, ok := endpointTypes[e]; ok {
		return t.Name
	}
	return strconv.Itoa(int(e))
}

func (e Endpoint) String() string {
	if t, ok := endpointTypes[e.typ]; ok && t.Formatter != nil {
		return t.Formatter([]byte(e.raw))
	}
	return fmt.Sprintf("%v:%v", e.typ, e.raw)
}

// Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint.
// Flows are usable as map keys.
type Flow struct {
	typ      EndpointType
	src, dst string
}

// FlowFromEndpoints creates a new flow by pasting together two endpoints.
// The endpoints must have the same EndpointType, or this function will return
// an error.
func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) {
	if src.typ != dst.typ {
		err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ)
		return
	}
	return Flow{src.typ, src.raw, dst.raw}, nil
}

// FastHash provides a quick hashing function for a flow, useful if you'd
// like to split up flows by modulos or other load-balancing techniques.
// It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide
// with its reverse flow.  IE: the flow A->B will have the same hash as the flow
// B->A.
//
// The output of FastHash is not guaranteed to remain the same through future
// code revisions, so should not be used to key values in persistent storage.
func (a Flow) FastHash() (h uint64) {
	// This combination must be commutative.  We don't use ^, since that would
	// give the same hash for all A->A flows.
	h = fnvHash(a.src) + fnvHash(a.dst)
	h ^= uint64(a.typ)
	h *= fnvPrime
	return
}

// String returns a human-readable representation of this flow, in the form
// "Src->Dst"
func (f Flow) String() string {
	s, d := f.Endpoints()
	return fmt.Sprintf("%v->%v", s, d)
}

// EndpointType returns the EndpointType for this Flow.
func (f Flow) EndpointType() EndpointType {
	return f.typ
}

// Endpoints returns the two Endpoints for this flow.
func (f Flow) Endpoints() (src, dst Endpoint) {
	return Endpoint{f.typ, f.src}, Endpoint{f.typ, f.dst}
}

// Src returns the source Endpoint for this flow.
func (f Flow) Src() (src Endpoint) {
	src, _ = f.Endpoints()
	return
}

// Dst returns the destination Endpoint for this flow.
func (f Flow) Dst() (dst Endpoint) {
	_, dst = f.Endpoints()
	return
}

// Reverse returns a new flow with endpoints reversed.
func (f Flow) Reverse() Flow {
	return Flow{f.typ, f.dst, f.src}
}

// NewFlow creates a new flow.
func NewFlow(t EndpointType, src, dst []byte) Flow {
	return Flow{t, string(src), string(dst)}
}

// EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints
// that are specified incorrectly during creation.
var EndpointInvalid EndpointType = RegisterEndpointType(0, EndpointTypeMetadata{"invalid", func(b []byte) string {
	return fmt.Sprintf("%v", b)
}})

// InvalidEndpoint is a singleton Endpoint of type EndpointInvalid.
var InvalidEndpoint Endpoint = NewEndpoint(EndpointInvalid, nil)

// InvalidFlow is a singleton Flow of type EndpointInvalid.
var InvalidFlow Flow = NewFlow(EndpointInvalid, nil, nil)
<commit_msg>Use byte array for flows instead of string.<commit_after>// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package gopacket

import (
	"bytes"
	"fmt"
	"strconv"
)

// Endpoint is the set of bytes used to address packets at various layers.
// See LinkLayer, NetworkLayer, and TransportLayer specifications.
// Endpoints are usable as map keys.
type Endpoint struct {
	typ EndpointType
	len int
	raw [maxFlowSize]byte
}

// EndpointType returns the endpoint type associated with this endpoint.
func (e Endpoint) EndpointType() EndpointType { return e.typ }

// Raw returns the raw bytes of this endpoint.  These aren't human-readable
// most of the time, but they are faster than calling String.
func (e Endpoint) Raw() []byte { return e.raw[:] }

// LessThan provides a stable ordering for all endpoints.  It sorts first based
// on the EndpointType of an endpoint, then based on the raw bytes of that endpoint.
// For some endpoints, the actual comparison may not make sense, however this
// ordering does provide useful information for most Endpoint types.
// Ordering is based first on endpoint type, then on raw endpoint bytes.
// Endpoint bytes are sorted lexigraphically.
func (a Endpoint) LessThan(b Endpoint) bool {
	return a.typ < b.typ || (a.typ == b.typ && bytes.Compare(a.raw[:a.len], b.raw[:b.len]) < 0)
}

func fnvHash(s []byte) (h uint64) {
	h = fnvBasis
	for i := 0; i < len(s); i++ {
		h ^= uint64(s[i])
		h *= fnvPrime
	}
	return
}

const fnvBasis = 14695981039346656037
const fnvPrime = 1099511628211

// FastHash provides a quick hashing function for an endpoint, useful if you'd
// like to split up endpoints by modulos or other load-balancing techniques.
// It uses a variant of Fowler-Noll-Vo hashing.
//
// The output of FastHash is not guaranteed to remain the same through future
// code revisions, so should not be used to key values in persistent storage.
func (a Endpoint) FastHash() (h uint64) {
	h = fnvHash(a.raw[:a.len])
	h ^= uint64(a.typ)
	h *= fnvPrime
	return
}

// NewEndpoint creates a new Endpoint object.
func NewEndpoint(typ EndpointType, raw []byte) (e Endpoint) {
	e.len = len(raw)
	if e.len > maxFlowSize {
		panic("endpoint too large, max endpoint size is 16")
	}
	e.typ = typ
	copy(e.raw[:], raw)
	return
}

// EndpointTypeMetadata is used to register a new endpoint type.
type EndpointTypeMetadata struct {
	// Name is the string returned by an EndpointType's String function.
	Name string
	// Formatter is called from an Endpoint's String function to format the raw
	// bytes in an Endpoint into a human-readable string.
	Formatter func([]byte) string
}

// EndpointType is the type of a gopacket Endpoint.  This type determines how
// the bytes stored in the endpoint should be interpreted.
type EndpointType int64

var endpointTypes = map[EndpointType]EndpointTypeMetadata{}

// RegisterEndpointType creates a new EndpointType and registers it globally.
// It MUST be passed a unique number, or it will panic.  Numbers 0-999 are
// reserved for gopacket's use.
func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType {
	t := EndpointType(num)
	if _, ok := endpointTypes[t]; ok {
		panic("Endpoint type number already in use")
	}
	endpointTypes[t] = meta
	return t
}

func (e EndpointType) String() string {
	if t, ok := endpointTypes[e]; ok {
		return t.Name
	}
	return strconv.Itoa(int(e))
}

func (e Endpoint) String() string {
	if t, ok := endpointTypes[e.typ]; ok && t.Formatter != nil {
		return t.Formatter(e.raw[:e.len])
	}
	return fmt.Sprintf("%v:%v", e.typ, e.raw)
}

const maxFlowSize = 16

// Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint.
// Flows are usable as map keys.
type Flow struct {
	typ        EndpointType
	slen, dlen int
	src, dst   [maxFlowSize]byte
}

// FlowFromEndpoints creates a new flow by pasting together two endpoints.
// The endpoints must have the same EndpointType, or this function will return
// an error.
func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) {
	if src.typ != dst.typ {
		err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ)
		return
	}
	return Flow{src.typ, src.len, dst.len, src.raw, dst.raw}, nil
}

// FastHash provides a quick hashing function for a flow, useful if you'd
// like to split up flows by modulos or other load-balancing techniques.
// It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide
// with its reverse flow.  IE: the flow A->B will have the same hash as the flow
// B->A.
//
// The output of FastHash is not guaranteed to remain the same through future
// code revisions, so should not be used to key values in persistent storage.
func (a Flow) FastHash() (h uint64) {
	// This combination must be commutative.  We don't use ^, since that would
	// give the same hash for all A->A flows.
	h = fnvHash(a.src[:a.slen]) + fnvHash(a.dst[:a.dlen])
	h ^= uint64(a.typ)
	h *= fnvPrime
	return
}

// String returns a human-readable representation of this flow, in the form
// "Src->Dst"
func (f Flow) String() string {
	s, d := f.Endpoints()
	return fmt.Sprintf("%v->%v", s, d)
}

// EndpointType returns the EndpointType for this Flow.
func (f Flow) EndpointType() EndpointType {
	return f.typ
}

// Endpoints returns the two Endpoints for this flow.
func (f Flow) Endpoints() (src, dst Endpoint) {
	return Endpoint{f.typ, f.slen, f.src}, Endpoint{f.typ, f.dlen, f.dst}
}

// Src returns the source Endpoint for this flow.
func (f Flow) Src() (src Endpoint) {
	src, _ = f.Endpoints()
	return
}

// Dst returns the destination Endpoint for this flow.
func (f Flow) Dst() (dst Endpoint) {
	_, dst = f.Endpoints()
	return
}

// Reverse returns a new flow with endpoints reversed.
func (f Flow) Reverse() Flow {
	return Flow{f.typ, f.dlen, f.slen, f.dst, f.src}
}

// NewFlow creates a new flow.
func NewFlow(t EndpointType, src, dst []byte) (f Flow) {
	f.slen = len(src)
	f.dlen = len(dst)
	if f.slen > maxFlowSize || f.dlen > maxFlowSize {
		panic("flow endpoint too large")
	}
	f.typ = t
	copy(f.src[:], src)
	copy(f.dst[:], dst)
	return
}

// EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints
// that are specified incorrectly during creation.
var EndpointInvalid EndpointType = RegisterEndpointType(0, EndpointTypeMetadata{"invalid", func(b []byte) string {
	return fmt.Sprintf("%v", b)
}})

// InvalidEndpoint is a singleton Endpoint of type EndpointInvalid.
var InvalidEndpoint Endpoint = NewEndpoint(EndpointInvalid, nil)

// InvalidFlow is a singleton Flow of type EndpointInvalid.
var InvalidFlow Flow = NewFlow(EndpointInvalid, nil, nil)
<|endoftext|>


<commit_before>//go:build !windows && !js && !appengine
// +build !windows,!js,!appengine

package runewidth

import (
	"os"
	"testing"
)

type envVars struct {
	lang     string
	lc_all   string
	lc_ctype string
}

func saveEnv() envVars {
	return envVars{
		lang:     os.Getenv("LANG"),
		lc_all:   os.Getenv("LC_ALL"),
		lc_ctype: os.Getenv("LC_CTYPE"),
	}
}
func restoreEnv(env *envVars) {
	os.Setenv("LANG", env.lang)
	os.Setenv("LC_ALL", env.lc_all)
	os.Setenv("LC_CTYPE", env.lc_ctype)
}

func TestIsEastAsian(t *testing.T) {
	testcases := []struct {
		locale string
		want   bool
	}{
		{"foo@cjk_narrow", false},
		{"foo@cjk", false},
		{"utf-8@cjk", false},
		{"ja_JP.CP932", true},
	}

	for _, tt := range testcases {
		got := isEastAsian(tt.locale)
		if got != tt.want {
			t.Fatalf("isEastAsian(%q) should be %v", tt.locale, tt.want)
		}
	}
}

func TestIsEastAsianLCCTYPE(t *testing.T) {
	env := saveEnv()
	defer restoreEnv(&env)
	os.Setenv("LC_ALL", "")

	testcases := []struct {
		lcctype string
		want    bool
	}{
		{"ja_JP.UTF-8", true},
		{"C", false},
		{"POSIX", false},
		{"en_US.UTF-8", false},
	}

	for _, tt := range testcases {
		os.Setenv("LC_CTYPE", tt.lcctype)
		got := IsEastAsian()
		if got != tt.want {
			t.Fatalf("IsEastAsian() for LC_CTYPE=%v should be %v", tt.lcctype, tt.want)
		}
	}
}

func TestIsEastAsianLANG(t *testing.T) {
	env := saveEnv()
	defer restoreEnv(&env)
	os.Setenv("LC_ALL", "")
	os.Setenv("LC_CTYPE", "")

	testcases := []struct {
		lcctype string
		want    bool
	}{
		{"ja_JP.UTF-8", true},
		{"C", false},
		{"POSIX", false},
		{"en_US.UTF-8", false},
		{"C.UTF-8", false},
	}

	for _, tt := range testcases {
		os.Setenv("LANG", tt.lcctype)
		got := IsEastAsian()
		if got != tt.want {
			t.Fatalf("IsEastAsian() for LANG=%v should be %v", tt.lcctype, tt.want)
		}
	}
}
<commit_msg>Fix underscore names in tests<commit_after>//go:build !windows && !js && !appengine
// +build !windows,!js,!appengine

package runewidth

import (
	"os"
	"testing"
)

type envVars struct {
	lang    string
	lcall   string
	lcctype string
}

func saveEnv() envVars {
	return envVars{
		lang:    os.Getenv("LANG"),
		lcall:   os.Getenv("LC_ALL"),
		lcctype: os.Getenv("LC_CTYPE"),
	}
}
func restoreEnv(env *envVars) {
	os.Setenv("LANG", env.lang)
	os.Setenv("LC_ALL", env.lcall)
	os.Setenv("LC_CTYPE", env.lcctype)
}

func TestIsEastAsian(t *testing.T) {
	testcases := []struct {
		locale string
		want   bool
	}{
		{"foo@cjk_narrow", false},
		{"foo@cjk", false},
		{"utf-8@cjk", false},
		{"ja_JP.CP932", true},
	}

	for _, tt := range testcases {
		got := isEastAsian(tt.locale)
		if got != tt.want {
			t.Fatalf("isEastAsian(%q) should be %v", tt.locale, tt.want)
		}
	}
}

func TestIsEastAsianLCCTYPE(t *testing.T) {
	env := saveEnv()
	defer restoreEnv(&env)
	os.Setenv("LC_ALL", "")

	testcases := []struct {
		lcctype string
		want    bool
	}{
		{"ja_JP.UTF-8", true},
		{"C", false},
		{"POSIX", false},
		{"en_US.UTF-8", false},
	}

	for _, tt := range testcases {
		os.Setenv("LC_CTYPE", tt.lcctype)
		got := IsEastAsian()
		if got != tt.want {
			t.Fatalf("IsEastAsian() for LC_CTYPE=%v should be %v", tt.lcctype, tt.want)
		}
	}
}

func TestIsEastAsianLANG(t *testing.T) {
	env := saveEnv()
	defer restoreEnv(&env)
	os.Setenv("LC_ALL", "")
	os.Setenv("LC_CTYPE", "")

	testcases := []struct {
		lcctype string
		want    bool
	}{
		{"ja_JP.UTF-8", true},
		{"C", false},
		{"POSIX", false},
		{"en_US.UTF-8", false},
		{"C.UTF-8", false},
	}

	for _, tt := range testcases {
		os.Setenv("LANG", tt.lcctype)
		got := IsEastAsian()
		if got != tt.want {
			t.Fatalf("IsEastAsian() for LANG=%v should be %v", tt.lcctype, tt.want)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ingore

//
// 给特殊模式标定的单词增加链接.
//
// Example:
//	addlinks
//	addlinks dir "\.go$"
//
// Help:
//	addlinks -h
//
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"unicode/utf8"
)

const MaxFileSize = 8 << 20 // 8MB

const usage = `
Usage: addlinks dir [nameFilter]
       addlinks -h

Example:
  addlinks
  addlinks dir "\.go$"

Report bugs to <chaishushan{AT}gmail.com>.
`

func main() {
	if len(os.Args) < 2 || os.Args[1] == "-h" {
		fmt.Fprintln(os.Stderr, usage[1:len(usage)-1])
		os.Exit(0)
	}
	dir, nameFilter := os.Args[1], ".*"
	if len(os.Args) > 2 {
		nameFilter = os.Args[2]
	}

	total := 0
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			log.Fatal("filepath.Walk: ", err)
			return err
		}
		if info.IsDir() {
			return nil
		}
		relpath, err := filepath.Rel(dir, path)
		if err != nil {
			log.Fatal("filepath.Rel: ", err)
			return err
		}
		mathed, err := regexp.MatchString(nameFilter, relpath)
		if err != nil {
			log.Fatal("regexp.MatchString: ", err)
		}
		if mathed {
			if changed := convertFile(path); changed {
				fmt.Printf("%s\n", relpath)
				total++
			}
		}
		return nil
	})
	fmt.Printf("total %d\n", total)
}

func convertFile(path string) (changed bool) {
	abspath, err := filepath.Abs(path)
	if err != nil {
		log.Fatal("convertFile: filepath.Abs:", err)
	}

	fi, err := os.Lstat(abspath)
	if err != nil {
		log.Fatal("convertFile: os.Lstat:", err)
	}
	if fi.Size() > MaxFileSize {
		return false
	}

	oldData, err := ioutil.ReadFile(abspath)
	if err != nil {
		log.Fatal("convertFile: ioutil.ReadFile:", err)
	}
	if !utf8.Valid(oldData) {
		return false
	}

	newData := oldData
	for k, v := range _LinksTable {
		newData = bytes.Replace(oldData, []byte(k), []byte(v), -1)
	}

	if string(newData) == string(oldData) {
		return false
	}

	err = ioutil.WriteFile(abspath, newData, 0666)
	if err != nil {
		log.Fatal("convertFile: ioutil.WriteFile:", err)
	}
	return true
}

var _LinksTable = map[string]string{
	"[Alan A. A. Donovan]()": "[Alan A. A. Donovan](https://github.com/adonovan)",
}
<commit_msg>addlinks 改用 正则表达式 替换<commit_after>// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ingore

//
// 给特殊模式标定的单词增加链接.
//
// Example:
//	addlinks
//	addlinks dir "\.go$"
//
// Help:
//	addlinks -h
//
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"unicode/utf8"
)

const MaxFileSize = 8 << 20 // 8MB

const usage = `
Usage: addlinks dir [nameFilter]
       addlinks -h

Example:
  addlinks
  addlinks dir "\.go$"

Report bugs to <chaishushan{AT}gmail.com>.
`

func main() {
	if len(os.Args) < 2 || os.Args[1] == "-h" {
		fmt.Fprintln(os.Stderr, usage[1:len(usage)-1])
		os.Exit(0)
	}
	dir, nameFilter := os.Args[1], ".*"
	if len(os.Args) > 2 {
		nameFilter = os.Args[2]
	}

	total := 0
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			log.Fatal("filepath.Walk: ", err)
			return err
		}
		if info.IsDir() {
			return nil
		}
		relpath, err := filepath.Rel(dir, path)
		if err != nil {
			log.Fatal("filepath.Rel: ", err)
			return err
		}
		mathed, err := regexp.MatchString(nameFilter, relpath)
		if err != nil {
			log.Fatal("regexp.MatchString: ", err)
		}
		if mathed {
			if changed := convertFile(path); changed {
				fmt.Printf("%s\n", relpath)
				total++
			}
		}
		return nil
	})
	fmt.Printf("total %d\n", total)
}

func convertFile(path string) (changed bool) {
	abspath, err := filepath.Abs(path)
	if err != nil {
		log.Fatal("convertFile: filepath.Abs:", err)
	}

	fi, err := os.Lstat(abspath)
	if err != nil {
		log.Fatal("convertFile: os.Lstat:", err)
	}
	if fi.Size() > MaxFileSize {
		return false
	}

	oldData, err := ioutil.ReadFile(abspath)
	if err != nil {
		log.Fatal("convertFile: ioutil.ReadFile:", err)
	}
	if !utf8.Valid(oldData) {
		return false
	}

	newData := append([]byte{}, oldData...)
	for re, v := range _RegexpLinksTable {
		newData = re.ReplaceAll(newData, []byte(v))
	}

	if string(newData) == string(oldData) {
		return false
	}

	err = ioutil.WriteFile(abspath, newData, 0666)
	if err != nil {
		log.Fatal("convertFile: ioutil.WriteFile:", err)
	}
	return true
}

var _RegexpLinksTable = func() map[*regexp.Regexp]string {
	m := make(map[*regexp.Regexp]string)
	for k, v := range _LinkTable {
		reKey := regexp.MustCompile(regexp.QuoteMeta(`[`+k+`]`) + `\(\S*\)`)
		m[reKey] = fmt.Sprintf("[%s](%s)", k, v)
	}
	return m
}()

var _LinkTable = map[string]string{
	"Alan A. A. Donovan": "https://github.com/adonovan",
}
<|endoftext|>