<commit_before>package filters

import (
	"encoding/json"
	"fmt"
	"testing"

	"github.com/abesto/easyssh/target"
	"github.com/abesto/easyssh/util"
	"github.com/maraino/go-mock"
)

type dummyEc2InstanceIdParser struct {
	shouldMatch bool
}

func (p dummyEc2InstanceIdParser) Parse(input string) string {
	if p.shouldMatch {
		return input + ".instanceid"
	}
	return ""
}

func TestEc2InstanceIdLookupStringViaMake(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		input := "(ec2-instance-id test-region)"
		structs := "[ec2-instance-id test-region]"
		final := "<ec2-instance-id test-region>"
		l.ExpectDebugf("MakeFromString %s -> %s", input, structs)
		l.ExpectDebugf("Make %s -> %s", structs, final)
		Make(input)
	})
}

func TestEc2InstanceIdLookupMakeWithoutArgument(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		l.ExpectDebugf("MakeFromString %s -> %s", "(ec2-instance-id)", "[ec2-instance-id]")
		util.ExpectPanic(t, "<ec2-instance-id > requires exactly 1 argument(s), got 0: []",
			func() { Make("(ec2-instance-id)") })
	})
}

func TestEc2InstanceIdLookupFilterWithoutSetArgs(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		util.ExpectPanic(t, "<ec2-instance-id > requires exactly 1 argument(s), got 0: []",
			func() { (&ec2InstanceIdLookup{}).Filter([]target.Target{}) })
	})
}

func TestEc2InstanceIdSetTooManyArgs(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		l.ExpectDebugf("MakeFromString %s -> %s", "(ec2-instance-id foo bar)", "[ec2-instance-id foo bar]")
		util.ExpectPanic(t, "<ec2-instance-id > requires exactly 1 argument(s), got 2: [foo bar]",
			func() { Make("(ec2-instance-id foo bar)") })
	})
}

func TestEc2InstanceIdSetArgs(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		l.ExpectDebugf("MakeFromString %s -> %s", "(ec2-instance-id foo)", "[ec2-instance-id foo]").Times(1)
		l.ExpectDebugf("Make %s -> %s", "[ec2-instance-id foo]", "<ec2-instance-id foo>").Times(1)
		f := Make("(ec2-instance-id foo)").(*ec2InstanceIdLookup)
		if f.region != "foo" {
			t.Errorf("Expected region to be foo, was %s", f.region)
		}
		if len(f.args) != 1 || fmt.Sprintf("%s", f.args[0]) != "foo" {
			t.Error(len(f.args), f.args)
		}
	})
}

func TestEc2InstanceIdParser(t *testing.T) {
	cases := map[string]string{
		"foo-i-deadbeef.subnet.private": "i-deadbeef",
		"i-foo":      "",
		"abesto.net": "",
	}
	parser := realEc2InstanceIdParser{}
	for input, expected := range cases {
		actual := parser.Parse(input)
		if actual != expected {
			t.Errorf("Parsed '%s' into EC2 instance id '%s', expected '%s'", input, actual, expected)
		}
	}
}

func givenAnEc2InstanceIdLookupWithMockedParserAndRunner(shouldMatch bool) (*util.MockCommandRunner, *ec2InstanceIdLookup) {
	r := &util.MockCommandRunner{}
	idParser := dummyEc2InstanceIdParser{shouldMatch}
	f := &ec2InstanceIdLookup{idParser: idParser, commandRunner: r, region: "dummy-region", args: []interface{}{"dummy-region"}}
	return r, f
}

func awsReturns(r *util.MockCommandRunner, instanceId string, region string, output string, err error) *mock.MockFunction {
	return r.When("RunGetOutput", "aws", []string{"ec2", "describe-instances", "--instance-id", instanceId, "--region", region}).Return([]byte(output), err)
}

func assertFilterResults(t *testing.T, f *ec2InstanceIdLookup, input []target.Target, expectedOutput []target.Target) {
	actualOutput := f.Filter(input)
	if len(input) != len(actualOutput) {
		t.Fail()
	}
	for i := 0; i < len(input); i++ {
		if expectedOutput[0] != actualOutput[0] {
			t.Errorf("Target %d was expected to be %s, found %s", i, expectedOutput[0], actualOutput[0])
		}
	}
}

func TestEc2InstanceIdLookupFails(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		r, f := givenAnEc2InstanceIdLookupWithMockedParserAndRunner(true)
		msg := "A client error (InvalidInstanceID.NotFound) occurred when calling the DescribeInstances operation: The instance ID 'i-deadbeef' does not exist"
		host := "dummy-instance-id"
		instanceId := host + ".instanceid"
		l.ExpectInfof("EC2 Instance lookup: %s in %s", instanceId, f.region).Times(2)
		l.ExpectDebugf("Response from AWS API: %s", msg).Times(2)
		l.ExpectInfof("EC2 Instance lookup failed for %s (%s) in region %s (aws command failed): %s", host, instanceId, f.region, msg).Times(2)
		// When the aws cli tool fails
		awsReturns(r, instanceId, f.region, msg, util.DummyError{"test fails aws"}).Times(2)
		// Filtering doesn't touch the target list
		targets := target.GivenTargets(host, host)
		assertFilterResults(t, f, targets, targets)
		util.VerifyMocks(t, r)
		// And no panic happened on JSON parsing, even though the CLI tools output was not valid JSON, because we don't even try to parse the output.
	})
}

func TestEc2InstanceIdLookupInvalidJson(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		invalidJson := "HAH! not a valid JSON"
		host := "dummy-instance-id"
		instanceId := host + ".instanceid"
		r, f := givenAnEc2InstanceIdLookupWithMockedParserAndRunner(true)
		l.ExpectDebugf("Response from AWS API: %s", invalidJson)
		l.ExpectInfof("EC2 Instance lookup: %s in %s", instanceId, f.region)
		// When the AWS API returns invalid JSON
		awsReturns(r, instanceId, f.region, invalidJson, nil).Times(1)
		// I get a fatal error for filtering
		util.ExpectPanic(t, fmt.Sprintf("Invalid JSON returned by AWS API.\nError: invalid character 'H' looking for beginning of value\nJSON follows this line\n%s", invalidJson),
			func() { f.Filter([]target.Target{target.FromString(host)}) })
		util.VerifyMocks(t, r)
	})
}

func jsonWithoutReservations() string {
	bytes, _ := json.Marshal(ec2DescribeInstanceApiResponse{Reservations: []ec2Reservation{}})
	return string(bytes)
}

func jsonWithIp(ip string) string {
	bytes, _ := json.Marshal(ec2DescribeInstanceApiResponse{Reservations: []ec2Reservation{ec2Reservation{Instances: []ec2Instance{ec2Instance{PublicIpAddress: ip}}}}})
	return string(bytes)
}

type lookupCase struct {
	inputHost  string
	instanceId string
	publicIp   string
	json       string
}

func makeLookupCase(inputHost string, publicIp string) lookupCase {
	c := lookupCase{inputHost: inputHost, instanceId: inputHost + ".instanceid"}
	if publicIp == "" {
		c.publicIp = inputHost
		c.json = jsonWithoutReservations()
	} else {
		c.publicIp = publicIp
		c.json = jsonWithIp(publicIp)
	}
	return c
}

func makeInputAndOutputTargets(cases []lookupCase, shouldRewrite bool) ([]target.Target, []target.Target) {
	inputTargets := make([]target.Target, len(cases))
	outputTargets := make([]target.Target, len(cases))
	for i, c := range cases {
		inputTargets[i] = target.FromString(c.inputHost)
		if shouldRewrite {
			outputTargets[i] = target.FromString(c.publicIp)
		} else {
			outputTargets[i] = target.FromString(c.inputHost)
		}
	}
	return inputTargets, outputTargets
}

func assertLookupCasesPass(t *testing.T, r *util.MockCommandRunner, f *ec2InstanceIdLookup, shouldRewrite bool, cases []lookupCase) {
	for _, c := range cases {
		awsReturns(r, c.instanceId, f.region, c.json, nil).Times(1)
	}
	inputTargets, expectedOutputTargets := makeInputAndOutputTargets(cases, shouldRewrite)
	assertFilterResults(t, f, inputTargets, expectedOutputTargets)
}

func TestEc2InstanceIdLookupDoesntLookLikeInstanceId(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		cases := []lookupCase{
			makeLookupCase("no-hits", ""),
			makeLookupCase("foo.i-deadbeef.bar", "1.1.1.1"),
			makeLookupCase("i-12345678", "2.2.2.2"),
		}
		r, f := givenAnEc2InstanceIdLookupWithMockedParserAndRunner(false)
		for _, c := range cases {
			l.ExpectDebugf("Target %s looks like it doesn't have EC2 instance ID, skipping lookup for region %s", c.inputHost, f.region)
		}
		assertLookupCasesPass(t, r, f, false, cases)

	})
}

func TestEc2InstanceIdLookupHappyPath(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		r, f := givenAnEc2InstanceIdLookupWithMockedParserAndRunner(true)
		cases := []lookupCase{
			makeLookupCase("no-hits", ""),
			makeLookupCase("foo.i-deadbeef.bar", "1.1.1.1"),
			makeLookupCase("i-12345678", "2.2.2.2"),
		}

		for _, c := range cases {
			l.ExpectInfof("EC2 Instance lookup: %s in %s", c.instanceId, f.region)
			l.ExpectDebugf("Response from AWS API: %s", c.json)
			if c.json == jsonWithoutReservations() {
				l.ExpectInfof("EC2 instance lookup failed for %s (%s) in region %s (Reservations is empty in the received JSON)", c.inputHost, c.instanceId, f.region)
			} else {
				l.ExpectInfof("AWS API returned PublicIpAddress %s for %s (%s)", c.publicIp, c.inputHost, c.instanceId)
			}
		}

		assertLookupCasesPass(t, r, f, true, cases)
		util.VerifyMocks(t, r)
	})
}
<commit_msg>Small fix in test code<commit_after>package filters

import (
	"encoding/json"
	"fmt"
	"testing"

	"github.com/abesto/easyssh/target"
	"github.com/abesto/easyssh/util"
	"github.com/maraino/go-mock"
)

type dummyEc2InstanceIdParser struct {
	shouldMatch bool
}

func (p dummyEc2InstanceIdParser) Parse(input string) string {
	if p.shouldMatch {
		return input + ".instanceid"
	}
	return ""
}

func TestEc2InstanceIdLookupStringViaMake(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		input := "(ec2-instance-id test-region)"
		structs := "[ec2-instance-id test-region]"
		final := "<ec2-instance-id test-region>"
		l.ExpectDebugf("MakeFromString %s -> %s", input, structs)
		l.ExpectDebugf("Make %s -> %s", structs, final)
		Make(input)
	})
}

func TestEc2InstanceIdLookupMakeWithoutArgument(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		l.ExpectDebugf("MakeFromString %s -> %s", "(ec2-instance-id)", "[ec2-instance-id]")
		util.ExpectPanic(t, "<ec2-instance-id > requires exactly 1 argument(s), got 0: []",
			func() { Make("(ec2-instance-id)") })
	})
}

func TestEc2InstanceIdLookupFilterWithoutSetArgs(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		util.ExpectPanic(t, "<ec2-instance-id > requires exactly 1 argument(s), got 0: []",
			func() { (&ec2InstanceIdLookup{}).Filter([]target.Target{}) })
	})
}

func TestEc2InstanceIdSetTooManyArgs(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		l.ExpectDebugf("MakeFromString %s -> %s", "(ec2-instance-id foo bar)", "[ec2-instance-id foo bar]")
		util.ExpectPanic(t, "<ec2-instance-id > requires exactly 1 argument(s), got 2: [foo bar]",
			func() { Make("(ec2-instance-id foo bar)") })
	})
}

func TestEc2InstanceIdSetArgs(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		l.ExpectDebugf("MakeFromString %s -> %s", "(ec2-instance-id foo)", "[ec2-instance-id foo]").Times(1)
		l.ExpectDebugf("Make %s -> %s", "[ec2-instance-id foo]", "<ec2-instance-id foo>").Times(1)
		f := Make("(ec2-instance-id foo)").(*ec2InstanceIdLookup)
		if f.region != "foo" {
			t.Errorf("Expected region to be foo, was %s", f.region)
		}
		if len(f.args) != 1 || fmt.Sprintf("%s", f.args[0]) != "foo" {
			t.Error(len(f.args), f.args)
		}
	})
}

func TestEc2InstanceIdParser(t *testing.T) {
	cases := map[string]string{
		"foo-i-deadbeef.subnet.private": "i-deadbeef",
		"i-foo":      "",
		"abesto.net": "",
	}
	parser := realEc2InstanceIdParser{}
	for input, expected := range cases {
		actual := parser.Parse(input)
		if actual != expected {
			t.Errorf("Parsed '%s' into EC2 instance id '%s', expected '%s'", input, actual, expected)
		}
	}
}

func givenAnEc2InstanceIdLookupWithMockedParserAndRunner(shouldMatch bool) (*util.MockCommandRunner, *ec2InstanceIdLookup) {
	r := &util.MockCommandRunner{}
	idParser := dummyEc2InstanceIdParser{shouldMatch}
	f := &ec2InstanceIdLookup{idParser: idParser, commandRunner: r, region: "dummy-region", args: []interface{}{"dummy-region"}}
	return r, f
}

func awsReturns(r *util.MockCommandRunner, instanceId string, region string, output string, err error) *mock.MockFunction {
	return r.When("RunGetOutput", "aws", []string{"ec2", "describe-instances", "--instance-id", instanceId, "--region", region}).Return([]byte(output), err)
}

func assertFilterResults(t *testing.T, f *ec2InstanceIdLookup, input []target.Target, expectedOutput []target.Target) {
	actualOutput := f.Filter(input)
	if len(input) != len(actualOutput) {
		t.Fail()
	}
	for i := 0; i < len(input); i++ {
		if expectedOutput[0] != actualOutput[0] {
			t.Errorf("Target %d was expected to be %s, found %s", i, expectedOutput[0], actualOutput[0])
		}
	}
}

func TestEc2InstanceIdLookupFails(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		r, f := givenAnEc2InstanceIdLookupWithMockedParserAndRunner(true)
		msg := "A client error (InvalidInstanceID.NotFound) occurred when calling the DescribeInstances operation: The instance ID 'i-deadbeef' does not exist"
		host := "dummy-instance-id"
		instanceId := host + ".instanceid"
		l.ExpectInfof("EC2 Instance lookup: %s in %s", instanceId, f.region).Times(2)
		l.ExpectDebugf("Response from AWS API: %s", msg).Times(2)
		l.ExpectInfof("EC2 Instance lookup failed for %s (%s) in region %s (aws command failed): %s", host, instanceId, f.region, msg).Times(2)
		// When the aws cli tool fails
		awsReturns(r, instanceId, f.region, msg, util.DummyError{Msg: "test fails aws"}).Times(2)
		// Filtering doesn't touch the target list
		targets := target.GivenTargets(host, host)
		assertFilterResults(t, f, targets, targets)
		util.VerifyMocks(t, r)
		// And no panic happened on JSON parsing, even though the CLI tools output was not valid JSON, because we don't even try to parse the output.
	})
}

func TestEc2InstanceIdLookupInvalidJson(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		invalidJson := "HAH! not a valid JSON"
		host := "dummy-instance-id"
		instanceId := host + ".instanceid"
		r, f := givenAnEc2InstanceIdLookupWithMockedParserAndRunner(true)
		l.ExpectDebugf("Response from AWS API: %s", invalidJson)
		l.ExpectInfof("EC2 Instance lookup: %s in %s", instanceId, f.region)
		// When the AWS API returns invalid JSON
		awsReturns(r, instanceId, f.region, invalidJson, nil).Times(1)
		// I get a fatal error for filtering
		util.ExpectPanic(t, fmt.Sprintf("Invalid JSON returned by AWS API.\nError: invalid character 'H' looking for beginning of value\nJSON follows this line\n%s", invalidJson),
			func() { f.Filter([]target.Target{target.FromString(host)}) })
		util.VerifyMocks(t, r)
	})
}

func jsonWithoutReservations() string {
	bytes, _ := json.Marshal(ec2DescribeInstanceApiResponse{Reservations: []ec2Reservation{}})
	return string(bytes)
}

func jsonWithIp(ip string) string {
	bytes, _ := json.Marshal(ec2DescribeInstanceApiResponse{Reservations: []ec2Reservation{ec2Reservation{Instances: []ec2Instance{ec2Instance{PublicIpAddress: ip}}}}})
	return string(bytes)
}

type lookupCase struct {
	inputHost  string
	instanceId string
	publicIp   string
	json       string
}

func makeLookupCase(inputHost string, publicIp string) lookupCase {
	c := lookupCase{inputHost: inputHost, instanceId: inputHost + ".instanceid"}
	if publicIp == "" {
		c.publicIp = inputHost
		c.json = jsonWithoutReservations()
	} else {
		c.publicIp = publicIp
		c.json = jsonWithIp(publicIp)
	}
	return c
}

func makeInputAndOutputTargets(cases []lookupCase, shouldRewrite bool) ([]target.Target, []target.Target) {
	inputTargets := make([]target.Target, len(cases))
	outputTargets := make([]target.Target, len(cases))
	for i, c := range cases {
		inputTargets[i] = target.FromString(c.inputHost)
		if shouldRewrite {
			outputTargets[i] = target.FromString(c.publicIp)
		} else {
			outputTargets[i] = target.FromString(c.inputHost)
		}
	}
	return inputTargets, outputTargets
}

func assertLookupCasesPass(t *testing.T, r *util.MockCommandRunner, f *ec2InstanceIdLookup, shouldRewrite bool, cases []lookupCase) {
	for _, c := range cases {
		awsReturns(r, c.instanceId, f.region, c.json, nil).Times(1)
	}
	inputTargets, expectedOutputTargets := makeInputAndOutputTargets(cases, shouldRewrite)
	assertFilterResults(t, f, inputTargets, expectedOutputTargets)
}

func TestEc2InstanceIdLookupDoesntLookLikeInstanceId(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		cases := []lookupCase{
			makeLookupCase("no-hits", ""),
			makeLookupCase("foo.i-deadbeef.bar", "1.1.1.1"),
			makeLookupCase("i-12345678", "2.2.2.2"),
		}
		r, f := givenAnEc2InstanceIdLookupWithMockedParserAndRunner(false)
		for _, c := range cases {
			l.ExpectDebugf("Target %s looks like it doesn't have EC2 instance ID, skipping lookup for region %s", c.inputHost, f.region)
		}
		assertLookupCasesPass(t, r, f, false, cases)

	})
}

func TestEc2InstanceIdLookupHappyPath(t *testing.T) {
	util.WithLogAssertions(t, func(l *util.MockLogger) {
		r, f := givenAnEc2InstanceIdLookupWithMockedParserAndRunner(true)
		cases := []lookupCase{
			makeLookupCase("no-hits", ""),
			makeLookupCase("foo.i-deadbeef.bar", "1.1.1.1"),
			makeLookupCase("i-12345678", "2.2.2.2"),
		}

		for _, c := range cases {
			l.ExpectInfof("EC2 Instance lookup: %s in %s", c.instanceId, f.region)
			l.ExpectDebugf("Response from AWS API: %s", c.json)
			if c.json == jsonWithoutReservations() {
				l.ExpectInfof("EC2 instance lookup failed for %s (%s) in region %s (Reservations is empty in the received JSON)", c.inputHost, c.instanceId, f.region)
			} else {
				l.ExpectInfof("AWS API returned PublicIpAddress %s for %s (%s)", c.publicIp, c.inputHost, c.instanceId)
			}
		}

		assertLookupCasesPass(t, r, f, true, cases)
		util.VerifyMocks(t, r)
	})
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type UnixtimeMacro struct {
	duration time.Duration
	format   string
}

var PksInputs map[string]string = make(map[string]string)
var Md5Inputs map[string]string = make(map[string]string)
var Base64Inputs map[string]string = make(map[string]string)
var UnixtimeMacros map[string]UnixtimeMacro
var PrintTimeMacros map[string]UnixtimeMacro
var CommandMacros = make(map[string][]string)
var Md5Macros = make(map[string][]string)
var Base64Macros = make(map[string][]string)
var PksMacros = make(map[string][]string)

var reArgs = regexp.MustCompile("{%ARGS\\[(\\d+)\\]}")

func arrayContains(arr []string, str string) bool {
	i := 0
	for i < len(arr) {
		if str == arr[i] {
			break
		}
		i += 1
	}
	return i < len(arr)

}

func addCommandMacro(cmd string, macro string) {
	if !arrayContains(CommandMacros[cmd], macro) {
		CommandMacros[cmd] = append(CommandMacros[cmd], macro)
	}
}

func InitSessionLogMacros(sessionLog string) {
	// this will create an entry for a command named "\nSessionLog",
	// the newline will prevent any command named "SessionLog" in an ini file from overwriting it
	InitMacros("\nSessionLog", sessionLog)
}

func InitMacros(cmd string, field string) {
	_, exists := CommandMacros[cmd]
	if !exists {
		CommandMacros[cmd] = make([]string, 0)
	}

	rx, _ := regexp.Compile("\\{%.*?\\}")
	rxenv, _ := regexp.Compile("\\{\\$.*?\\}")

	for _, macro := range rx.FindAllString(field, -1) {
		addCommandMacro(cmd, macro)
	}
	for _, macro := range rxenv.FindAllString(field, -1) {
		addCommandMacro(cmd, macro)
	}
}

func parseTimeModifier(arg string) (time.Duration, error) {
	if len(arg) == 0 {
		return time.Duration(0), nil
	}

	rx := regexp.MustCompile("([\\+\\-]\\d+)(.*)")
	parsed := rx.FindStringSubmatch(arg)
	if parsed == nil {
		return time.Duration(0), errors.New(fmt.Sprintf("time modifier %s is not supported", arg))
	} else {
		mult, _ := strconv.Atoi(parsed[1]) // e.g -4, +4, 4
		if parsed[2] == "MONTH" || parsed[2] == "MONTHS" {
			return time.Duration(mult*30*24) * time.Hour, nil
		} else if parsed[2] == "DAY" || parsed[2] == "DAYS" {
			return time.Duration(mult) * 24 * time.Hour, nil
		} else if parsed[2] == "HOUR" || parsed[2] == "HOURS" {
			return time.Duration(mult) * time.Hour, nil
		} else if parsed[2] == "MINUTE" || parsed[2] == "MINUTES" {
			return time.Duration(mult) * time.Minute, nil
		} else if parsed[2] == "SECOND" || parsed[2] == "SECONDS" {
			return time.Duration(mult) * time.Second, nil
		} else {
			return time.Duration(0), errors.New(fmt.Sprintf("time modifier %s is not supported", arg))
		}
	}
}

func initUnixtimeMacro(macro string) {
	declaration := macro[2 : len(macro)-1]
	if strings.HasPrefix(declaration, "UNIXTIME") {
		arg := declaration[8:]
		rx1, _ := regexp.Compile("%(\\d+)?x")
		fmtmatch := rx1.FindString(arg)
		format := "%d"
		if len(fmtmatch) > 0 {
			format = fmtmatch
			arg = strings.Replace(arg, fmtmatch, "", -1)
		}
		duration, err := parseTimeModifier(arg)
		if err != nil {
			log.Fatalf("UNIXTIME macro %s: %s", declaration, err.Error())
		} else {
			UnixtimeMacros[macro] = UnixtimeMacro{duration, format}
		}
	}
}

func initPrintTimeMacro(macro string) {
	declaration := macro[2 : len(macro)-1]
	if strings.HasPrefix(declaration, "TIME") {
		format := "2006-01-02 15:04:05"
		arg := declaration[4:]
		duration, err := parseTimeModifier(arg)
		if err != nil {
			log.Fatalf("TIME macro %s: %s", declaration, err.Error())
		} else {
			PrintTimeMacros[macro] = UnixtimeMacro{duration, format}
		}
	}
}

func InitUnixtimeMacros() {
	PrintTimeMacros = make(map[string]UnixtimeMacro)
	UnixtimeMacros = make(map[string]UnixtimeMacro)
	for _, macros := range CommandMacros {
		for _, macro := range macros {
			initUnixtimeMacro(macro)
			initPrintTimeMacro(macro)
		}
	}
	for _, macros := range Base64Macros {
		for _, macro := range macros {
			initUnixtimeMacro(macro)
			initPrintTimeMacro(macro)
		}
	}
	for _, macros := range Md5Macros {
		for _, macro := range macros {
			initUnixtimeMacro(macro)
			initPrintTimeMacro(macro)
		}
	}
}

func addPksMacro(cmd string, macro string) {
	if !arrayContains(PksMacros[cmd], macro) {
		PksMacros[cmd] = append(PksMacros[cmd], macro)
	}
}

func InitPksMacro(cmd string, pksInput string) {
	if len(pksInput) == 0 {
		return
	}
	PksInputs[cmd] = pksInput
	PksMacros[cmd] = make([]string, 0)

	rx, _ := regexp.Compile("\\{%.*?\\}")
	rxenv, _ := regexp.Compile("\\{\\$.*?\\}")

	for _, macro := range rx.FindAllString(pksInput, -1) {
		addPksMacro(cmd, macro)
	}
	for _, macro := range rxenv.FindAllString(pksInput, -1) {
		addPksMacro(cmd, macro)
	}
} //InitPKSMacro
func addMd5Macro(cmd string, macro string) {
	if !arrayContains(Md5Macros[cmd], macro) {
		Md5Macros[cmd] = append(Md5Macros[cmd], macro)
	}
}

func InitMd5Macro(cmd string, md5Input string) {
	if len(md5Input) == 0 {
		return
	}

	Md5Inputs[cmd] = md5Input
	Md5Macros[cmd] = make([]string, 0)

	rx, _ := regexp.Compile("\\{%.*?\\}")
	rxenv, _ := regexp.Compile("\\{\\$.*?\\}")

	for _, macro := range rx.FindAllString(md5Input, -1) {
		addMd5Macro(cmd, macro)
	}
	for _, macro := range rxenv.FindAllString(md5Input, -1) {
		addMd5Macro(cmd, macro)
	}
}

func addBase64Macro(cmd string, macro string) {
	if !arrayContains(Base64Macros[cmd], macro) {
		Base64Macros[cmd] = append(Base64Macros[cmd], macro)
	}
}

func InitBase64Macro(cmd string, base64Input string) {
	if len(base64Input) == 0 {
		return
	}

	Base64Inputs[cmd] = base64Input
	Base64Macros[cmd] = make([]string, 0)

	rx, _ := regexp.Compile("\\{%.*?\\}")
	rxenv, _ := regexp.Compile("\\{\\$.*?\\}")

	for _, macro := range rx.FindAllString(base64Input, -1) {
		addBase64Macro(cmd, macro)
	}
	for _, macro := range rxenv.FindAllString(base64Input, -1) {
		addBase64Macro(cmd, macro)
	}
}

func _runnerMacro(command string, declaration string, inputData string, sessionVars map[string]string, reqTime time.Time) string {
	if !(strings.HasPrefix(declaration, "{%") || strings.HasPrefix(declaration, "{$")) || !strings.HasSuffix(declaration, "}") {
		return ""
	}
	//This func processes the token and returns the string
	uxt, ok := UnixtimeMacros[declaration]
	prt, ok1 := PrintTimeMacros[declaration]
	if ok {
		timestamp := reqTime.Add(uxt.duration).UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond)) //why not use now instead of reqTime?
		rx, _ := regexp.Compile("%(\\d+)x")
		fmtdigits := rx.FindStringSubmatch(uxt.format)
		if len(fmtdigits) == 0 {
			return fmt.Sprintf(uxt.format, timestamp)
		} else {
			fmtnum, _ := strconv.Atoi(fmtdigits[1])
			if fmtnum >= 12 {
				return fmt.Sprintf(uxt.format, timestamp)
			} else {
				tmp := fmt.Sprintf("%012x", timestamp)
				return tmp[0:fmtnum]
			}
		}
	} else if ok1 {
		return reqTime.Add(prt.duration).Format(prt.format)
	} else if declaration == "{%MD5SUM}" {
		testMd5 := Md5Inputs[command]
		for _, macro := range Md5Macros[command] {
			testMd5 = strings.Replace(testMd5, macro, runnerMacro(command, macro, inputData, sessionVars, reqTime), -1)
		}
		return strings.ToUpper(fmt.Sprintf("%x", md5.Sum([]byte(testMd5))))
	} else if declaration == "{%PKSENC}" {
		pksInput := PksInputs[command]
		for _, macro := range PksMacros[command] {
			pksInput = strings.Replace(pksInput, macro, runnerMacro(command, macro, inputData, sessionVars, reqTime), -1)
		}

		// that's dirty, waiting for proper func management
		inputs := strings.Split(pksInput, ",")
		if len(inputs) != 3 {
			return "invalid PKSInput format. Must be pwd,key,keyexp"
		}
		encryptor, err := NewPKSEncryptor(inputs[1], inputs[2], inputs[0])
		if err != nil {
			return err.Error()
		}
		res, err := encryptor.Encrypt()
		if err != nil {
			return err.Error()
		}
		return hex.EncodeToString(res)
	} else if declaration == "{%BASE64ENC}" {
		base64In := Base64Inputs[command]
		for _, macro := range Base64Macros[command] {
			base64In = strings.Replace(base64In, macro, runnerMacro(command, macro, inputData, sessionVars, reqTime), -1)
		}
		return base64.StdEncoding.EncodeToString([]byte(base64In))
	} else if strings.HasPrefix(declaration, "{$") {
		// an env var macro like {$SECRET}
		return os.Getenv(declaration[2 : len(declaration)-1])
	} else {
		// Check if it match {%ARGS[X]}
		argsIndex := reArgs.FindStringSubmatch(declaration) // regexp.MustCompile("{%ARGS\\[(\\d+)\\]}")
		if len(argsIndex) > 0 {
			i, _ := strconv.Atoi(argsIndex[1])
			arr := strings.Split(inputData, delimeter)
			if i >= len(arr) {
				// TODO : print error ?
				return ""
			}
			return arr[i]
		}
		// Check if it match a session var
		if declaration[1] == '%' {
			session_var := declaration[2 : len(declaration)-1]
			val, ok := sessionVars[session_var]
			if ok {
				return val
			}
		}
	}
	return ""
} //_runnerMacro replaces the variable reference like {%X} or {$X} with the value stored in the hash table

func runnerMacro(command string, declaration string, inputData string, sessionVars map[string]string, reqTime time.Time) string {
	if !(strings.HasPrefix(declaration, "{%") || strings.HasPrefix(declaration, "{$")) || !strings.HasSuffix(declaration, "}") {
		return ""
	} //This functions gets passed in a {%X} and {$X} variable reference, looks it up the hash table and returns the string value

	ssrx, _ := regexp.Compile("\\[(\\d+):(\\d+)\\]}")
	declSubstr := ssrx.FindStringSubmatch(declaration)
	if len(declSubstr) == 0 {
		return _runnerMacro(command, declaration, inputData, sessionVars, reqTime)
	} else {
		declaration = strings.Replace(declaration, declSubstr[0], "}", 1)
		result := _runnerMacro(command, declaration, inputData, sessionVars, reqTime)
		ss0, _ := strconv.Atoi(declSubstr[1])
		ss1, _ := strconv.Atoi(declSubstr[2])
		return result[ss0:ss1]
	}
}

func RunnerMacros(command string, inputData string, sessionVars map[string]string, reqTime time.Time, field string) string {
	for _, macro := range CommandMacros[command] {
		field = strings.Replace(field, macro, runnerMacro(command, macro, inputData, sessionVars, reqTime), -1)
	}
	return field
}

func RunnerMacrosRegexp(command string, inputData string, sessionVars map[string]string, reqTime time.Time, field string) string {
	for _, macro := range CommandMacros[command] {
		replacement := regexp.QuoteMeta(runnerMacro(command, macro, inputData, sessionVars, reqTime))
		field = strings.Replace(field, macro, replacement, -1)
	}
	return field
}

func SessionLogMacros(inputData string, sessionVars map[string]string, logTime time.Time, initial string) string {
	return RunnerMacros("\nSessionLog", inputData, sessionVars, logTime, initial)
}
<commit_msg>adding javascript macro support cleanup comments<commit_after>package main

import (
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type UnixtimeMacro struct {
	duration time.Duration
	format   string
}

var PksInputs map[string]string = make(map[string]string)
var Md5Inputs map[string]string = make(map[string]string)
var Base64Inputs map[string]string = make(map[string]string)
var UnixtimeMacros map[string]UnixtimeMacro
var PrintTimeMacros map[string]UnixtimeMacro
var CommandMacros = make(map[string][]string)
var Md5Macros = make(map[string][]string)
var Base64Macros = make(map[string][]string)
var PksMacros = make(map[string][]string)

var reArgs = regexp.MustCompile("{%ARGS\\[(\\d+)\\]}")

func arrayContains(arr []string, str string) bool {
	i := 0
	for i < len(arr) {
		if str == arr[i] {
			break
		}
		i += 1
	}
	return i < len(arr)

}

func addCommandMacro(cmd string, macro string) {
	if !arrayContains(CommandMacros[cmd], macro) {
		CommandMacros[cmd] = append(CommandMacros[cmd], macro)
	}
}

func InitSessionLogMacros(sessionLog string) {
	// this will create an entry for a command named "\nSessionLog",
	// the newline will prevent any command named "SessionLog" in an ini file from overwriting it
	InitMacros("\nSessionLog", sessionLog)
}

func InitMacros(cmd string, field string) {
	_, exists := CommandMacros[cmd]
	if !exists {
		CommandMacros[cmd] = make([]string, 0)
	}

	rx, _ := regexp.Compile("\\{%.*?\\}")
	rxenv, _ := regexp.Compile("\\{\\$.*?\\}")

	for _, macro := range rx.FindAllString(field, -1) {
		addCommandMacro(cmd, macro)
	}
	for _, macro := range rxenv.FindAllString(field, -1) {
		addCommandMacro(cmd, macro)
	}
}

func parseTimeModifier(arg string) (time.Duration, error) {
	if len(arg) == 0 {
		return time.Duration(0), nil
	}

	rx := regexp.MustCompile("([\\+\\-]\\d+)(.*)")
	parsed := rx.FindStringSubmatch(arg)
	if parsed == nil {
		return time.Duration(0), errors.New(fmt.Sprintf("time modifier %s is not supported", arg))
	} else {
		mult, _ := strconv.Atoi(parsed[1]) // e.g -4, +4, 4
		if parsed[2] == "MONTH" || parsed[2] == "MONTHS" {
			return time.Duration(mult*30*24) * time.Hour, nil
		} else if parsed[2] == "DAY" || parsed[2] == "DAYS" {
			return time.Duration(mult) * 24 * time.Hour, nil
		} else if parsed[2] == "HOUR" || parsed[2] == "HOURS" {
			return time.Duration(mult) * time.Hour, nil
		} else if parsed[2] == "MINUTE" || parsed[2] == "MINUTES" {
			return time.Duration(mult) * time.Minute, nil
		} else if parsed[2] == "SECOND" || parsed[2] == "SECONDS" {
			return time.Duration(mult) * time.Second, nil
		} else {
			return time.Duration(0), errors.New(fmt.Sprintf("time modifier %s is not supported", arg))
		}
	}
}

func initUnixtimeMacro(macro string) {
	declaration := macro[2 : len(macro)-1]
	if strings.HasPrefix(declaration, "UNIXTIME") {
		arg := declaration[8:]
		rx1, _ := regexp.Compile("%(\\d+)?x")
		fmtmatch := rx1.FindString(arg)
		format := "%d"
		if len(fmtmatch) > 0 {
			format = fmtmatch
			arg = strings.Replace(arg, fmtmatch, "", -1)
		}
		duration, err := parseTimeModifier(arg)
		if err != nil {
			log.Fatalf("UNIXTIME macro %s: %s", declaration, err.Error())
		} else {
			UnixtimeMacros[macro] = UnixtimeMacro{duration, format}
		}
	}
}

func initPrintTimeMacro(macro string) {
	declaration := macro[2 : len(macro)-1]
	if strings.HasPrefix(declaration, "TIME") {
		format := "2006-01-02 15:04:05"
		arg := declaration[4:]
		duration, err := parseTimeModifier(arg)
		if err != nil {
			log.Fatalf("TIME macro %s: %s", declaration, err.Error())
		} else {
			PrintTimeMacros[macro] = UnixtimeMacro{duration, format}
		}
	}
}

func InitUnixtimeMacros() {
	PrintTimeMacros = make(map[string]UnixtimeMacro)
	UnixtimeMacros = make(map[string]UnixtimeMacro)
	for _, macros := range CommandMacros {
		for _, macro := range macros {
			initUnixtimeMacro(macro)
			initPrintTimeMacro(macro)
		}
	}
	for _, macros := range Base64Macros {
		for _, macro := range macros {
			initUnixtimeMacro(macro)
			initPrintTimeMacro(macro)
		}
	}
	for _, macros := range Md5Macros {
		for _, macro := range macros {
			initUnixtimeMacro(macro)
			initPrintTimeMacro(macro)
		}
	}
}

func addPksMacro(cmd string, macro string) {
	if !arrayContains(PksMacros[cmd], macro) {
		PksMacros[cmd] = append(PksMacros[cmd], macro)
	}
}

func InitPksMacro(cmd string, pksInput string) {
	if len(pksInput) == 0 {
		return
	}
	PksInputs[cmd] = pksInput
	PksMacros[cmd] = make([]string, 0)

	rx, _ := regexp.Compile("\\{%.*?\\}")
	rxenv, _ := regexp.Compile("\\{\\$.*?\\}")

	for _, macro := range rx.FindAllString(pksInput, -1) {
		addPksMacro(cmd, macro)
	}
	for _, macro := range rxenv.FindAllString(pksInput, -1) {
		addPksMacro(cmd, macro)
	}
}
func addMd5Macro(cmd string, macro string) {
	if !arrayContains(Md5Macros[cmd], macro) {
		Md5Macros[cmd] = append(Md5Macros[cmd], macro)
	}
}

func InitMd5Macro(cmd string, md5Input string) {
	if len(md5Input) == 0 {
		return
	}

	Md5Inputs[cmd] = md5Input
	Md5Macros[cmd] = make([]string, 0)

	rx, _ := regexp.Compile("\\{%.*?\\}")
	rxenv, _ := regexp.Compile("\\{\\$.*?\\}")

	for _, macro := range rx.FindAllString(md5Input, -1) {
		addMd5Macro(cmd, macro)
	}
	for _, macro := range rxenv.FindAllString(md5Input, -1) {
		addMd5Macro(cmd, macro)
	}
}

func addBase64Macro(cmd string, macro string) {
	if !arrayContains(Base64Macros[cmd], macro) {
		Base64Macros[cmd] = append(Base64Macros[cmd], macro)
	}
}

func InitBase64Macro(cmd string, base64Input string) {
	if len(base64Input) == 0 {
		return
	}

	Base64Inputs[cmd] = base64Input
	Base64Macros[cmd] = make([]string, 0)

	rx, _ := regexp.Compile("\\{%.*?\\}")
	rxenv, _ := regexp.Compile("\\{\\$.*?\\}")

	for _, macro := range rx.FindAllString(base64Input, -1) {
		addBase64Macro(cmd, macro)
	}
	for _, macro := range rxenv.FindAllString(base64Input, -1) {
		addBase64Macro(cmd, macro)
	}
}

func _runnerMacro(command string, declaration string, inputData string, sessionVars map[string]string, reqTime time.Time) string {
	if !(strings.HasPrefix(declaration, "{%") || strings.HasPrefix(declaration, "{$")) || !strings.HasSuffix(declaration, "}") {
		return ""
	}
	uxt, ok := UnixtimeMacros[declaration]
	prt, ok1 := PrintTimeMacros[declaration]
	if ok {
		timestamp := reqTime.Add(uxt.duration).UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond)) //why not use now instead of reqTime?
		rx, _ := regexp.Compile("%(\\d+)x")
		fmtdigits := rx.FindStringSubmatch(uxt.format)
		if len(fmtdigits) == 0 {
			return fmt.Sprintf(uxt.format, timestamp)
		} else {
			fmtnum, _ := strconv.Atoi(fmtdigits[1])
			if fmtnum >= 12 {
				return fmt.Sprintf(uxt.format, timestamp)
			} else {
				tmp := fmt.Sprintf("%012x", timestamp)
				return tmp[0:fmtnum]
			}
		}
	} else if ok1 {
		return reqTime.Add(prt.duration).Format(prt.format)
	} else if declaration == "{%MD5SUM}" {
		testMd5 := Md5Inputs[command]
		for _, macro := range Md5Macros[command] {
			testMd5 = strings.Replace(testMd5, macro, runnerMacro(command, macro, inputData, sessionVars, reqTime), -1)
		}
		return strings.ToUpper(fmt.Sprintf("%x", md5.Sum([]byte(testMd5))))
	} else if declaration == "{%PKSENC}" {
		pksInput := PksInputs[command]
		for _, macro := range PksMacros[command] {
			pksInput = strings.Replace(pksInput, macro, runnerMacro(command, macro, inputData, sessionVars, reqTime), -1)
		}

		// that's dirty, waiting for proper func management
		inputs := strings.Split(pksInput, ",")
		if len(inputs) != 3 {
			return "invalid PKSInput format. Must be pwd,key,keyexp"
		}
		encryptor, err := NewPKSEncryptor(inputs[1], inputs[2], inputs[0])
		if err != nil {
			return err.Error()
		}
		res, err := encryptor.Encrypt()
		if err != nil {
			return err.Error()
		}
		return hex.EncodeToString(res)
	} else if declaration == "{%BASE64ENC}" {
		base64In := Base64Inputs[command]
		for _, macro := range Base64Macros[command] {
			base64In = strings.Replace(base64In, macro, runnerMacro(command, macro, inputData, sessionVars, reqTime), -1)
		}
		return base64.StdEncoding.EncodeToString([]byte(base64In))
	} else if strings.HasPrefix(declaration, "{$") {
		// an env var macro like {$SECRET}
		return os.Getenv(declaration[2 : len(declaration)-1])
	} else {
		// Check if it match {%ARGS[X]}
		argsIndex := reArgs.FindStringSubmatch(declaration) // regexp.MustCompile("{%ARGS\\[(\\d+)\\]}")
		if len(argsIndex) > 0 {
			i, _ := strconv.Atoi(argsIndex[1])
			arr := strings.Split(inputData, delimeter)
			if i >= len(arr) {
				// TODO : print error ?
				return ""
			}
			return arr[i]
		}
		// Check if it match a session var
		if declaration[1] == '%' {
			session_var := declaration[2 : len(declaration)-1]
			val, ok := sessionVars[session_var]
			if ok {
				return val
			}
		}
	}
	return ""
}

func runnerMacro(command string, declaration string, inputData string, sessionVars map[string]string, reqTime time.Time) string {
	if !(strings.HasPrefix(declaration, "{%") || strings.HasPrefix(declaration, "{$")) || !strings.HasSuffix(declaration, "}") {
		return ""
	}

	ssrx, _ := regexp.Compile("\\[(\\d+):(\\d+)\\]}")
	declSubstr := ssrx.FindStringSubmatch(declaration)
	if len(declSubstr) == 0 {
		//This function gets passed in a {%X} and {$X} variable reference (i.e. declaration), looks it up the hash table and returns the string value
		return _runnerMacro(command, declaration, inputData, sessionVars, reqTime)
	} else {
		declaration = strings.Replace(declaration, declSubstr[0], "}", 1)
		result := _runnerMacro(command, declaration, inputData, sessionVars, reqTime)
		ss0, _ := strconv.Atoi(declSubstr[1])
		ss1, _ := strconv.Atoi(declSubstr[2])
		return result[ss0:ss1]
	}
}

func RunnerMacros(command string, inputData string, sessionVars map[string]string, reqTime time.Time, field string) string {
	for _, macro := range CommandMacros[command] {
		field = strings.Replace(field, macro, runnerMacro(command, macro, inputData, sessionVars, reqTime), -1)
	}
	return field
}

func RunnerMacrosRegexp(command string, inputData string, sessionVars map[string]string, reqTime time.Time, field string) string {
	for _, macro := range CommandMacros[command] {
		replacement := regexp.QuoteMeta(runnerMacro(command, macro, inputData, sessionVars, reqTime))
		field = strings.Replace(field, macro, replacement, -1)
	}
	return field
}

func SessionLogMacros(inputData string, sessionVars map[string]string, logTime time.Time, initial string) string {
	return RunnerMacros("\nSessionLog", inputData, sessionVars, logTime, initial)
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"net/url"
)

const (
	createChatURL  = "https://qyapi.weixin.qq.com/cgi-bin/chat/create"
	getChatURL     = "https://qyapi.weixin.qq.com/cgi-bin/chat/get"
	updateChatURL  = "https://qyapi.weixin.qq.com/cgi-bin/chat/update"
	quitChatURL    = "https://qyapi.weixin.qq.com/cgi-bin/chat/quit"
	clearnotifyURL = "https://qyapi.weixin.qq.com/cgi-bin/chat/clearnotify"
	sendChatURL    = "https://qyapi.weixin.qq.com/cgi-bin/chat/send"
	setmuteChatURL = "https://qyapi.weixin.qq.com/cgi-bin/chat/setmute"
)

type Chat struct {
	Name     string   `json: "name"`
	Owner    string   `json: "owner"`
	UserList []string `json: "userlist"`
}

// CreateChat 方法用于创建微信聊天
func (a *API) CreateChat(chat Chat) error {
	token, err := a.Tokener.Token()
	if err != nil {
		return err
	}

	qs := make(url.Values)
	qs.Add("access_token", token)

	url := createChatURL + "?" + qs.Encode()
	data, err := json.Marshal(chat)
	if err != nil {
		return err
	}

	_, err = a.Client.PostJSON(url, data)
	return err
}
<commit_msg>增加chatid<commit_after>package api

import (
	"encoding/json"
	"net/url"
)

const (
	createChatURL  = "https://qyapi.weixin.qq.com/cgi-bin/chat/create"
	getChatURL     = "https://qyapi.weixin.qq.com/cgi-bin/chat/get"
	updateChatURL  = "https://qyapi.weixin.qq.com/cgi-bin/chat/update"
	quitChatURL    = "https://qyapi.weixin.qq.com/cgi-bin/chat/quit"
	clearnotifyURL = "https://qyapi.weixin.qq.com/cgi-bin/chat/clearnotify"
	sendChatURL    = "https://qyapi.weixin.qq.com/cgi-bin/chat/send"
	setmuteChatURL = "https://qyapi.weixin.qq.com/cgi-bin/chat/setmute"
)

type Chat struct {
	ChatId   string   `json: "chatid"`
	Name     string   `json: "name"`
	Owner    string   `json: "owner"`
	UserList []string `json: "userlist"`
}

// CreateChat 方法用于创建微信聊天
func (a *API) CreateChat(chat Chat) error {
	token, err := a.Tokener.Token()
	if err != nil {
		return err
	}

	qs := make(url.Values)
	qs.Add("access_token", token)

	url := createChatURL + "?" + qs.Encode()
	data, err := json.Marshal(chat)
	if err != nil {
		return err
	}

	_, err = a.Client.PostJSON(url, data)
	return err
}
<|endoftext|>


<commit_before>package main

// Import needed packages
import (
	"github.com/finspect/finspect"
	"gopkg.in/fsnotify.v1"
	"log"
)

// Example watcher from fsnotify
func ExampleNewWatcher() {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				log.Println("event:", event)
				if event.Op&fsnotify.Write == fsnotify.Write {
					log.Println("modified file:", event.Name)
				}
			case err := <-watcher.Errors:
				log.Println("error:", err)
			}
		}
	}()

	err = watcher.Add("/tmp")
	if err != nil {
		log.Fatal(err)
	}
	<-done
}

func main() {
	ExampleNewWatcher()
}
<commit_msg>Adding in BoltDB to experiment and use as watcher event log.<commit_after>package main

// Import needed packages
import (
	"encoding/json"
	"github.com/boltdb/bolt"
	"gopkg.in/fsnotify.v1"
	"log"
	"time"
)

type WatchEvent struct {
	Type fsnotify.Event
	File string
}

func ExampleNewWatcher(db *bolt.DB) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				log.Println("event:", event)
				if event.Op&fsnotify.Write == fsnotify.Write {

					watchEvent := &WatchEvent{
						Type: event,
						File: event.Name,
					}
					db.Update(func(tx *bolt.Tx) error {
						b, err := tx.CreateBucketIfNotExists([]byte("events"))
						if err != nil {
							return err
						}
						encoded, err := json.Marshal(watchEvent)
						if err != nil {
							return err
						}
						return b.Put([]byte(time.Now().Format(time.RFC3339)), encoded)
					})
					log.Println("modified file:", event.Name)
				}
			case err := <-watcher.Errors:
				log.Println("error:", err)
			}
		}
	}()

	err = watcher.Add("/tmp")
	if err != nil {
		log.Fatal(err)
	}
	<-done
}

func main() {
	db, err := bolt.Open("/tmp.db", 0644, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	ExampleNewWatcher(db)
}
<|endoftext|>


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

// Package pe implements access to PE (Microsoft Windows Portable Executable) files.
package pe

import (
	"debug/dwarf"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
	"unsafe"
)

// A File represents an open PE file.
type File struct {
	FileHeader
	OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
	Sections       []*Section
	Symbols        []*Symbol

	closer io.Closer
}

type SectionHeader struct {
	Name                 string
	VirtualSize          uint32
	VirtualAddress       uint32
	Size                 uint32
	Offset               uint32
	PointerToRelocations uint32
	PointerToLineNumbers uint32
	NumberOfRelocations  uint16
	NumberOfLineNumbers  uint16
	Characteristics      uint32
}

type Section struct {
	SectionHeader

	// Embed ReaderAt for ReadAt method.
	// Do not embed SectionReader directly
	// to avoid having Read and Seek.
	// If a client wants Read and Seek it must use
	// Open() to avoid fighting over the seek offset
	// with other clients.
	io.ReaderAt
	sr *io.SectionReader
}

type Symbol struct {
	Name          string
	Value         uint32
	SectionNumber int16
	Type          uint16
	StorageClass  uint8
}

type ImportDirectory struct {
	OriginalFirstThunk uint32
	TimeDateStamp      uint32
	ForwarderChain     uint32
	Name               uint32
	FirstThunk         uint32

	dll string
}

// Data reads and returns the contents of the PE section.
func (s *Section) Data() ([]byte, error) {
	dat := make([]byte, s.sr.Size())
	n, err := s.sr.ReadAt(dat, 0)
	if n == len(dat) {
		err = nil
	}
	return dat[0:n], err
}

// Open returns a new ReadSeeker reading the PE section.
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }

type FormatError struct {
	off int64
	msg string
	val interface{}
}

func (e *FormatError) Error() string {
	msg := e.msg
	if e.val != nil {
		msg += fmt.Sprintf(" '%v'", e.val)
	}
	msg += fmt.Sprintf(" in record at byte %#x", e.off)
	return msg
}

// Open opens the named file using os.Open and prepares it for use as a PE binary.
func Open(name string) (*File, error) {
	f, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	ff, err := NewFile(f)
	if err != nil {
		f.Close()
		return nil, err
	}
	ff.closer = f
	return ff, nil
}

// Close closes the File.
// If the File was created using NewFile directly instead of Open,
// Close has no effect.
func (f *File) Close() error {
	var err error
	if f.closer != nil {
		err = f.closer.Close()
		f.closer = nil
	}
	return err
}

// NewFile creates a new File for accessing a PE binary in an underlying reader.
func NewFile(r io.ReaderAt) (*File, error) {
	f := new(File)
	sr := io.NewSectionReader(r, 0, 1<<63-1)

	var dosheader [96]byte
	if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
		return nil, err
	}
	var base int64
	if dosheader[0] == 'M' && dosheader[1] == 'Z' {
		signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
		var sign [4]byte
		r.ReadAt(sign[:], signoff)
		if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
			return nil, errors.New("Invalid PE File Format.")
		}
		base = signoff + 4
	} else {
		base = int64(0)
	}
	sr.Seek(base, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
		return nil, err
	}
	if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 {
		return nil, errors.New("Invalid PE File Format.")
	}

	var ss []byte
	if f.FileHeader.NumberOfSymbols > 0 {
		// Get COFF string table, which is located at the end of the COFF symbol table.
		sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
		var l uint32
		if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
			return nil, err
		}
		ss = make([]byte, l)
		if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil {
			return nil, err
		}

		// Process COFF symbol table.
		sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET)
		aux := uint8(0)
		for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ {
			cs := new(COFFSymbol)
			if err := binary.Read(sr, binary.LittleEndian, cs); err != nil {
				return nil, err
			}
			if aux > 0 {
				aux--
				continue
			}
			var name string
			if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 {
				si := int(binary.LittleEndian.Uint32(cs.Name[4:]))
				name, _ = getString(ss, si)
			} else {
				name = cstring(cs.Name[:])
			}
			aux = cs.NumberOfAuxSymbols
			s := &Symbol{
				Name:          name,
				Value:         cs.Value,
				SectionNumber: cs.SectionNumber,
				Type:          cs.Type,
				StorageClass:  cs.StorageClass,
			}
			f.Symbols = append(f.Symbols, s)
		}
	}

	// Read optional header.
	sr.Seek(base, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
		return nil, err
	}
	var oh32 OptionalHeader32
	var oh64 OptionalHeader64
	switch uintptr(f.FileHeader.SizeOfOptionalHeader) {
	case unsafe.Sizeof(oh32):
		if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil {
			return nil, err
		}
		if oh32.Magic != 0x10b { // PE32
			return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
		}
		f.OptionalHeader = &oh32
	case unsafe.Sizeof(oh64):
		if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
			return nil, err
		}
		if oh64.Magic != 0x20b { // PE32+
			return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
		}
		f.OptionalHeader = &oh64
	}

	// Process sections.
	f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
	for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
		sh := new(SectionHeader32)
		if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
			return nil, err
		}
		var name string
		if sh.Name[0] == '\x2F' {
			si, _ := strconv.Atoi(cstring(sh.Name[1:]))
			name, _ = getString(ss, si)
		} else {
			name = cstring(sh.Name[0:])
		}
		s := new(Section)
		s.SectionHeader = SectionHeader{
			Name:                 name,
			VirtualSize:          sh.VirtualSize,
			VirtualAddress:       sh.VirtualAddress,
			Size:                 sh.SizeOfRawData,
			Offset:               sh.PointerToRawData,
			PointerToRelocations: sh.PointerToRelocations,
			PointerToLineNumbers: sh.PointerToLineNumbers,
			NumberOfRelocations:  sh.NumberOfRelocations,
			NumberOfLineNumbers:  sh.NumberOfLineNumbers,
			Characteristics:      sh.Characteristics,
		}
		s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
		s.ReaderAt = s.sr
		f.Sections[i] = s
	}
	return f, nil
}

func cstring(b []byte) string {
	var i int
	for i = 0; i < len(b) && b[i] != 0; i++ {
	}
	return string(b[0:i])
}

// getString extracts a string from symbol string table.
func getString(section []byte, start int) (string, bool) {
	if start < 0 || start >= len(section) {
		return "", false
	}

	for end := start; end < len(section); end++ {
		if section[end] == 0 {
			return string(section[start:end]), true
		}
	}
	return "", false
}

// Section returns the first section with the given name, or nil if no such
// section exists.
func (f *File) Section(name string) *Section {
	for _, s := range f.Sections {
		if s.Name == name {
			return s
		}
	}
	return nil
}

func (f *File) DWARF() (*dwarf.Data, error) {
	// There are many other DWARF sections, but these
	// are the required ones, and the debug/dwarf package
	// does not use the others, so don't bother loading them.
	var names = [...]string{"abbrev", "info", "str"}
	var dat [len(names)][]byte
	for i, name := range names {
		name = ".debug_" + name
		s := f.Section(name)
		if s == nil {
			continue
		}
		b, err := s.Data()
		if err != nil && uint32(len(b)) < s.Size {
			return nil, err
		}
		dat[i] = b
	}

	abbrev, info, str := dat[0], dat[1], dat[2]
	return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
}

// ImportedSymbols returns the names of all symbols
// referred to by the binary f that are expected to be
// satisfied by other libraries at dynamic load time.
// It does not return weak symbols.
func (f *File) ImportedSymbols() ([]string, error) {
	pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
	ds := f.Section(".idata")
	if ds == nil {
		// not dynamic, so no libraries
		return nil, nil
	}
	d, err := ds.Data()
	if err != nil {
		return nil, err
	}
	var ida []ImportDirectory
	for len(d) > 0 {
		var dt ImportDirectory
		dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
		dt.Name = binary.LittleEndian.Uint32(d[12:16])
		dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
		d = d[20:]
		if dt.OriginalFirstThunk == 0 {
			break
		}
		ida = append(ida, dt)
	}
	names, _ := ds.Data()
	var all []string
	for _, dt := range ida {
		dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
		d, _ = ds.Data()
		// seek to OriginalFirstThunk
		d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
		for len(d) > 0 {
			if pe64 { // 64bit
				va := binary.LittleEndian.Uint64(d[0:8])
				d = d[8:]
				if va == 0 {
					break
				}
				if va&0x8000000000000000 > 0 { // is Ordinal
					// TODO add dynimport ordinal support.
				} else {
					fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
					all = append(all, fn+":"+dt.dll)
				}
			} else { // 32bit
				va := binary.LittleEndian.Uint32(d[0:4])
				d = d[4:]
				if va == 0 {
					break
				}
				if va&0x80000000 > 0 { // is Ordinal
					// TODO add dynimport ordinal support.
					//ord := va&0x0000FFFF
				} else {
					fn, _ := getString(names, int(va-ds.VirtualAddress+2))
					all = append(all, fn+":"+dt.dll)
				}
			}
		}
	}

	return all, nil
}

// ImportedLibraries returns the names of all libraries
// referred to by the binary f that are expected to be
// linked with the binary at dynamic link time.
func (f *File) ImportedLibraries() ([]string, error) {
	// TODO
	// cgo -dynimport don't use this for windows PE, so just return.
	return nil, nil
}
<commit_msg>debug/pe: remove use of unsafe<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package pe implements access to PE (Microsoft Windows Portable Executable) files.
package pe

import (
	"debug/dwarf"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
)

// A File represents an open PE file.
type File struct {
	FileHeader
	OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
	Sections       []*Section
	Symbols        []*Symbol

	closer io.Closer
}

type SectionHeader struct {
	Name                 string
	VirtualSize          uint32
	VirtualAddress       uint32
	Size                 uint32
	Offset               uint32
	PointerToRelocations uint32
	PointerToLineNumbers uint32
	NumberOfRelocations  uint16
	NumberOfLineNumbers  uint16
	Characteristics      uint32
}

type Section struct {
	SectionHeader

	// Embed ReaderAt for ReadAt method.
	// Do not embed SectionReader directly
	// to avoid having Read and Seek.
	// If a client wants Read and Seek it must use
	// Open() to avoid fighting over the seek offset
	// with other clients.
	io.ReaderAt
	sr *io.SectionReader
}

type Symbol struct {
	Name          string
	Value         uint32
	SectionNumber int16
	Type          uint16
	StorageClass  uint8
}

type ImportDirectory struct {
	OriginalFirstThunk uint32
	TimeDateStamp      uint32
	ForwarderChain     uint32
	Name               uint32
	FirstThunk         uint32

	dll string
}

// Data reads and returns the contents of the PE section.
func (s *Section) Data() ([]byte, error) {
	dat := make([]byte, s.sr.Size())
	n, err := s.sr.ReadAt(dat, 0)
	if n == len(dat) {
		err = nil
	}
	return dat[0:n], err
}

// Open returns a new ReadSeeker reading the PE section.
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }

type FormatError struct {
	off int64
	msg string
	val interface{}
}

func (e *FormatError) Error() string {
	msg := e.msg
	if e.val != nil {
		msg += fmt.Sprintf(" '%v'", e.val)
	}
	msg += fmt.Sprintf(" in record at byte %#x", e.off)
	return msg
}

// Open opens the named file using os.Open and prepares it for use as a PE binary.
func Open(name string) (*File, error) {
	f, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	ff, err := NewFile(f)
	if err != nil {
		f.Close()
		return nil, err
	}
	ff.closer = f
	return ff, nil
}

// Close closes the File.
// If the File was created using NewFile directly instead of Open,
// Close has no effect.
func (f *File) Close() error {
	var err error
	if f.closer != nil {
		err = f.closer.Close()
		f.closer = nil
	}
	return err
}

var (
	sizeofOptionalHeader32 = uintptr(binary.Size(OptionalHeader32{}))
	sizeofOptionalHeader64 = uintptr(binary.Size(OptionalHeader64{}))
)

// NewFile creates a new File for accessing a PE binary in an underlying reader.
func NewFile(r io.ReaderAt) (*File, error) {
	f := new(File)
	sr := io.NewSectionReader(r, 0, 1<<63-1)

	var dosheader [96]byte
	if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
		return nil, err
	}
	var base int64
	if dosheader[0] == 'M' && dosheader[1] == 'Z' {
		signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
		var sign [4]byte
		r.ReadAt(sign[:], signoff)
		if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
			return nil, errors.New("Invalid PE File Format.")
		}
		base = signoff + 4
	} else {
		base = int64(0)
	}
	sr.Seek(base, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
		return nil, err
	}
	if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 {
		return nil, errors.New("Invalid PE File Format.")
	}

	var ss []byte
	if f.FileHeader.NumberOfSymbols > 0 {
		// Get COFF string table, which is located at the end of the COFF symbol table.
		sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
		var l uint32
		if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
			return nil, err
		}
		ss = make([]byte, l)
		if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil {
			return nil, err
		}

		// Process COFF symbol table.
		sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET)
		aux := uint8(0)
		for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ {
			cs := new(COFFSymbol)
			if err := binary.Read(sr, binary.LittleEndian, cs); err != nil {
				return nil, err
			}
			if aux > 0 {
				aux--
				continue
			}
			var name string
			if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 {
				si := int(binary.LittleEndian.Uint32(cs.Name[4:]))
				name, _ = getString(ss, si)
			} else {
				name = cstring(cs.Name[:])
			}
			aux = cs.NumberOfAuxSymbols
			s := &Symbol{
				Name:          name,
				Value:         cs.Value,
				SectionNumber: cs.SectionNumber,
				Type:          cs.Type,
				StorageClass:  cs.StorageClass,
			}
			f.Symbols = append(f.Symbols, s)
		}
	}

	// Read optional header.
	sr.Seek(base, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
		return nil, err
	}
	var oh32 OptionalHeader32
	var oh64 OptionalHeader64
	switch uintptr(f.FileHeader.SizeOfOptionalHeader) {
	case sizeofOptionalHeader32:
		if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil {
			return nil, err
		}
		if oh32.Magic != 0x10b { // PE32
			return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
		}
		f.OptionalHeader = &oh32
	case sizeofOptionalHeader64:
		if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
			return nil, err
		}
		if oh64.Magic != 0x20b { // PE32+
			return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
		}
		f.OptionalHeader = &oh64
	}

	// Process sections.
	f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
	for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
		sh := new(SectionHeader32)
		if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
			return nil, err
		}
		var name string
		if sh.Name[0] == '\x2F' {
			si, _ := strconv.Atoi(cstring(sh.Name[1:]))
			name, _ = getString(ss, si)
		} else {
			name = cstring(sh.Name[0:])
		}
		s := new(Section)
		s.SectionHeader = SectionHeader{
			Name:                 name,
			VirtualSize:          sh.VirtualSize,
			VirtualAddress:       sh.VirtualAddress,
			Size:                 sh.SizeOfRawData,
			Offset:               sh.PointerToRawData,
			PointerToRelocations: sh.PointerToRelocations,
			PointerToLineNumbers: sh.PointerToLineNumbers,
			NumberOfRelocations:  sh.NumberOfRelocations,
			NumberOfLineNumbers:  sh.NumberOfLineNumbers,
			Characteristics:      sh.Characteristics,
		}
		s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
		s.ReaderAt = s.sr
		f.Sections[i] = s
	}
	return f, nil
}

func cstring(b []byte) string {
	var i int
	for i = 0; i < len(b) && b[i] != 0; i++ {
	}
	return string(b[0:i])
}

// getString extracts a string from symbol string table.
func getString(section []byte, start int) (string, bool) {
	if start < 0 || start >= len(section) {
		return "", false
	}

	for end := start; end < len(section); end++ {
		if section[end] == 0 {
			return string(section[start:end]), true
		}
	}
	return "", false
}

// Section returns the first section with the given name, or nil if no such
// section exists.
func (f *File) Section(name string) *Section {
	for _, s := range f.Sections {
		if s.Name == name {
			return s
		}
	}
	return nil
}

func (f *File) DWARF() (*dwarf.Data, error) {
	// There are many other DWARF sections, but these
	// are the required ones, and the debug/dwarf package
	// does not use the others, so don't bother loading them.
	var names = [...]string{"abbrev", "info", "str"}
	var dat [len(names)][]byte
	for i, name := range names {
		name = ".debug_" + name
		s := f.Section(name)
		if s == nil {
			continue
		}
		b, err := s.Data()
		if err != nil && uint32(len(b)) < s.Size {
			return nil, err
		}
		dat[i] = b
	}

	abbrev, info, str := dat[0], dat[1], dat[2]
	return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
}

// ImportedSymbols returns the names of all symbols
// referred to by the binary f that are expected to be
// satisfied by other libraries at dynamic load time.
// It does not return weak symbols.
func (f *File) ImportedSymbols() ([]string, error) {
	pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
	ds := f.Section(".idata")
	if ds == nil {
		// not dynamic, so no libraries
		return nil, nil
	}
	d, err := ds.Data()
	if err != nil {
		return nil, err
	}
	var ida []ImportDirectory
	for len(d) > 0 {
		var dt ImportDirectory
		dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
		dt.Name = binary.LittleEndian.Uint32(d[12:16])
		dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
		d = d[20:]
		if dt.OriginalFirstThunk == 0 {
			break
		}
		ida = append(ida, dt)
	}
	names, _ := ds.Data()
	var all []string
	for _, dt := range ida {
		dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
		d, _ = ds.Data()
		// seek to OriginalFirstThunk
		d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
		for len(d) > 0 {
			if pe64 { // 64bit
				va := binary.LittleEndian.Uint64(d[0:8])
				d = d[8:]
				if va == 0 {
					break
				}
				if va&0x8000000000000000 > 0 { // is Ordinal
					// TODO add dynimport ordinal support.
				} else {
					fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
					all = append(all, fn+":"+dt.dll)
				}
			} else { // 32bit
				va := binary.LittleEndian.Uint32(d[0:4])
				d = d[4:]
				if va == 0 {
					break
				}
				if va&0x80000000 > 0 { // is Ordinal
					// TODO add dynimport ordinal support.
					//ord := va&0x0000FFFF
				} else {
					fn, _ := getString(names, int(va-ds.VirtualAddress+2))
					all = append(all, fn+":"+dt.dll)
				}
			}
		}
	}

	return all, nil
}

// ImportedLibraries returns the names of all libraries
// referred to by the binary f that are expected to be
// linked with the binary at dynamic link time.
func (f *File) ImportedLibraries() ([]string, error) {
	// TODO
	// cgo -dynimport don't use this for windows PE, so just return.
	return nil, nil
}
<|endoftext|>


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

package api

import (
	"fmt"
	"net/http"

	"github.com/tsuru/tsuru/app"
	"github.com/tsuru/tsuru/auth"
	saml "github.com/tsuru/tsuru/auth/saml"
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/errors"
)

func samlMetadata(w http.ResponseWriter, r *http.Request) error {
	if app.AuthScheme.Name() != "saml" {
		return &errors.HTTP{
			Code:    http.StatusBadRequest,
			Message: "This URL is only supported with saml enabled",
		}
	}
	page, err := saml.Metadata()
	if err != nil {
		return &errors.HTTP{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	w.Header().Set("Content-Type", "application/xml")
	w.Write([]byte(page))
	return nil
}

func samlCallbackLogin(w http.ResponseWriter, r *http.Request) error {
	if app.AuthScheme.Name() != "saml" {
		return &errors.HTTP{
			Code:    http.StatusBadRequest,
			Message: "This URL is only supported with saml enabled",
		}
	}
	params := map[string]string{}
	content := r.PostFormValue("SAMLResponse")
	if content == "" {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: "Empty SAML Response"}
	}
	params["callback"] = "true"
	params["xml"] = content
	//Get saml.SAMLAuthScheme, error already treated on first check
	scheme, _ := auth.GetScheme("saml")
	_, err := scheme.Login(params)
	if err != nil {
		msg := fmt.Sprintf(cmd.SamlCallbackFailureMessage(), err.Error())
		fmt.Fprintf(w, msg)
	} else {
		fmt.Fprintf(w, cmd.SamlCallbackSuccessMessage())
	}
	return nil
}
<commit_msg>remove alias from import<commit_after>// Copyright 2015 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

import (
	"fmt"
	"net/http"

	"github.com/tsuru/tsuru/app"
	"github.com/tsuru/tsuru/auth"
	"github.com/tsuru/tsuru/auth/saml"
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/errors"
)

func samlMetadata(w http.ResponseWriter, r *http.Request) error {
	if app.AuthScheme.Name() != "saml" {
		return &errors.HTTP{
			Code:    http.StatusBadRequest,
			Message: "This URL is only supported with saml enabled",
		}
	}
	page, err := saml.Metadata()
	if err != nil {
		return &errors.HTTP{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	w.Header().Set("Content-Type", "application/xml")
	w.Write([]byte(page))
	return nil
}

func samlCallbackLogin(w http.ResponseWriter, r *http.Request) error {
	if app.AuthScheme.Name() != "saml" {
		return &errors.HTTP{
			Code:    http.StatusBadRequest,
			Message: "This URL is only supported with saml enabled",
		}
	}
	params := map[string]string{}
	content := r.PostFormValue("SAMLResponse")
	if content == "" {
		return &errors.HTTP{Code: http.StatusBadRequest, Message: "Empty SAML Response"}
	}
	params["callback"] = "true"
	params["xml"] = content
	//Get saml.SAMLAuthScheme, error already treated on first check
	scheme, _ := auth.GetScheme("saml")
	_, err := scheme.Login(params)
	if err != nil {
		msg := fmt.Sprintf(cmd.SamlCallbackFailureMessage(), err.Error())
		fmt.Fprintf(w, msg)
	} else {
		fmt.Fprintf(w, cmd.SamlCallbackSuccessMessage())
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2013,2014 SmugMug, Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY SMUGMUG, INC. ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SMUGMUG, INC. BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES;LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Manages reading the conf file into the global var as described in the `conf` package.
package conf_file

import (
	"os"
	"strconv"
	"net"
	"net/url"
	"log"
	"io/ioutil"
	"encoding/json"
	"path/filepath"
	"github.com/smugmug/goawsroles/roles_files"
	"github.com/smugmug/godynamo/aws_const"
	"github.com/smugmug/godynamo/conf"
)

// Read will look for and read in the conf file, which can then be referenced as conf.Vals.
// The conf file is specifically relevant to properly formatted requests, so it is currently
// called in the initialization of the authreq package.
// You may also set $GODYNAMO_CONF_FILE to be a fully-qualified path to a conf file
// if the two preset locations are not adequate.
func Read() {
	var cf conf.SDK_conf_file
	local_conf := os.Getenv("HOME") + string(filepath.Separator) + "." + conf.CONF_NAME
	etc_conf   := string(filepath.Separator) + "etc" + string(filepath.Separator) + conf.CONF_NAME
	read_conf  := false
	conf_files := make([]string,0)
	// assumes that if set, this is a fully-qualified file path
	if os.Getenv("GODYNAMO_CONF_FILE") != "" {
		conf_files = append(conf_files,os.Getenv("GODYNAMO_CONF_FILE"))
	}
	conf_files = append(conf_files,local_conf)
	conf_files = append(conf_files,etc_conf)
	conf.Vals.ConfLock.Lock()
	defer conf.Vals.ConfLock.Unlock()
	CONF_LOCATIONS:for _,conf_file := range conf_files {
		conf_bytes,conf_err := ioutil.ReadFile(conf_file)
		if conf_err != nil {
			log.Printf("cannot find conf file at %s\n",conf_file)
			continue CONF_LOCATIONS
		} else {
			um_err := json.Unmarshal(conf_bytes,&cf)
			if um_err != nil {
				panic("conf_file.Read:" + conf_file +
					" json err: " +
					um_err.Error())
			} else {
				log.Printf("read conf from: %s\n",conf_file)
				read_conf = true
				break
			}
		}
	}
	if !read_conf {
		panic("confload.init: read err: " +
			"\n\n\n*****\nMake sure you have a conf file!\n" +
			"An example conf file is located in the /conf dir.\n" +
			"Put it in your home dir as\n$HOME/.aws-config.json\nor " +
			"in /etc as\n/etc/aws-config.json\nand fill " +
			"in the values for your AWS account*****\n\n\n")
	}

	// make sure the dynamo endpoint is available
	addrs,addrs_err := net.LookupIP(cf.Services.Dynamo_db.Host)
	if addrs_err != nil {
		panic("cannot look up hostname: " + cf.Services.Dynamo_db.Host)
	}
	dynamo_ip := (addrs[0]).String()

	// assign the values to our globally-available conf.Vals struct instance
	conf.Vals.Auth.AccessKey = cf.Services.Default_settings.Params.Access_key_id
	conf.Vals.Auth.Secret = cf.Services.Default_settings.Params.Secret_access_key
	conf.Vals.UseSysLog = cf.Services.Default_settings.Params.Use_sys_log
	conf.Vals.Network.DynamoDB.Host = cf.Services.Dynamo_db.Host
	conf.Vals.Network.DynamoDB.IP = dynamo_ip
	conf.Vals.Network.DynamoDB.Zone = cf.Services.Dynamo_db.Zone
	scheme := "http"
	port   := aws_const.PORT // already a string
	if cf.Services.Dynamo_db.Scheme != "" {
		scheme = cf.Services.Dynamo_db.Scheme
	}
	if cf.Services.Dynamo_db.Port != 0 {
		port = strconv.Itoa(cf.Services.Dynamo_db.Port)
	}
	conf.Vals.Network.DynamoDB.Port = port
	conf.Vals.Network.DynamoDB.Scheme = scheme
	conf.Vals.Network.DynamoDB.URL = scheme + "://" + conf.Vals.Network.DynamoDB.Host +
	":" + port
	_,url_err := url.Parse(conf.Vals.Network.DynamoDB.URL)
	if url_err != nil {
		panic("confload.init: read err: conf.Vals.Network.DynamoDB.URL malformed")
	}
	log.Printf("remote url:%s\n",conf.Vals.Network.DynamoDB.URL)

	// If set to true, programs that are written with godynamo may
	// opt to launch the keepalive goroutine to keep conns open.
	conf.Vals.Network.DynamoDB.KeepAlive = cf.Services.Dynamo_db.KeepAlive

	// read in flags for IAM support
	if cf.Services.Dynamo_db.IAM.Use_iam == true {
		if cf.Services.Dynamo_db.IAM.Role_provider != roles_files.ROLE_PROVIDER {
			panic("confload.init: read err: " +
				"\n\n\n**** only IAM role provider 'file' is supported *****\n\n\n")
		}
		conf.Vals.IAM.RoleProvider = cf.Services.Dynamo_db.IAM.Role_provider
		conf.Vals.IAM.File.BaseDir = cf.Services.Dynamo_db.IAM.Base_dir
		conf.Vals.IAM.File.AccessKey = cf.Services.Dynamo_db.IAM.Access_key
		conf.Vals.IAM.File.Secret = cf.Services.Dynamo_db.IAM.Secret_key
		conf.Vals.IAM.File.Token = cf.Services.Dynamo_db.IAM.Token
		if cf.Services.Dynamo_db.IAM.Watch == true {
			conf.Vals.IAM.Watch = true
		} else {
			conf.Vals.IAM.Watch = false
		}
		conf.Vals.UseIAM = true
	}
	conf.Vals.Initialized = true
}
<commit_msg>move env conf file name to const<commit_after>// Copyright (c) 2013,2014 SmugMug, Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY SMUGMUG, INC. ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SMUGMUG, INC. BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES;LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Manages reading the conf file into the global var as described in the `conf` package.
package conf_file

import (
	"os"
	"strconv"
	"net"
	"net/url"
	"log"
	"io/ioutil"
	"encoding/json"
	"path/filepath"
	"github.com/smugmug/goawsroles/roles_files"
	"github.com/smugmug/godynamo/aws_const"
	"github.com/smugmug/godynamo/conf"
)

// Read will look for and read in the conf file, which can then be referenced as conf.Vals.
// The conf file is specifically relevant to properly formatted requests, so it is currently
// called in the initialization of the authreq package.
// You may also set $GODYNAMO_CONF_FILE to be a fully-qualified path to a conf file
// if the two preset locations are not adequate.
func Read() {
	var cf conf.SDK_conf_file
	local_conf := os.Getenv("HOME") + string(filepath.Separator) + "." + conf.CONF_NAME
	etc_conf   := string(filepath.Separator) + "etc" + string(filepath.Separator) + conf.CONF_NAME
	read_conf  := false
	conf_files := make([]string,0)
	const env_conf = "GODYNAMO_CONF_FILE"
	// assumes that if set, this is a fully-qualified file path
	if os.Getenv(env_conf) != "" {
		conf_files = append(conf_files,os.Getenv(env_conf))
	}
	conf_files = append(conf_files,local_conf)
	conf_files = append(conf_files,etc_conf)
	conf.Vals.ConfLock.Lock()
	defer conf.Vals.ConfLock.Unlock()
	CONF_LOCATIONS:for _,conf_file := range conf_files {
		conf_bytes,conf_err := ioutil.ReadFile(conf_file)
		if conf_err != nil {
			log.Printf("cannot find conf file at %s\n",conf_file)
			continue CONF_LOCATIONS
		} else {
			um_err := json.Unmarshal(conf_bytes,&cf)
			if um_err != nil {
				panic("conf_file.Read:" + conf_file +
					" json err: " +
					um_err.Error())
			} else {
				log.Printf("read conf from: %s\n",conf_file)
				read_conf = true
				break
			}
		}
	}
	if !read_conf {
		panic("confload.init: read err: " +
			"\n\n\n*****\nMake sure you have a conf file!\n" +
			"An example conf file is located in the /conf dir.\n" +
			"Put it in your home dir as\n$HOME/.aws-config.json\nor " +
			"in /etc as\n/etc/aws-config.json\nand fill " +
			"in the values for your AWS account*****\n\n\n")
	}

	// make sure the dynamo endpoint is available
	addrs,addrs_err := net.LookupIP(cf.Services.Dynamo_db.Host)
	if addrs_err != nil {
		panic("cannot look up hostname: " + cf.Services.Dynamo_db.Host)
	}
	dynamo_ip := (addrs[0]).String()

	// assign the values to our globally-available conf.Vals struct instance
	conf.Vals.Auth.AccessKey = cf.Services.Default_settings.Params.Access_key_id
	conf.Vals.Auth.Secret = cf.Services.Default_settings.Params.Secret_access_key
	conf.Vals.UseSysLog = cf.Services.Default_settings.Params.Use_sys_log
	conf.Vals.Network.DynamoDB.Host = cf.Services.Dynamo_db.Host
	conf.Vals.Network.DynamoDB.IP = dynamo_ip
	conf.Vals.Network.DynamoDB.Zone = cf.Services.Dynamo_db.Zone
	scheme := "http"
	port   := aws_const.PORT // already a string
	if cf.Services.Dynamo_db.Scheme != "" {
		scheme = cf.Services.Dynamo_db.Scheme
	}
	if cf.Services.Dynamo_db.Port != 0 {
		port = strconv.Itoa(cf.Services.Dynamo_db.Port)
	}
	conf.Vals.Network.DynamoDB.Port = port
	conf.Vals.Network.DynamoDB.Scheme = scheme
	conf.Vals.Network.DynamoDB.URL = scheme + "://" + conf.Vals.Network.DynamoDB.Host +
	":" + port
	_,url_err := url.Parse(conf.Vals.Network.DynamoDB.URL)
	if url_err != nil {
		panic("confload.init: read err: conf.Vals.Network.DynamoDB.URL malformed")
	}
	log.Printf("remote url:%s\n",conf.Vals.Network.DynamoDB.URL)

	// If set to true, programs that are written with godynamo may
	// opt to launch the keepalive goroutine to keep conns open.
	conf.Vals.Network.DynamoDB.KeepAlive = cf.Services.Dynamo_db.KeepAlive

	// read in flags for IAM support
	if cf.Services.Dynamo_db.IAM.Use_iam == true {
		if cf.Services.Dynamo_db.IAM.Role_provider != roles_files.ROLE_PROVIDER {
			panic("confload.init: read err: " +
				"\n\n\n**** only IAM role provider 'file' is supported *****\n\n\n")
		}
		conf.Vals.IAM.RoleProvider = cf.Services.Dynamo_db.IAM.Role_provider
		conf.Vals.IAM.File.BaseDir = cf.Services.Dynamo_db.IAM.Base_dir
		conf.Vals.IAM.File.AccessKey = cf.Services.Dynamo_db.IAM.Access_key
		conf.Vals.IAM.File.Secret = cf.Services.Dynamo_db.IAM.Secret_key
		conf.Vals.IAM.File.Token = cf.Services.Dynamo_db.IAM.Token
		if cf.Services.Dynamo_db.IAM.Watch == true {
			conf.Vals.IAM.Watch = true
		} else {
			conf.Vals.IAM.Watch = false
		}
		conf.Vals.UseIAM = true
	}
	conf.Vals.Initialized = true
}
<|endoftext|>


<commit_before><commit_msg>Drop hypervisors from hypervisors list with duplicate ip address and in down state.<commit_after><|endoftext|>


<commit_before>package vdom_test

import (
	"testing"

	. "github.com/gowade/wade/vdom"

	"github.com/stretchr/testify/suite"
)

type DiffTestSuite struct {
	suite.Suite
}

type GoDomNode struct {
	Node
}

func (n GoDomNode) Child(idx int) DomNode {
	return GoDomNode{n.Node.(*Element).Children[idx]}
}

type attrChange struct {
	remove  bool
	attr    string
	value   interface{}
	domNode DomNode
}

type change struct {
	action Action
	dNode  GoDomNode
}

func (c change) affectedNode() GoDomNode {
	return c.dNode.Child(c.action.Index).(GoDomNode)
}

type modifier struct {
	changes     []change
	attrChanges []attrChange
}

func (m *modifier) recordAC(c attrChange) {
	m.attrChanges = append(m.attrChanges, c)
}

func (m *modifier) Do(d DomNode, action Action) {
	change := change{action: action}
	if d != nil {
		change.dNode = d.(GoDomNode)
	}
	m.changes = append(m.changes, change)
}

func (m *modifier) SetProp(d DomNode, attr string, v interface{}) {
}

func (m *modifier) SetAttr(d DomNode, attr string, v interface{}) {
	if b, ok := v.(bool); ok && b == false {
		m.RemoveAttr(d, attr)
		return
	}

	m.recordAC(attrChange{false, attr, v, d})
}

func (m *modifier) RemoveAttr(d DomNode, attr string) {
	m.recordAC(attrChange{true, attr, nil, d})
}

func newModifier() *modifier {
	return &modifier{make([]change, 0), make([]attrChange, 0)}
}
func (s *DiffTestSuite) TestDiff() {
	m1 := newModifier()
	a := NewElement("div", "", nil, nil)
	PerformDiff(a, nil, GoDomNode{NewElement("div", "", nil, nil)}, m1)
	s.Len(m1.changes, 1)
	s.Equal(m1.changes[0].action.Type, Update)
	s.Equal(m1.changes[0].dNode.NodeData(), "div")

	b := NewElement("div", "", Attributes{"title": "d"}, []Node{
		NewElement("span", "", nil, []Node{NewTextNode("C")}),
		NewElement("ul", "", Attributes{"disabled": true}, []Node{
			NewElement("li", "", nil, []Node{NewTextNode("A")}),
		}),
	})
	d := GoDomNode{b}
	a = NewElement("div", "", nil, []Node{
		NewElement("span", "", nil, []Node{}),
		NewElement("ul", "", Attributes{"disabled": false, "value": "0"}, []Node{
			NewElement("notli", "", Attributes{"id": "11"}, []Node{NewTextNode("A")}),
			NewElement("li", "", nil, []Node{NewTextNode("B")}),
		})})

	m1 = newModifier()
	PerformDiff(a, b, d, m1)
	s.Equal(m1.changes[0].action.Type, Deletion)
	s.Equal(m1.changes[0].action.Index, 0)
	s.Equal(m1.changes[0].affectedNode().NodeData(), "C")

	s.Equal(m1.changes[1].action.Type, Update)
	s.Equal(m1.changes[1].action.Content.NodeData(), "notli")
	s.Equal(m1.changes[1].dNode.NodeData(), "li")

	s.Equal(m1.changes[2].action.Type, Insertion)
	s.Equal(m1.changes[2].action.Content.(*Element).Children[0].NodeData(), "B")

	s.Len(m1.changes, 3)

	// Test attribute diffing
	s.Equal(m1.attrChanges[0].remove, true)
	s.Equal(m1.attrChanges[0].attr, "title")
	s.Equal(m1.attrChanges[0].domNode.(GoDomNode).NodeData(), "div")

	s.Equal(m1.attrChanges[1].remove, true)
	s.Equal(m1.attrChanges[1].attr, "disabled")
	s.Equal(m1.attrChanges[1].value, nil)
	s.Equal(m1.attrChanges[1].domNode.(GoDomNode).NodeData(), "ul")

	s.Equal(m1.attrChanges[2].remove, false)
	s.Equal(m1.attrChanges[2].attr, "value")
	s.Equal(m1.attrChanges[2].value, "0")
	s.Equal(m1.attrChanges[2].domNode.(GoDomNode).NodeData(), "ul")

	s.Len(m1.attrChanges, 3)
}

func (s *DiffTestSuite) TestKeyedDiff() {
	m1 := newModifier()
	b := NewElement("div", "", nil, []Node{
		NewElement("ul", "", nil, []Node{
			NewElement("li", "1", nil, nil),
			NewElement("li", "2", nil, nil),
			NewElement("li", "3", nil, nil),
			NewElement("li", "4", nil, nil),
		}),
	})
	d := GoDomNode{b}
	a := NewElement("div", "", nil, []Node{
		NewElement("ul", "", nil, []Node{
			NewElement("li", "0", nil, nil),
			NewElement("li", "4", nil, nil),
			NewElement("li", "", nil, nil),
			NewElement("li", "2", nil, nil),
			NewElement("li", "5", nil, nil),
		}),
	})

	PerformDiff(a, b, d, m1)

	s.Equal(m1.changes[0].action.Type, Deletion)
	s.Equal(m1.changes[0].action.Index, 0)
	s.Equal(m1.changes[1].action.Type, Deletion)
	s.Equal(m1.changes[1].action.Index, 2)

	s.Equal(m1.changes[2].action.Type, Insertion)
	s.Equal(m1.changes[2].action.Index, 0)
	s.Equal(m1.changes[2].action.Content.(*Element).Key, "0")

	s.Equal(m1.changes[3].action.Type, Insertion)
	s.Equal(m1.changes[3].action.Index, 4)
	s.Equal(m1.changes[3].action.Content.(*Element).Key, "5")

	s.Equal(m1.changes[4].action.Type, Move)
	s.Equal(m1.changes[4].action.Index, 1)
	s.Equal(m1.changes[4].action.From, 3)

	s.Equal(m1.changes[5].action.Type, Move)
	s.Equal(m1.changes[5].action.Index, 3)
	s.Equal(m1.changes[5].action.From, 1)

	// unkeyed
	s.Equal(m1.changes[6].action.Type, Insertion)
	s.Equal(m1.changes[6].action.Index, 2)
}

func TestDiff(t *testing.T) {
	suite.Run(t, new(DiffTestSuite))
}
<commit_msg>Remove diff_test.go, it's not needed anymore.<commit_after><|endoftext|>


<commit_before>package api

import "strings"
import "time"
import "fmt"
import "hash/crc64"

var _ = fmt.Sprintf("dummy")

// Parsecsv parses the input string for comma separated string values and
// return parsed strings.
func Parsecsv(input string) []string {
	if input == "" {
		return nil
	}
	ss := strings.Split(input, ",")

	var outs []string

	for _, s := range ss {
		s = strings.Trim(s, " \t\r\n")
		if s == "" {
			continue
		}
		outs = append(outs, s)
	}
	return outs
}

// Maxints return the max value amont numbers.
func Maxints(numbers ...int) int {
	maxNum := numbers[0]
	for _, item := range numbers {
		if maxNum < item {
			maxNum = item
		}
	}
	return maxNum
}

// Repeatstr to repeat the string `string` n times and return the same.
func Repeatstr(str string, n int) string {
	out := ""
	for i := 0; i < n; i++ {
		out += str
	}
	return out
}

func StringSet(xs []string) []string {
	// TODO: is there a better algorithm to identify duplicates
	ys := make([]string, len(xs))
outer:
	for _, x := range xs {
		for _, y := range ys {
			if x == y {
				continue outer
			}
		}
		ys = append(ys, x)
	}
	return ys
}

func ValidateDate(tm time.Time, year, month, date, hour, min, sec int) bool {
	y, m, d := tm.Date()
	h, t, s := tm.Clock()
	if y != year || m != time.Month(month) || d != date {
		return false
	} else if h != hour || t != min || s != sec {
		return false
	}
	return true
}

func HasString(xs []string, y string) bool {
	for _, x := range xs {
		if y == x {
			return true
		}
	}
	return false
}

var isoCrc64 *crc64.Table

func Crc64(data []byte) uint64 {
	return crc64.Checksum(data, isoCrc64)
}

func init() {
	isoCrc64 = crc64.MakeTable(crc64.ISO)
}
<commit_msg>util: helper function to get stack-trace.<commit_after>package api

import "strings"
import "time"
import "fmt"
import "bytes"
import "hash/crc64"

var _ = fmt.Sprintf("dummy")

// Parsecsv parses the input string for comma separated string values and
// return parsed strings.
func Parsecsv(input string) []string {
	if input == "" {
		return nil
	}
	ss := strings.Split(input, ",")

	var outs []string

	for _, s := range ss {
		s = strings.Trim(s, " \t\r\n")
		if s == "" {
			continue
		}
		outs = append(outs, s)
	}
	return outs
}

// Maxints return the max value amont numbers.
func Maxints(numbers ...int) int {
	maxNum := numbers[0]
	for _, item := range numbers {
		if maxNum < item {
			maxNum = item
		}
	}
	return maxNum
}

// Repeatstr to repeat the string `string` n times and return the same.
func Repeatstr(str string, n int) string {
	out := ""
	for i := 0; i < n; i++ {
		out += str
	}
	return out
}

func StringSet(xs []string) []string {
	// TODO: is there a better algorithm to identify duplicates
	ys := make([]string, len(xs))
outer:
	for _, x := range xs {
		for _, y := range ys {
			if x == y {
				continue outer
			}
		}
		ys = append(ys, x)
	}
	return ys
}

func ValidateDate(tm time.Time, year, month, date, hour, min, sec int) bool {
	y, m, d := tm.Date()
	h, t, s := tm.Clock()
	if y != year || m != time.Month(month) || d != date {
		return false
	} else if h != hour || t != min || s != sec {
		return false
	}
	return true
}

func HasString(xs []string, y string) bool {
	for _, x := range xs {
		if y == x {
			return true
		}
	}
	return false
}

func GetStacktrace(skip int, stack []byte) string {
	var buf bytes.Buffer
	lines := strings.Split(string(stack), "\n")
	for _, call := range lines[skip*2:] {
		buf.WriteString(fmt.Sprintf("%s\n", call))
	}
	return buf.String()
}

var isoCrc64 *crc64.Table

func Crc64(data []byte) uint64 {
	return crc64.Checksum(data, isoCrc64)
}

func init() {
	isoCrc64 = crc64.MakeTable(crc64.ISO)
}
<|endoftext|>


<commit_before>package routing

import (
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"testing"
	"time"

	"github.com/lightningnetwork/lnd/channeldb/kvdb"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/lightningnetwork/lnd/routing/route"
)

const (
	sourceNodeID = 1
	targetNodeID = 2
)

// integratedRoutingContext defines the context in which integrated routing
// tests run.
type integratedRoutingContext struct {
	graph *mockGraph
	t     *testing.T

	source *mockNode
	target *mockNode

	amt         lnwire.MilliSatoshi
	finalExpiry int32

	mcCfg          MissionControlConfig
	pathFindingCfg PathFindingConfig
}

// newIntegratedRoutingContext instantiates a new integrated routing test
// context with a source and a target node.
func newIntegratedRoutingContext(t *testing.T) *integratedRoutingContext {
	// Instantiate a mock graph.
	source := newMockNode(sourceNodeID)
	target := newMockNode(targetNodeID)

	graph := newMockGraph(t)
	graph.addNode(source)
	graph.addNode(target)
	graph.source = source

	// Initiate the test context with a set of default configuration values.
	// We don't use the lnd defaults here, because otherwise changing the
	// defaults would break the unit tests. The actual values picked aren't
	// critical to excite certain behavior, but do need to be aligned with
	// the test case assertions.
	ctx := integratedRoutingContext{
		t:           t,
		graph:       graph,
		amt:         100000,
		finalExpiry: 40,

		mcCfg: MissionControlConfig{
			PenaltyHalfLife:       30 * time.Minute,
			AprioriHopProbability: 0.6,
			AprioriWeight:         0.5,
			SelfNode:              source.pubkey,
		},

		pathFindingCfg: PathFindingConfig{
			AttemptCost:    1000,
			MinProbability: 0.01,
		},

		source: source,
		target: target,
	}

	return &ctx
}

// htlcAttempt records the route and outcome of an attempted htlc.
type htlcAttempt struct {
	route   *route.Route
	success bool
}

func (h htlcAttempt) String() string {
	return fmt.Sprintf("success=%v, route=%v", h.success, h.route)
}

// testPayment launches a test payment and asserts that it is completed after
// the expected number of attempts.
func (c *integratedRoutingContext) testPayment(maxParts uint32) ([]htlcAttempt,
	error) {

	var (
		nextPid  uint64
		attempts []htlcAttempt
	)

	// Create temporary database for mission control.
	file, err := ioutil.TempFile("", "*.db")
	if err != nil {
		c.t.Fatal(err)
	}

	dbPath := file.Name()
	defer os.Remove(dbPath)

	db, err := kvdb.Open(
		kvdb.BoltBackendName, dbPath, true, kvdb.DefaultDBTimeout,
	)
	if err != nil {
		c.t.Fatal(err)
	}
	defer db.Close()

	// Instantiate a new mission control with the current configuration
	// values.
	mc, err := NewMissionControl(db, &c.mcCfg)
	if err != nil {
		c.t.Fatal(err)
	}

	getBandwidthHints := func() (map[uint64]lnwire.MilliSatoshi, error) {
		// Create bandwidth hints based on local channel balances.
		bandwidthHints := map[uint64]lnwire.MilliSatoshi{}
		for _, ch := range c.graph.nodes[c.source.pubkey].channels {
			bandwidthHints[ch.id] = ch.balance
		}

		return bandwidthHints, nil
	}

	var paymentAddr [32]byte
	payment := LightningPayment{
		FinalCLTVDelta: uint16(c.finalExpiry),
		FeeLimit:       lnwire.MaxMilliSatoshi,
		Target:         c.target.pubkey,
		PaymentAddr:    &paymentAddr,
		DestFeatures:   lnwire.NewFeatureVector(mppFeatures, nil),
		Amount:         c.amt,
		CltvLimit:      math.MaxUint32,
		MaxParts:       maxParts,
	}

	session, err := newPaymentSession(
		&payment, getBandwidthHints,
		func() (routingGraph, func(), error) {
			return c.graph, func() {}, nil
		},
		mc, c.pathFindingCfg,
	)
	if err != nil {
		c.t.Fatal(err)
	}

	// Override default minimum shard amount.
	session.minShardAmt = lnwire.NewMSatFromSatoshis(5000)

	// Now the payment control loop starts. It will keep trying routes until
	// the payment succeeds.
	var (
		amtRemaining  = payment.Amount
		inFlightHtlcs uint32
	)
	for {
		// Create bandwidth hints based on local channel balances.
		bandwidthHints := map[uint64]lnwire.MilliSatoshi{}
		for _, ch := range c.graph.nodes[c.source.pubkey].channels {
			bandwidthHints[ch.id] = ch.balance
		}

		// Find a route.
		route, err := session.RequestRoute(
			amtRemaining, lnwire.MaxMilliSatoshi, inFlightHtlcs, 0,
		)
		if err != nil {
			return attempts, err
		}

		// Send out the htlc on the mock graph.
		pid := nextPid
		nextPid++
		htlcResult, err := c.graph.sendHtlc(route)
		if err != nil {
			c.t.Fatal(err)
		}

		success := htlcResult.failure == nil
		attempts = append(attempts, htlcAttempt{
			route:   route,
			success: success,
		})

		// Process the result. In normal Lightning operations, the
		// sender doesn't get an acknowledgement from the recipient that
		// the htlc arrived. In integrated routing tests, this
		// acknowledgement is available. It is a simplification of
		// reality that still allows certain classes of tests to be
		// performed.
		if success {
			inFlightHtlcs++

			err := mc.ReportPaymentSuccess(pid, route)
			if err != nil {
				c.t.Fatal(err)
			}

			amtRemaining -= route.ReceiverAmt()

			// If the full amount has been paid, the payment is
			// successful and the control loop can be terminated.
			if amtRemaining == 0 {
				break
			}

			// Otherwise try to send the remaining amount.
			continue
		}

		// Failure, update mission control and retry.
		finalResult, err := mc.ReportPaymentFail(
			pid, route,
			getNodeIndex(route, htlcResult.failureSource),
			htlcResult.failure,
		)
		if err != nil {
			c.t.Fatal(err)
		}

		if finalResult != nil {
			break
		}
	}

	return attempts, nil
}

// getNodeIndex returns the zero-based index of the given node in the route.
func getNodeIndex(route *route.Route, failureSource route.Vertex) *int {
	if failureSource == route.SourcePubKey {
		idx := 0
		return &idx
	}

	for i, h := range route.Hops {
		if h.PubKeyBytes == failureSource {
			idx := i + 1
			return &idx
		}
	}
	return nil
}
<commit_msg>routing: allow custom dest feature bits in integratedRoutingContext.testPayment<commit_after>package routing

import (
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"testing"
	"time"

	"github.com/lightningnetwork/lnd/channeldb/kvdb"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/lightningnetwork/lnd/routing/route"
)

const (
	sourceNodeID = 1
	targetNodeID = 2
)

// integratedRoutingContext defines the context in which integrated routing
// tests run.
type integratedRoutingContext struct {
	graph *mockGraph
	t     *testing.T

	source *mockNode
	target *mockNode

	amt         lnwire.MilliSatoshi
	finalExpiry int32

	mcCfg          MissionControlConfig
	pathFindingCfg PathFindingConfig
}

// newIntegratedRoutingContext instantiates a new integrated routing test
// context with a source and a target node.
func newIntegratedRoutingContext(t *testing.T) *integratedRoutingContext {
	// Instantiate a mock graph.
	source := newMockNode(sourceNodeID)
	target := newMockNode(targetNodeID)

	graph := newMockGraph(t)
	graph.addNode(source)
	graph.addNode(target)
	graph.source = source

	// Initiate the test context with a set of default configuration values.
	// We don't use the lnd defaults here, because otherwise changing the
	// defaults would break the unit tests. The actual values picked aren't
	// critical to excite certain behavior, but do need to be aligned with
	// the test case assertions.
	ctx := integratedRoutingContext{
		t:           t,
		graph:       graph,
		amt:         100000,
		finalExpiry: 40,

		mcCfg: MissionControlConfig{
			PenaltyHalfLife:       30 * time.Minute,
			AprioriHopProbability: 0.6,
			AprioriWeight:         0.5,
			SelfNode:              source.pubkey,
		},

		pathFindingCfg: PathFindingConfig{
			AttemptCost:    1000,
			MinProbability: 0.01,
		},

		source: source,
		target: target,
	}

	return &ctx
}

// htlcAttempt records the route and outcome of an attempted htlc.
type htlcAttempt struct {
	route   *route.Route
	success bool
}

func (h htlcAttempt) String() string {
	return fmt.Sprintf("success=%v, route=%v", h.success, h.route)
}

// testPayment launches a test payment and asserts that it is completed after
// the expected number of attempts.
func (c *integratedRoutingContext) testPayment(maxParts uint32,
	destFeatureBits ...lnwire.FeatureBit) ([]htlcAttempt, error) {

	// We start out with the base set of MPP feature bits. If the caller
	// overrides this set of bits, then we'll use their feature bits
	// entirely.
	baseFeatureBits := mppFeatures
	if len(destFeatureBits) != 0 {
		baseFeatureBits = lnwire.NewRawFeatureVector(destFeatureBits...)
	}

	var (
		nextPid  uint64
		attempts []htlcAttempt
	)

	// Create temporary database for mission control.
	file, err := ioutil.TempFile("", "*.db")
	if err != nil {
		c.t.Fatal(err)
	}

	dbPath := file.Name()
	defer os.Remove(dbPath)

	db, err := kvdb.Open(
		kvdb.BoltBackendName, dbPath, true, kvdb.DefaultDBTimeout,
	)
	if err != nil {
		c.t.Fatal(err)
	}
	defer db.Close()

	// Instantiate a new mission control with the current configuration
	// values.
	mc, err := NewMissionControl(db, &c.mcCfg)
	if err != nil {
		c.t.Fatal(err)
	}

	getBandwidthHints := func() (map[uint64]lnwire.MilliSatoshi, error) {
		// Create bandwidth hints based on local channel balances.
		bandwidthHints := map[uint64]lnwire.MilliSatoshi{}
		for _, ch := range c.graph.nodes[c.source.pubkey].channels {
			bandwidthHints[ch.id] = ch.balance
		}

		return bandwidthHints, nil
	}

	var paymentAddr [32]byte
	payment := LightningPayment{
		FinalCLTVDelta: uint16(c.finalExpiry),
		FeeLimit:       lnwire.MaxMilliSatoshi,
		Target:         c.target.pubkey,
		PaymentAddr:    &paymentAddr,
		DestFeatures:   lnwire.NewFeatureVector(baseFeatureBits, nil),
		Amount:         c.amt,
		CltvLimit:      math.MaxUint32,
		MaxParts:       maxParts,
	}

	session, err := newPaymentSession(
		&payment, getBandwidthHints,
		func() (routingGraph, func(), error) {
			return c.graph, func() {}, nil
		},
		mc, c.pathFindingCfg,
	)
	if err != nil {
		c.t.Fatal(err)
	}

	// Override default minimum shard amount.
	session.minShardAmt = lnwire.NewMSatFromSatoshis(5000)

	// Now the payment control loop starts. It will keep trying routes until
	// the payment succeeds.
	var (
		amtRemaining  = payment.Amount
		inFlightHtlcs uint32
	)
	for {
		// Create bandwidth hints based on local channel balances.
		bandwidthHints := map[uint64]lnwire.MilliSatoshi{}
		for _, ch := range c.graph.nodes[c.source.pubkey].channels {
			bandwidthHints[ch.id] = ch.balance
		}

		// Find a route.
		route, err := session.RequestRoute(
			amtRemaining, lnwire.MaxMilliSatoshi, inFlightHtlcs, 0,
		)
		if err != nil {
			return attempts, err
		}

		// Send out the htlc on the mock graph.
		pid := nextPid
		nextPid++
		htlcResult, err := c.graph.sendHtlc(route)
		if err != nil {
			c.t.Fatal(err)
		}

		success := htlcResult.failure == nil
		attempts = append(attempts, htlcAttempt{
			route:   route,
			success: success,
		})

		// Process the result. In normal Lightning operations, the
		// sender doesn't get an acknowledgement from the recipient that
		// the htlc arrived. In integrated routing tests, this
		// acknowledgement is available. It is a simplification of
		// reality that still allows certain classes of tests to be
		// performed.
		if success {
			inFlightHtlcs++

			err := mc.ReportPaymentSuccess(pid, route)
			if err != nil {
				c.t.Fatal(err)
			}

			amtRemaining -= route.ReceiverAmt()

			// If the full amount has been paid, the payment is
			// successful and the control loop can be terminated.
			if amtRemaining == 0 {
				break
			}

			// Otherwise try to send the remaining amount.
			continue
		}

		// Failure, update mission control and retry.
		finalResult, err := mc.ReportPaymentFail(
			pid, route,
			getNodeIndex(route, htlcResult.failureSource),
			htlcResult.failure,
		)
		if err != nil {
			c.t.Fatal(err)
		}

		if finalResult != nil {
			break
		}
	}

	return attempts, nil
}

// getNodeIndex returns the zero-based index of the given node in the route.
func getNodeIndex(route *route.Route, failureSource route.Vertex) *int {
	if failureSource == route.SourcePubKey {
		idx := 0
		return &idx
	}

	for i, h := range route.Hops {
		if h.PubKeyBytes == failureSource {
			idx := i + 1
			return &idx
		}
	}
	return nil
}
<|endoftext|>