<commit_before>package utils

import (
	"bytes"
	"fmt"
	"io"
	"os/exec"
	"strings"
	"syscall"
)

// ExecCmd executes a command with args and returns its output as a string along
// with an error, if any
func ExecCmd(name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err := cmd.Run()
	if err != nil {
		return "", fmt.Errorf("`%v %v` failed: %v (%v)", name, strings.Join(args, " "), stderr.String(), err)
	}

	return stdout.String(), nil
}

// ExecCmdWithStdStreams execute a command with the specified standard streams.
func ExecCmdWithStdStreams(stdin io.Reader, stdout, stderr io.Writer, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = stdin
	cmd.Stdout = stdout
	cmd.Stderr = stderr

	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("`%v %v` failed: %v", name, strings.Join(args, " "), err)
	}

	return nil
}

// Prctl is a way to make the prctl linux syscall
func Prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) {
	_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0)
	if e1 != 0 {
		err = e1
	}
	return
}

// StatusToExitCode converts wait status code to an exit code
func StatusToExitCode(status int) int {
	return ((status) & 0xff00) >> 8
}
<commit_msg>Add a utility to run a pid in a systemd scope<commit_after>package utils

import (
	"bytes"
	"fmt"
	"io"
	"os/exec"
	"strings"
	"syscall"

	systemdDbus "github.com/coreos/go-systemd/dbus"
	"github.com/godbus/dbus"
)

// ExecCmd executes a command with args and returns its output as a string along
// with an error, if any
func ExecCmd(name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	err := cmd.Run()
	if err != nil {
		return "", fmt.Errorf("`%v %v` failed: %v (%v)", name, strings.Join(args, " "), stderr.String(), err)
	}

	return stdout.String(), nil
}

// ExecCmdWithStdStreams execute a command with the specified standard streams.
func ExecCmdWithStdStreams(stdin io.Reader, stdout, stderr io.Writer, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = stdin
	cmd.Stdout = stdout
	cmd.Stderr = stderr

	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("`%v %v` failed: %v", name, strings.Join(args, " "), err)
	}

	return nil
}

// Prctl is a way to make the prctl linux syscall
func Prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) {
	_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0)
	if e1 != 0 {
		err = e1
	}
	return
}

// StatusToExitCode converts wait status code to an exit code
func StatusToExitCode(status int) int {
	return ((status) & 0xff00) >> 8
}

// RunUnderSystemdScope adds the specified pid to a systemd scope
func RunUnderSystemdScope(pid int, slice string, unitName string) error {
	var properties []systemdDbus.Property
	conn, err := systemdDbus.New()
	if err != nil {
		return err
	}
	properties = append(properties, systemdDbus.PropSlice(slice))
	properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
	properties = append(properties, newProp("Delegate", true))
	properties = append(properties, newProp("DefaultDependencies", false))
	if _, err := conn.StartTransientUnit(unitName, "replace", properties, nil); err != nil {
		return err
	}
	return nil
}

func newProp(name string, units interface{}) systemdDbus.Property {
	return systemdDbus.Property{
		Name:  name,
		Value: dbus.MakeVariant(units),
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package cmd

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/TheThingsNetwork/ttn/api/handler"
	"github.com/TheThingsNetwork/ttn/ttnctl/util"
	"github.com/apex/log"
	"github.com/spf13/cobra"
)

var applicationsPayloadFunctionsSetCmd = &cobra.Command{
	Use:   "set [decoder/converter/validator/encoder] [file.js]",
	Short: "Set payload functions of an application",
	Long: `ttnctl pf set can be used to get or set payload functions of an application.
The functions are read from the supplied file or from STDIN.`,
	Example: `$ ttnctl applications pf set decoder
  INFO Discovering Handler...
  INFO Connecting with Handler...
function Decoder(bytes, port) {
  // Decode an uplink message from a buffer
  // (array) of bytes to an object of fields.
  var decoded = {};

  // if (port === 1) decoded.led = bytes[0];

  return decoded;
}
########## Write your Decoder here and end with Ctrl+D (EOF):
function Decoder(bytes, port) {
  var decoded = {};

  if (port === 1) decoded.led = bytes[0];

  return decoded;
}
  INFO Updated application                      AppID=test
`,
	Run: func(cmd *cobra.Command, args []string) {

		appID := util.GetAppID(ctx)

		conn, manager := util.GetHandlerManager(ctx, appID)
		defer conn.Close()

		app, err := manager.GetApplication(appID)
		if err != nil && strings.Contains(err.Error(), "not found") {
			app = &handler.Application{AppId: appID}
		} else if err != nil {
			ctx.WithError(err).Fatal("Could not get existing application.")
		}

		if len(args) == 0 {
			cmd.UsageFunc()(cmd)
			return
		}

		function := args[0]

		if len(args) == 2 {
			content, err := ioutil.ReadFile(args[1])
			if err != nil {
				ctx.WithError(err).Fatal("Could not read function file")
			}
			switch function {
			case "decoder":
				app.Decoder = string(content)
			case "converter":
				app.Converter = string(content)
			case "validator":
				app.Validator = string(content)
			case "encoder":
				app.Encoder = string(content)
			default:
				ctx.Fatalf("Function %s does not exist", function)
			}
		} else {
			switch function {
			case "decoder":
				fmt.Println(`function Decoder(bytes, port) {
  // Decode an uplink message from a buffer
  // (array) of bytes to an object of fields.
  var decoded = {};

  // if (port === 1) decoded.led = bytes[0];

  return decoded;
}
########## Write your Decoder here and end with Ctrl+D (EOF):`)
				app.Decoder = readFunction()
			case "converter":
				fmt.Println(`function Converter(decoded, port) {
  // Merge, split or otherwise
  // mutate decoded fields.
  var converted = decoded;

  // if (port === 1 && (converted.led === 0 || converted.led === 1)) {
  //   converted.led = Boolean(converted.led);
  // }

  return converted;
}
########## Write your Converter here and end with Ctrl+D (EOF):`)
				app.Converter = readFunction()
			case "validator":
				fmt.Println(`function Validator(converted, port) {
  // Return false if the decoded, converted
  // message is invalid and should be dropped.

  // if (port === 1 && typeof converted.led !== 'boolean') {
  //   return false;
  // }

  return true;
}
########## Write your Validator here and end with Ctrl+D (EOF):`)
				app.Validator = readFunction()
			case "encoder":
				fmt.Println(`function Encoder(object, port) {
  // Encode downlink messages sent as
  // object to an array or buffer of bytes.
  var bytes = [];

  // if (port === 1) bytes[0] = object.led ? 1 : 0;

  return bytes;
}
########## Write your Encoder here and end with Ctrl+D (EOF):`)
				app.Encoder = readFunction()
			default:
				ctx.Fatalf("Function %s does not exist", function)
			}
		}

		err = manager.SetApplication(app)
		if err != nil {
			ctx.WithError(err).Fatal("Could not update application")
		}

		ctx.WithFields(log.Fields{
			"AppID": appID,
		}).Infof("Updated application")
	},
}

func readFunction() string {
	content, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		ctx.WithError(err).Fatal("Could not read function from STDIN.")
	}
	return strings.TrimSpace(string(content))
}

func init() {
	applicationsPayloadFunctionsCmd.AddCommand(applicationsPayloadFunctionsSetCmd)
}
<commit_msg>Use braces<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package cmd

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/TheThingsNetwork/ttn/api/handler"
	"github.com/TheThingsNetwork/ttn/ttnctl/util"
	"github.com/apex/log"
	"github.com/spf13/cobra"
)

var applicationsPayloadFunctionsSetCmd = &cobra.Command{
	Use:   "set [decoder/converter/validator/encoder] [file.js]",
	Short: "Set payload functions of an application",
	Long: `ttnctl pf set can be used to get or set payload functions of an application.
The functions are read from the supplied file or from STDIN.`,
	Example: `$ ttnctl applications pf set decoder
  INFO Discovering Handler...
  INFO Connecting with Handler...
function Decoder(bytes, port) {
  // Decode an uplink message from a buffer
  // (array) of bytes to an object of fields.
  var decoded = {};

  // if (port === 1) {
  //   decoded.led = bytes[0];
  // }

  return decoded;
}
########## Write your Decoder here and end with Ctrl+D (EOF):
function Decoder(bytes, port) {
  var decoded = {};

  // if (port === 1) {
  //   decoded.led = bytes[0];
  // }

  return decoded;
}
  INFO Updated application                      AppID=test
`,
	Run: func(cmd *cobra.Command, args []string) {

		appID := util.GetAppID(ctx)

		conn, manager := util.GetHandlerManager(ctx, appID)
		defer conn.Close()

		app, err := manager.GetApplication(appID)
		if err != nil && strings.Contains(err.Error(), "not found") {
			app = &handler.Application{AppId: appID}
		} else if err != nil {
			ctx.WithError(err).Fatal("Could not get existing application.")
		}

		if len(args) == 0 {
			cmd.UsageFunc()(cmd)
			return
		}

		function := args[0]

		if len(args) == 2 {
			content, err := ioutil.ReadFile(args[1])
			if err != nil {
				ctx.WithError(err).Fatal("Could not read function file")
			}
			switch function {
			case "decoder":
				app.Decoder = string(content)
			case "converter":
				app.Converter = string(content)
			case "validator":
				app.Validator = string(content)
			case "encoder":
				app.Encoder = string(content)
			default:
				ctx.Fatalf("Function %s does not exist", function)
			}
		} else {
			switch function {
			case "decoder":
				fmt.Println(`function Decoder(bytes, port) {
  // Decode an uplink message from a buffer
  // (array) of bytes to an object of fields.
  var decoded = {};

  // if (port === 1) {
  //   decoded.led = bytes[0];
  // }

  return decoded;
}
########## Write your Decoder here and end with Ctrl+D (EOF):`)
				app.Decoder = readFunction()
			case "converter":
				fmt.Println(`function Converter(decoded, port) {
  // Merge, split or otherwise
  // mutate decoded fields.
  var converted = decoded;

  // if (port === 1 && (converted.led === 0 || converted.led === 1)) {
  //   converted.led = Boolean(converted.led);
  // }

  return converted;
}
########## Write your Converter here and end with Ctrl+D (EOF):`)
				app.Converter = readFunction()
			case "validator":
				fmt.Println(`function Validator(converted, port) {
  // Return false if the decoded, converted
  // message is invalid and should be dropped.

  // if (port === 1 && typeof converted.led !== 'boolean') {
  //   return false;
  // }

  return true;
}
########## Write your Validator here and end with Ctrl+D (EOF):`)
				app.Validator = readFunction()
			case "encoder":
				fmt.Println(`function Encoder(object, port) {
  // Encode downlink messages sent as
  // object to an array or buffer of bytes.
  var bytes = [];

  // if (port === 1) {
  //   bytes[0] = object.led ? 1 : 0;
  // }

  return bytes;
}
########## Write your Encoder here and end with Ctrl+D (EOF):`)
				app.Encoder = readFunction()
			default:
				ctx.Fatalf("Function %s does not exist", function)
			}
		}

		err = manager.SetApplication(app)
		if err != nil {
			ctx.WithError(err).Fatal("Could not update application")
		}

		ctx.WithFields(log.Fields{
			"AppID": appID,
		}).Infof("Updated application")
	},
}

func readFunction() string {
	content, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		ctx.WithError(err).Fatal("Could not read function from STDIN.")
	}
	return strings.TrimSpace(string(content))
}

func init() {
	applicationsPayloadFunctionsCmd.AddCommand(applicationsPayloadFunctionsSetCmd)
}
<|endoftext|>


<commit_before>package client

import (
	"errors"
	"fmt"
	"strings"

	"github.com/open-policy-agent/opa/ast"
)

var (
	// Currently rules should only access data.inventory
	validDataFields = map[string]bool{
		"inventory": true,
	}
)

func newRegoConformer(allowedDataFields []string) *regoConformer {
	allowed := make(map[string]bool)
	for _, v := range allowedDataFields {
		if !validDataFields[v] {
			continue
		}
		allowed[v] = true
	}
	return &regoConformer{allowedDataFields: allowed}
}

type regoConformer struct {
	allowedDataFields map[string]bool
}

// ensureRegoConformance rewrites the package path and ensures there is no access of `data`
// beyond the whitelisted bits. Note that this rewriting will currently modify the Rego to look
// potentially very different from the input, but it will still be functionally equivalent.
func (rc *regoConformer) ensureRegoConformance(kind, path, rego string) (string, error) {
	if rego == "" {
		return "", errors.New("Rego source code is empty")
	}
	module, err := ast.ParseModule(kind, rego)
	if err != nil {
		return "", err
	}
	if len(module.Imports) != 0 {
		return "", errors.New("Use of the `import` keyword is not allowed")
	}
	// Temporarily unset Package.Path to avoid triggering a "prohibited data field" error
	module.Package.Path = nil
	if err := rc.checkDataAccess(module); err != nil {
		return "", err
	}
	module.Package.Path, err = packageRef(path)
	if err != nil {
		return "", err
	}
	return module.String(), nil
}

// rewritePackage rewrites the package in a rego module
func rewritePackage(path, rego string) (string, error) {
	if rego == "" {
		return "", errors.New("Rego source code is empty")
	}
	module, err := ast.ParseModule(path, rego)
	if err != nil {
		return "", err
	}
	module.Package.Path, err = packageRef(path)
	if err != nil {
		return "", err
	}
	return module.String(), nil
}

// packageRef constructs a Ref to the provided package path string
func packageRef(path string) (ast.Ref, error) {
	pathParts, err := ast.ParseRef(path)
	if err != nil {
		return nil, err
	}
	packageRef := ast.Ref([]*ast.Term{ast.VarTerm("data")})
	return packageRef.Extend(pathParts), nil
}

func makeInvalidRootFieldErr(val ast.Value, allowed map[string]bool) error {
	var validFields []string
	for field := range allowed {
		validFields = append(validFields, field)
	}
	return fmt.Errorf("Invalid `data` field: %s. Valid fields are: %s", val.String(), strings.Join(validFields, ", "))
}

var _ error = Errors{}

type Errors []error

func (errs Errors) Error() string {
	s := make([]string, len(errs))
	for _, e := range errs {
		s = append(s, e.Error())
	}
	return strings.Join(s, "\n")
}

// checkDataAccess makes sure that data is only referenced in terms of valid subfields
func (rc *regoConformer) checkDataAccess(module *ast.Module) Errors {
	var errs Errors
	ast.WalkRefs(module, func(r ast.Ref) bool {
		if r.HasPrefix(ast.DefaultRootRef) {
			if len(r) < 2 {
				errs = append(errs, fmt.Errorf("All references to `data` must access a field of `data`: %s", r))
				return false
			}
			if !r[1].IsGround() {
				errs = append(errs, fmt.Errorf("Fields of `data` must be accessed with a literal value (e.g. `data.inventory`, not `data[var]`): %s", r))
				return false
			}
			v := r[1].Value
			if val, ok := v.(ast.String); !ok {
				errs = append(errs, makeInvalidRootFieldErr(v, rc.allowedDataFields))
				return false
			} else {
				if !rc.allowedDataFields[string(val)] {
					errs = append(errs, makeInvalidRootFieldErr(v, rc.allowedDataFields))
					return false
				}
			}
		}
		return false
	})

	if len(errs) > 0 {
		return errs
	}
	return nil
}

// rule name -> arity
type ruleArities map[string]int

// requireRules makes sure the listed rules are specified with the required arity
func requireRules(name, rego string, reqs ruleArities) error {
	module, err := ast.ParseModule(name, rego)
	if err != nil {
		return err
	}

	arities := make(ruleArities, len(module.Rules))
	for _, rule := range module.Rules {
		name := string(rule.Head.Name)
		arity, err := getRuleArity(rule)
		if err != nil {
			return err
		}
		arities[name] = arity
	}

	var errs Errors
	for name, arity := range reqs {
		actual, ok := arities[name]
		if !ok {
			errs = append(errs, fmt.Errorf("Missing required rule: %s", name))
			continue
		}
		if arity != actual {
			errs = append(errs, fmt.Errorf("Rule %s has arity %d, want %d", name, actual, arity))
		}
	}
	if len(errs) != 0 {
		return errs
	}

	return nil
}

// getRuleArity returns the arity of a rule, assuming only no variables, a single variable, or
// an array of variables
func getRuleArity(r *ast.Rule) (int, error) {
	t := r.Head.Key
	if t == nil {
		return 0, nil
	}
	switch v := t.Value.(type) {
	case ast.Var:
		return 1, nil
	case ast.Object:
		return 1, nil
	case ast.Array:
		errs := false
		for _, e := range v {
			if _, ok := e.Value.(ast.Var); !ok {
				// for multi-arity args, a dev may be building the review object in the head of the rule
				if _, ok := e.Value.(ast.Object); !ok {
					errs = true
				}
			}
		}
		if errs {
			return 0, fmt.Errorf("Invalid rule signature: only single variables or arrays of variables or objects allowed: %s", v.String())
		}
		return len(v), nil
	}
	return 0, fmt.Errorf("Invalid rule signature, only variables or arrays allowed: %s", t.String())
}
<commit_msg>Provide clearer error when access to the data document is disabled<commit_after>package client

import (
	"errors"
	"fmt"
	"strings"

	"github.com/open-policy-agent/opa/ast"
)

var (
	// Currently rules should only access data.inventory
	validDataFields = map[string]bool{
		"inventory": true,
	}
)

func newRegoConformer(allowedDataFields []string) *regoConformer {
	allowed := make(map[string]bool)
	for _, v := range allowedDataFields {
		if !validDataFields[v] {
			continue
		}
		allowed[v] = true
	}
	return &regoConformer{allowedDataFields: allowed}
}

type regoConformer struct {
	allowedDataFields map[string]bool
}

// ensureRegoConformance rewrites the package path and ensures there is no access of `data`
// beyond the whitelisted bits. Note that this rewriting will currently modify the Rego to look
// potentially very different from the input, but it will still be functionally equivalent.
func (rc *regoConformer) ensureRegoConformance(kind, path, rego string) (string, error) {
	if rego == "" {
		return "", errors.New("Rego source code is empty")
	}
	module, err := ast.ParseModule(kind, rego)
	if err != nil {
		return "", err
	}
	if len(module.Imports) != 0 {
		return "", errors.New("Use of the `import` keyword is not allowed")
	}
	// Temporarily unset Package.Path to avoid triggering a "prohibited data field" error
	module.Package.Path = nil
	if err := rc.checkDataAccess(module); err != nil {
		return "", err
	}
	module.Package.Path, err = packageRef(path)
	if err != nil {
		return "", err
	}
	return module.String(), nil
}

// rewritePackage rewrites the package in a rego module
func rewritePackage(path, rego string) (string, error) {
	if rego == "" {
		return "", errors.New("Rego source code is empty")
	}
	module, err := ast.ParseModule(path, rego)
	if err != nil {
		return "", err
	}
	module.Package.Path, err = packageRef(path)
	if err != nil {
		return "", err
	}
	return module.String(), nil
}

// packageRef constructs a Ref to the provided package path string
func packageRef(path string) (ast.Ref, error) {
	pathParts, err := ast.ParseRef(path)
	if err != nil {
		return nil, err
	}
	packageRef := ast.Ref([]*ast.Term{ast.VarTerm("data")})
	return packageRef.Extend(pathParts), nil
}

func makeInvalidRootFieldErr(val ast.Value, allowed map[string]bool) error {
	if len(allowed) == 0 {
		return fmt.Errorf("Template is attempting to access `data.%s`. Access to the data document is disabled", val.String())
	}
	var validFields []string
	for field := range allowed {
		validFields = append(validFields, field)
	}
	return fmt.Errorf("Invalid `data` field: %s. Valid fields are: %s", val.String(), strings.Join(validFields, ", "))
}

var _ error = Errors{}

type Errors []error

func (errs Errors) Error() string {
	s := make([]string, len(errs))
	for _, e := range errs {
		s = append(s, e.Error())
	}
	return strings.Join(s, "\n")
}

// checkDataAccess makes sure that data is only referenced in terms of valid subfields
func (rc *regoConformer) checkDataAccess(module *ast.Module) Errors {
	var errs Errors
	ast.WalkRefs(module, func(r ast.Ref) bool {
		if r.HasPrefix(ast.DefaultRootRef) {
			if len(r) < 2 {
				errs = append(errs, fmt.Errorf("All references to `data` must access a field of `data`: %s", r))
				return false
			}
			if !r[1].IsGround() {
				errs = append(errs, fmt.Errorf("Fields of `data` must be accessed with a literal value (e.g. `data.inventory`, not `data[var]`): %s", r))
				return false
			}
			v := r[1].Value
			if val, ok := v.(ast.String); !ok {
				errs = append(errs, makeInvalidRootFieldErr(v, rc.allowedDataFields))
				return false
			} else {
				if !rc.allowedDataFields[string(val)] {
					errs = append(errs, makeInvalidRootFieldErr(v, rc.allowedDataFields))
					return false
				}
			}
		}
		return false
	})

	if len(errs) > 0 {
		return errs
	}
	return nil
}

// rule name -> arity
type ruleArities map[string]int

// requireRules makes sure the listed rules are specified with the required arity
func requireRules(name, rego string, reqs ruleArities) error {
	module, err := ast.ParseModule(name, rego)
	if err != nil {
		return err
	}

	arities := make(ruleArities, len(module.Rules))
	for _, rule := range module.Rules {
		name := string(rule.Head.Name)
		arity, err := getRuleArity(rule)
		if err != nil {
			return err
		}
		arities[name] = arity
	}

	var errs Errors
	for name, arity := range reqs {
		actual, ok := arities[name]
		if !ok {
			errs = append(errs, fmt.Errorf("Missing required rule: %s", name))
			continue
		}
		if arity != actual {
			errs = append(errs, fmt.Errorf("Rule %s has arity %d, want %d", name, actual, arity))
		}
	}
	if len(errs) != 0 {
		return errs
	}

	return nil
}

// getRuleArity returns the arity of a rule, assuming only no variables, a single variable, or
// an array of variables
func getRuleArity(r *ast.Rule) (int, error) {
	t := r.Head.Key
	if t == nil {
		return 0, nil
	}
	switch v := t.Value.(type) {
	case ast.Var:
		return 1, nil
	case ast.Object:
		return 1, nil
	case ast.Array:
		errs := false
		for _, e := range v {
			if _, ok := e.Value.(ast.Var); !ok {
				// for multi-arity args, a dev may be building the review object in the head of the rule
				if _, ok := e.Value.(ast.Object); !ok {
					errs = true
				}
			}
		}
		if errs {
			return 0, fmt.Errorf("Invalid rule signature: only single variables or arrays of variables or objects allowed: %s", v.String())
		}
		return len(v), nil
	}
	return 0, fmt.Errorf("Invalid rule signature, only variables or arrays allowed: %s", t.String())
}
<|endoftext|>


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

package leif

import (
	"context"
	"net/http"
	"reflect"
	"testing"

	"github.com/GoogleCloudPlatform/devrel-services/leif/githubservices"
	"github.com/google/go-github/github"
)

func TestTrackOwner(t *testing.T) {
	tests := []struct {
		name       string
		corpus     Corpus
		ownerName  string
		mockError  error
		wantCorpus Corpus
		wantOwner  *Owner
		wantErr    bool
	}{
		{
			name:      "Correctly tracks an owner",
			corpus:    Corpus{},
			ownerName: "someOwner",
			mockError: nil,
			wantCorpus: Corpus{
				watchedOwners: []*Owner{&Owner{name: "someOwner"}},
			},
			wantOwner: &Owner{name: "someOwner"},
			wantErr:   false,
		},
		{
			name:      "Does not track an owner that does not exist",
			corpus:    Corpus{},
			ownerName: "someOwner",
			mockError: &github.ErrorResponse{
				Response: &http.Response{
					StatusCode: 404,
					Status:     "404 Not Found",
					Request:    &http.Request{},
				},
				Message: "GH error",
			},
			wantCorpus: Corpus{},
			wantOwner:  nil,
			wantErr:    true,
		},
		{
			name: "Does not re-track an already tracked owner",
			corpus: Corpus{
				watchedOwners: []*Owner{&Owner{name: "someOwner"}},
			},
			ownerName: "someOwner",
			mockError: nil,
			wantCorpus: Corpus{
				watchedOwners: []*Owner{&Owner{name: "someOwner"}},
			},
			wantOwner: &Owner{name: "someOwner"},
			wantErr:   false,
		},
		{
			name: "Tracks an owner when tracking other owners",
			corpus: Corpus{
				watchedOwners: []*Owner{&Owner{name: "someOwner"}},
			},
			ownerName: "anotherOwner",
			mockError: nil,
			wantCorpus: Corpus{
				watchedOwners: []*Owner{
					&Owner{name: "anotherOwner"},
					&Owner{name: "someOwner"},
				},
			},
			wantOwner: &Owner{name: "anotherOwner"},
			wantErr:   false,
		},
	}
	for _, test := range tests {
		mock := new(githubservices.MockGithubUserService)

		mock.User = test.ownerName
		mock.Error = test.mockError
		client := githubservices.NewClient(nil, nil, mock)

		gotCorpus := test.corpus
		gotOwner, gotErr := gotCorpus.trackOwner(context.Background(), test.ownerName, &client)

		if !reflect.DeepEqual(gotCorpus, test.wantCorpus) {
			t.Errorf("%v did not pass.\n\tWant corpus:\t%v\n\tGot corpus:\t%v", test.name, test.wantCorpus, gotCorpus)
		}

		if !reflect.DeepEqual(gotOwner, test.wantOwner) {
			t.Errorf("%v did not pass.\n\tWant owner:\t%v\n\tGot owner:\t%v", test.name, test.wantOwner, gotOwner)
		}

		inCorpus := false
		for _, o := range gotCorpus.watchedOwners {
			if o == gotOwner {
				inCorpus = true
				break
			}
		}

		if gotOwner != nil && !inCorpus {
			t.Errorf("%v did not pass.\n\tReturned owner is not in corpus", test.name)
		}

		if (gotErr != nil && !test.wantErr) || (gotErr == nil && test.wantErr) {
			t.Errorf("%v did not pass.\n\tWant Err: %v \n\tGot Err: %v", test.name, test.wantErr, gotErr)
		}
	}
}

func TestTrackRepo(t *testing.T) {
	tests := []struct {
		name          string
		corpus        Corpus
		ownerName     string
		repoName      string
		mockUserError error
		mockRepoError error
		wantCorpus    Corpus
		wantErr       bool
	}{
		{
			name:          "Correctly tracks a repo",
			corpus:        Corpus{},
			ownerName:     "someOwner",
			repoName:      "someRepo",
			mockUserError: nil,
			mockRepoError: nil,
			wantCorpus: Corpus{
				watchedOwners: []*Owner{
					&Owner{
						name:  "someOwner",
						Repos: []*Repository{&Repository{name: "someRepo"}},
					}},
			},
			wantErr: false,
		},
		{
			name:          "Correctly tracks a repo on existing owner",
			corpus:        Corpus{watchedOwners: []*Owner{&Owner{name: "someOwner"}}},
			ownerName:     "someOwner",
			repoName:      "someRepo",
			mockUserError: nil,
			mockRepoError: nil,
			wantCorpus: Corpus{
				watchedOwners: []*Owner{
					&Owner{
						name:  "someOwner",
						Repos: []*Repository{&Repository{name: "someRepo"}},
					}},
			},
			wantErr: false,
		},
		{
			name: "Correctly tracks a repo on existing owner with repos",
			corpus: Corpus{watchedOwners: []*Owner{&Owner{
				name:  "someOwner",
				Repos: []*Repository{&Repository{name: "aRepo"}},
			}}},
			ownerName:     "someOwner",
			repoName:      "someRepo",
			mockUserError: nil,
			mockRepoError: nil,
			wantCorpus: Corpus{
				watchedOwners: []*Owner{
					&Owner{
						name:  "someOwner",
						Repos: []*Repository{&Repository{name: "aRepo"}, &Repository{name: "someRepo"}},
					}},
			},
			wantErr: false,
		},
		{
			name:      "Errors if owner does not exist",
			corpus:    Corpus{},
			ownerName: "someOwner",
			repoName:  "someRepo",
			mockUserError: &github.ErrorResponse{
				Response: &http.Response{
					StatusCode: 404,
					Status:     "404 Not Found",
					Request:    &http.Request{},
				},
				Message: "GH error",
			},
			mockRepoError: nil,
			wantCorpus:    Corpus{},
			wantErr:       true,
		},
		{
			name:          "Errors if repo does not exist",
			corpus:        Corpus{watchedOwners: []*Owner{&Owner{name: "someOwner"}}},
			ownerName:     "someOwner",
			repoName:      "someRepo",
			mockUserError: nil,
			mockRepoError: &github.ErrorResponse{
				Response: &http.Response{
					StatusCode: 404,
					Status:     "404 Not Found",
					Request:    &http.Request{},
				},
				Message: "GH error",
			},
			wantCorpus: Corpus{watchedOwners: []*Owner{&Owner{name: "someOwner"}}},
			wantErr:    true,
		},
	}
	for _, test := range tests {
		repoMock := new(githubservices.MockGithubRepositoryService)
		repoMock.Owner = test.ownerName
		repoMock.Error = test.mockRepoError

		userMock := new(githubservices.MockGithubUserService)
		userMock.User = test.ownerName
		userMock.Error = test.mockUserError

		client := githubservices.NewClient(nil, repoMock, userMock)

		gotCorpus := test.corpus
		gotErr := gotCorpus.TrackRepo(context.Background(), test.ownerName, test.repoName, &client)

		if !reflect.DeepEqual(gotCorpus, test.wantCorpus) {
			t.Errorf("%v did not pass.\n\tWant corpus:\t%v\n\tGot corpus:\t%v", test.name, test.wantCorpus, gotCorpus)
		}

		if (gotErr != nil && !test.wantErr) || (gotErr == nil && test.wantErr) {
			t.Errorf("%v did not pass.\n\tWant Err: %v \n\tGot Err: %v", test.name, test.wantErr, gotErr)
		}
	}
}
<commit_msg>test(leif): fix lock copying in tests (#102)<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package leif

import (
	"context"
	"net/http"
	"reflect"
	"testing"

	"github.com/GoogleCloudPlatform/devrel-services/leif/githubservices"
	"github.com/google/go-github/github"
)

func TestTrackOwner(t *testing.T) {
	tests := []struct {
		name       string
		currOwners []*Owner
		ownerName  string
		mockError  error
		wantOwners []*Owner
		wantOwner  *Owner
		wantErr    bool
	}{
		{
			name:       "Correctly tracks an owner",
			currOwners: nil,
			ownerName:  "someOwner",
			mockError:  nil,
			wantOwners: []*Owner{&Owner{name: "someOwner"}},
			wantOwner:  &Owner{name: "someOwner"},
			wantErr:    false,
		},
		{
			name:       "Does not track an owner that does not exist",
			currOwners: nil,
			ownerName:  "someOwner",
			mockError: &github.ErrorResponse{
				Response: &http.Response{
					StatusCode: 404,
					Status:     "404 Not Found",
					Request:    &http.Request{},
				},
				Message: "GH error",
			},
			wantOwners: nil,
			wantOwner:  nil,
			wantErr:    true,
		},
		{
			name:       "Does not re-track an already tracked owner",
			currOwners: []*Owner{&Owner{name: "someOwner"}},
			ownerName:  "someOwner",
			mockError:  nil,
			wantOwners: []*Owner{&Owner{name: "someOwner"}},
			wantOwner:  &Owner{name: "someOwner"},
			wantErr:    false,
		},
		{
			name:       "Tracks an owner when tracking other owners",
			currOwners: []*Owner{&Owner{name: "someOwner"}},
			ownerName:  "anotherOwner",
			mockError:  nil,
			wantOwners: []*Owner{
				&Owner{name: "anotherOwner"},
				&Owner{name: "someOwner"},
			},
			wantOwner: &Owner{name: "anotherOwner"},
			wantErr:   false,
		},
	}
	for _, test := range tests {
		mock := new(githubservices.MockGithubUserService)

		mock.User = test.ownerName
		mock.Error = test.mockError
		client := githubservices.NewClient(nil, nil, mock)

		gotCorpus := Corpus{watchedOwners: test.currOwners}
		gotOwner, gotErr := gotCorpus.trackOwner(context.Background(), test.ownerName, &client)

		if !reflect.DeepEqual(gotCorpus.watchedOwners, test.wantOwners) {
			t.Errorf("%v did not pass.\n\tWant corpus:\t%v\n\tGot corpus:\t%v", test.name, test.wantOwners, gotCorpus.watchedOwners)
		}

		if !reflect.DeepEqual(gotOwner, test.wantOwner) {
			t.Errorf("%v did not pass.\n\tWant owner:\t%v\n\tGot owner:\t%v", test.name, test.wantOwner, gotOwner)
		}

		inCorpus := false
		for _, o := range gotCorpus.watchedOwners {
			if o == gotOwner {
				inCorpus = true
				break
			}
		}

		if gotOwner != nil && !inCorpus {
			t.Errorf("%v did not pass.\n\tReturned owner is not in corpus", test.name)
		}

		if (gotErr != nil && !test.wantErr) || (gotErr == nil && test.wantErr) {
			t.Errorf("%v did not pass.\n\tWant Err: %v \n\tGot Err: %v", test.name, test.wantErr, gotErr)
		}
	}
}

func TestTrackRepo(t *testing.T) {
	tests := []struct {
		name          string
		currOwners    []*Owner
		ownerName     string
		repoName      string
		mockUserError error
		mockRepoError error
		wantOwners    []*Owner
		wantErr       bool
	}{
		{
			name:          "Correctly tracks a repo",
			currOwners:    nil,
			ownerName:     "someOwner",
			repoName:      "someRepo",
			mockUserError: nil,
			mockRepoError: nil,
			wantOwners: []*Owner{
				&Owner{
					name:  "someOwner",
					Repos: []*Repository{&Repository{name: "someRepo"}},
				},
			},
			wantErr: false,
		},
		{
			name:          "Correctly tracks a repo on existing owner",
			currOwners:    []*Owner{&Owner{name: "someOwner"}},
			ownerName:     "someOwner",
			repoName:      "someRepo",
			mockUserError: nil,
			mockRepoError: nil,
			wantOwners: []*Owner{
				&Owner{
					name:  "someOwner",
					Repos: []*Repository{&Repository{name: "someRepo"}},
				},
			},
			wantErr: false,
		},
		{
			name: "Correctly tracks a repo on existing owner with repos",
			currOwners: []*Owner{&Owner{
				name:  "someOwner",
				Repos: []*Repository{&Repository{name: "aRepo"}},
			}},
			ownerName:     "someOwner",
			repoName:      "someRepo",
			mockUserError: nil,
			mockRepoError: nil,
			wantOwners: []*Owner{
				&Owner{
					name:  "someOwner",
					Repos: []*Repository{&Repository{name: "aRepo"}, &Repository{name: "someRepo"}},
				},
			},
			wantErr: false,
		},
		{
			name:       "Errors if owner does not exist",
			currOwners: nil,
			ownerName:  "someOwner",
			repoName:   "someRepo",
			mockUserError: &github.ErrorResponse{
				Response: &http.Response{
					StatusCode: 404,
					Status:     "404 Not Found",
					Request:    &http.Request{},
				},
				Message: "GH error",
			},
			mockRepoError: nil,
			wantOwners:    nil,
			wantErr:       true,
		},
		{
			name:          "Errors if repo does not exist",
			currOwners:    []*Owner{&Owner{name: "someOwner"}},
			ownerName:     "someOwner",
			repoName:      "someRepo",
			mockUserError: nil,
			mockRepoError: &github.ErrorResponse{
				Response: &http.Response{
					StatusCode: 404,
					Status:     "404 Not Found",
					Request:    &http.Request{},
				},
				Message: "GH error",
			},
			wantOwners: []*Owner{&Owner{name: "someOwner"}},
			wantErr:    true,
		},
	}
	for _, test := range tests {
		repoMock := new(githubservices.MockGithubRepositoryService)
		repoMock.Owner = test.ownerName
		repoMock.Error = test.mockRepoError

		userMock := new(githubservices.MockGithubUserService)
		userMock.User = test.ownerName
		userMock.Error = test.mockUserError

		client := githubservices.NewClient(nil, repoMock, userMock)

		gotCorpus := Corpus{watchedOwners: test.currOwners}
		gotErr := gotCorpus.TrackRepo(context.Background(), test.ownerName, test.repoName, &client)

		if !reflect.DeepEqual(gotCorpus.watchedOwners, test.wantOwners) {
			t.Errorf("%v did not pass.\n\tWant owners:\t%v\n\tGot owners:\t%v", test.name, test.wantOwners, gotCorpus.watchedOwners)
		}

		if (gotErr != nil && !test.wantErr) || (gotErr == nil && test.wantErr) {
			t.Errorf("%v did not pass.\n\tWant Err: %v \n\tGot Err: %v", test.name, test.wantErr, gotErr)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 The Syncthing Authors.
//
// Adapted from https://github.com/jackpal/Taipei-Torrent/blob/dd88a8bfac6431c01d959ce3c745e74b8a911793/IGD.go
// Copyright (c) 2010 Jack Palevich (https://github.com/jackpal/Taipei-Torrent/blob/dd88a8bfac6431c01d959ce3c745e74b8a911793/LICENSE)
//
// 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.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "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 THE COPYRIGHT
// OWNER OR CONTRIBUTORS 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.
//

package upnp

import (
	"encoding/xml"
	"fmt"
	"net"
	"time"

	"github.com/syncthing/syncthing/lib/nat"
)

// An IGDService is a specific service provided by an IGD.
type IGDService struct {
	ID  string
	URL string
	URN string
}

// AddPortMapping adds a port mapping to the specified IGD service.
func (s *IGDService) AddPortMapping(localIPAddress net.IP, protocol nat.Protocol, internalPort, externalPort int, description string, duration time.Duration) error {
	tpl := `<u:AddPortMapping xmlns:u="%s">
	<NewRemoteHost></NewRemoteHost>
	<NewExternalPort>%d</NewExternalPort>
	<NewProtocol>%s</NewProtocol>
	<NewInternalPort>%d</NewInternalPort>
	<NewInternalClient>%s</NewInternalClient>
	<NewEnabled>1</NewEnabled>
	<NewPortMappingDescription>%s</NewPortMappingDescription>
	<NewLeaseDuration>%d</NewLeaseDuration>
	</u:AddPortMapping>`
	body := fmt.Sprintf(tpl, s.URN, externalPort, protocol, internalPort, localIPAddress, description, duration/time.Second)

	response, err := soapRequest(s.URL, s.URN, "AddPortMapping", body)
	if err != nil && duration > 0 {
		// Try to repair error code 725 - OnlyPermanentLeasesSupported
		envelope := &soapErrorResponse{}
		if unmarshalErr := xml.Unmarshal(response, envelope); unmarshalErr != nil {
			return unmarshalErr
		}
		if envelope.ErrorCode == 725 {
			return s.AddPortMapping(localIPAddress, protocol, externalPort, internalPort, description, 0)
		}
	}

	return err
}

// DeletePortMapping deletes a port mapping from the specified IGD service.
func (s *IGDService) DeletePortMapping(protocol nat.Protocol, externalPort int) error {
	tpl := `<u:DeletePortMapping xmlns:u="%s">
	<NewRemoteHost></NewRemoteHost>
	<NewExternalPort>%d</NewExternalPort>
	<NewProtocol>%s</NewProtocol>
	</u:DeletePortMapping>`
	body := fmt.Sprintf(tpl, s.URN, externalPort, protocol)

	_, err := soapRequest(s.URL, s.URN, "DeletePortMapping", body)
	return err
}

// GetExternalIPAddress queries the IGD service for its external IP address.
// Returns nil if the external IP address is invalid or undefined, along with
// any relevant errors
func (s *IGDService) GetExternalIPAddress() (net.IP, error) {
	tpl := `<u:GetExternalIPAddress xmlns:u="%s" />`

	body := fmt.Sprintf(tpl, s.URN)

	response, err := soapRequest(s.URL, s.URN, "GetExternalIPAddress", body)

	if err != nil {
		return nil, err
	}

	envelope := &soapGetExternalIPAddressResponseEnvelope{}
	err = xml.Unmarshal(response, envelope)
	if err != nil {
		return nil, err
	}

	result := net.ParseIP(envelope.Body.GetExternalIPAddressResponse.NewExternalIPAddress)

	return result, nil
}
<commit_msg>lib/upnp: Wrong order of internal/external port after OnlyPermanentLeasesSupported (fixes #3924)<commit_after>// Copyright (C) 2016 The Syncthing Authors.
//
// Adapted from https://github.com/jackpal/Taipei-Torrent/blob/dd88a8bfac6431c01d959ce3c745e74b8a911793/IGD.go
// Copyright (c) 2010 Jack Palevich (https://github.com/jackpal/Taipei-Torrent/blob/dd88a8bfac6431c01d959ce3c745e74b8a911793/LICENSE)
//
// 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.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "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 THE COPYRIGHT
// OWNER OR CONTRIBUTORS 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.
//

package upnp

import (
	"encoding/xml"
	"fmt"
	"net"
	"time"

	"github.com/syncthing/syncthing/lib/nat"
)

// An IGDService is a specific service provided by an IGD.
type IGDService struct {
	ID  string
	URL string
	URN string
}

// AddPortMapping adds a port mapping to the specified IGD service.
func (s *IGDService) AddPortMapping(localIPAddress net.IP, protocol nat.Protocol, internalPort, externalPort int, description string, duration time.Duration) error {
	tpl := `<u:AddPortMapping xmlns:u="%s">
	<NewRemoteHost></NewRemoteHost>
	<NewExternalPort>%d</NewExternalPort>
	<NewProtocol>%s</NewProtocol>
	<NewInternalPort>%d</NewInternalPort>
	<NewInternalClient>%s</NewInternalClient>
	<NewEnabled>1</NewEnabled>
	<NewPortMappingDescription>%s</NewPortMappingDescription>
	<NewLeaseDuration>%d</NewLeaseDuration>
	</u:AddPortMapping>`
	body := fmt.Sprintf(tpl, s.URN, externalPort, protocol, internalPort, localIPAddress, description, duration/time.Second)

	response, err := soapRequest(s.URL, s.URN, "AddPortMapping", body)
	if err != nil && duration > 0 {
		// Try to repair error code 725 - OnlyPermanentLeasesSupported
		envelope := &soapErrorResponse{}
		if unmarshalErr := xml.Unmarshal(response, envelope); unmarshalErr != nil {
			return unmarshalErr
		}
		if envelope.ErrorCode == 725 {
			return s.AddPortMapping(localIPAddress, protocol, internalPort, externalPort, description, 0)
		}
	}

	return err
}

// DeletePortMapping deletes a port mapping from the specified IGD service.
func (s *IGDService) DeletePortMapping(protocol nat.Protocol, externalPort int) error {
	tpl := `<u:DeletePortMapping xmlns:u="%s">
	<NewRemoteHost></NewRemoteHost>
	<NewExternalPort>%d</NewExternalPort>
	<NewProtocol>%s</NewProtocol>
	</u:DeletePortMapping>`
	body := fmt.Sprintf(tpl, s.URN, externalPort, protocol)

	_, err := soapRequest(s.URL, s.URN, "DeletePortMapping", body)
	return err
}

// GetExternalIPAddress queries the IGD service for its external IP address.
// Returns nil if the external IP address is invalid or undefined, along with
// any relevant errors
func (s *IGDService) GetExternalIPAddress() (net.IP, error) {
	tpl := `<u:GetExternalIPAddress xmlns:u="%s" />`

	body := fmt.Sprintf(tpl, s.URN)

	response, err := soapRequest(s.URL, s.URN, "GetExternalIPAddress", body)

	if err != nil {
		return nil, err
	}

	envelope := &soapGetExternalIPAddressResponseEnvelope{}
	err = xml.Unmarshal(response, envelope)
	if err != nil {
		return nil, err
	}

	result := net.ParseIP(envelope.Body.GetExternalIPAddressResponse.NewExternalIPAddress)

	return result, nil
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2015, Mark Bucciarelli <mkbucc@gmail.com>
*/

package vufs

import (
	"fmt"
	. "github.com/smartystreets/goconvey/convey"
	"io/ioutil"
	"io"
	"net"
	"os"
	"testing"

	"github.com/lionkov/go9p/p"
	"github.com/lionkov/go9p/p/clnt"
)

const (
	port               = ":5000"
	messageSizeInBytes = 8192
)

func initfs(rootdir string, mode os.FileMode, userdata string) {
	os.Mkdir(rootdir, mode)
	os.Mkdir(rootdir+"/adm", 0700)
	ioutil.WriteFile(rootdir+"/adm/users", []byte(userdata), 0600)
	ioutil.WriteFile(rootdir+"/adm/"+uidgidFile, []byte("1:users:adm:adm\n"), 0600)
}

func runserver(rootdir, port string) {

	var err error
	fs := New(rootdir)
	fs.Id = "vufs"
	fs.Upool, err = NewVusers(rootdir)
	if err != nil {
		panic(err)
	}
	//fs.Debuglevel = 1

	fs.Start(fs)

	go func() {
		err = fs.StartNetListener("tcp", port)
		if err != nil {
			panic(err)
		}
	}()

	// Make sure runserver is listening before returning.
	var conn net.Conn
	for i := 0; i < 16; i++ {
		if conn, err = net.Dial("tcp", port); err == nil {
			fmt.Printf("Server is up, got connnection %+v\n", conn)
			conn.Close()
			break
		}
	}
	if err != nil {
		panic("couldn't connect to runserver after 15 tries")
	}
}

func listDir(path string, user p.User) ([]*p.Dir, error) {

	client, err := clnt.Mount("tcp", port,  "/", messageSizeInBytes, user)
	if err != nil {
		return nil, err
	}
	defer client.Unmount()

	// file modes: ../../lionkov/go9p/p/p9.go:65,74
	file, err := client.FOpen("/", p.OREAD)
	if err != nil && err != io.EOF  {
		return nil, err
	}

	// returns an array of Dir instances: ../../lionkov/go9p/p/clnt/read.go:88
	d, err := file.Readdir(-1)
	if err != nil  {
		return nil, err
	}

	return d, nil

}

func TestServer(t *testing.T) {

	rootdir := "./tmpfs"

	initfs(rootdir, 0755, "1:adm:adm\n2:mark:mark\n")

	runserver(rootdir, port)

	mark := &vUser{
		id:      2,
		name:    "mark",
		members: []p.User{},
		groups:  []p.Group{}}

	hugo := &vUser{
		id:      3,
		name:    "hugo",
		members: []p.User{},
		groups:  []p.Group{}}

	Convey("Given a vufs rooted in a directory and a client", t, func() {
		var d []*p.Dir

		Convey("A valid user can list the one file in a 0755 root directory", func() {
			os.Chmod(rootdir, 0755)
			dirs, err := listDir("/", mark)
			So(err, ShouldBeNil)
			So(len(dirs), ShouldEqual, 1)
		})

		Convey("An invalid user cannot list root directory", func() {
			os.Chmod(rootdir, 0777)
			_, err := listDir(".", hugo)
			So(err, ShouldNotBeNil)
		})

		Convey("A valid user without permissions cannot list files", func() {
			err := os.Chmod(rootdir, 0700)
			So(err, ShouldBeNil)
			d, err = listDir(".", mark)
			So(err, ShouldNotBeNil)
		})
	})

	os.RemoveAll(rootdir)

}
<commit_msg>Convert tests to share a single connection and got the following race:<commit_after>/*
   Copyright (c) 2015, Mark Bucciarelli <mkbucc@gmail.com>
*/

package vufs

import (
	"fmt"
	. "github.com/smartystreets/goconvey/convey"
	"io/ioutil"
	"io"
	"net"
	"os"
	"testing"

	"github.com/lionkov/go9p/p"
	"github.com/lionkov/go9p/p/clnt"
)

const (
	port               = ":5000"
	messageSizeInBytes = 8192
)

// Initialize file system as:
//
//          /
//           |
//           +-- adm/            --rwx------ adm adm
//                   |
//                   +-- users     --rw------- adm adm
//
//         Notes:
//
//          a.    Users shown are virtual ones, not ones on disk.
//
//          b.    If no ownership specified (in .uidgid), it defaults to adm adm.
//
//
func initfs(rootdir string, mode os.FileMode, userdata string) {
	os.Mkdir(rootdir, mode)
	os.Mkdir(rootdir+"/adm", 0700)
	ioutil.WriteFile(rootdir+"/adm/users", []byte(userdata), 0600)
	//ioutil.WriteFile(rootdir+"/adm/"+uidgidFile, []byte("1:users:adm:adm\n"), 0600)
}

func runserver(rootdir, port string) net.Conn {

	var err error
	fs := New(rootdir)
	fs.Id = "vufs"
	fs.Upool, err = NewVusers(rootdir)
	if err != nil {
		panic(err)
	}
	//fs.Debuglevel = 1

	fs.Start(fs)

	go func() {
		err = fs.StartNetListener("tcp", port)
		if err != nil {
			panic(err)
		}
	}()

	// Make sure runserver is listening before returning.
	var conn net.Conn
	for i := 0; i < 16; i++ {
		if conn, err = net.Dial("tcp", port); err == nil {
			fmt.Printf("Server is up, got connnection %+v\n", conn)
			break
		}
	}
	if err != nil {
		panic("couldn't connect to runserver after 15 tries")
	}

	return conn
}

func listDir(conn net.Conn, path string, user p.User) ([]*p.Dir, error) {

	client, err := clnt.MountConn(conn,  "/", messageSizeInBytes, user)
	if err != nil {
		return nil, err
	}
	defer client.Unmount()

	// file modes: ../../lionkov/go9p/p/p9.go:65,74
	file, err := client.FOpen("/", p.OREAD)
	if err != nil && err != io.EOF  {
		return nil, err
	}

	// returns an array of Dir instances: ../../lionkov/go9p/p/clnt/read.go:88
	d, err := file.Readdir(-1)
	if err != nil  {
		return nil, err
	}

	return d, nil

}

func TestServer(t *testing.T) {

	rootdir := "./tmpfs"

	initfs(rootdir, 0755, "1:adm:adm\n2:mark:mark\n")

	// Kee
	conn := runserver(rootdir, port)

	adm := &vUser{
		id:      1,
		name:    "adm",
		members: []p.User{},
		groups:  []p.Group{}}

	mark := &vUser{
		id:      2,
		name:    "mark",
		members: []p.User{},
		groups:  []p.Group{}}

	hugo := &vUser{
		id:      3,
		name:    "hugo",
		members: []p.User{},
		groups:  []p.Group{}}

	Convey("Given a vufs rooted in a directory and a client", t, func() {
		var d []*p.Dir

		Convey("/adm/users is 0600 adm, adm", func() {
			dirs, err := listDir(conn, "/", adm)
			So(err, ShouldBeNil)
			So(len(dirs), ShouldEqual, 1)
		})

		Convey("A valid user can list the one file in a 0755 root directory", func() {
			os.Chmod(rootdir, 0755)
			dirs, err := listDir(conn, "/", mark)
			So(err, ShouldBeNil)
			So(len(dirs), ShouldEqual, 1)
		})

		Convey("An invalid user cannot list root directory", func() {
			os.Chmod(rootdir, 0777)
			_, err := listDir(conn, ".", hugo)
			So(err.Error(), ShouldEqual, "unknown user: 22: 0")
		})

		Convey("A valid user without permissions cannot list files", func() {
			err := os.Chmod(rootdir, 0700)
			So(err, ShouldBeNil)
			d, err = listDir(conn, ".", mark)
			So(err, ShouldNotBeNil)
		})

	})

	os.RemoveAll(rootdir)
	conn.Close()

}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Boise State University All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"io"
	"log"
	"net"
	"net/http"
	"strings"
)

// isWebsocket inspects an incoming request and determines if it is for a
// websocket.
func isWebsocket(r *http.Request) bool {
	upgrade := false
	for _, h := range r.Header["Connection"] {
		if strings.Index(strings.ToLower(h), "upgrade") >= 0 {
			upgrade = true
			break
		}
	}
	if !upgrade {
		return false
	}

	for _, h := range r.Header["Upgrade"] {
		if strings.Index(strings.ToLower(h), "websocket") >= 0 {
			return true
		}
	}
	return false
}

// websocketProxy handles websocket requests for reverse proxy calls.
// Written by @bradfitz.  See:
//
// https://groups.google.com/forum/#!msg/golang-nuts/KBx9pDlvFOc/QC5v-uC5UOgJ
func websocketProxy(target string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		d, err := net.Dial("tcp", target)
		if err != nil {
			http.Error(w, "Error contacting backend server.", 500)
			log.Printf("Error dialing websocket backend %s: %v", target, err)
			return
		}
		hj, ok := w.(http.Hijacker)
		if !ok {
			http.Error(w, "Not a hijacker?", 500)
			return
		}
		nc, _, err := hj.Hijack()
		if err != nil {
			log.Printf("Hijack error: %v", err)
			return
		}
		defer nc.Close()
		defer d.Close()

		err = r.Write(d)
		if err != nil {
			log.Printf("Error copying request to target: %v", err)
			return
		}

		errc := make(chan error, 2)
		cp := func(dst io.Writer, src io.Reader) {
			_, err := io.Copy(dst, src)
			errc <- err
		}
		go cp(d, nc)
		go cp(nc, d)
		<-errc
	})
}
<commit_msg>use Contains() rather than Index() for header isWebsocket<commit_after>// Copyright (c) 2017, Boise State University All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"io"
	"log"
	"net"
	"net/http"
	"strings"
)

// isWebsocket inspects an incoming request and determines if it is for a
// websocket.
func isWebsocket(r *http.Request) bool {
	upgrade := false
	for _, h := range r.Header["Connection"] {
		if strings.Contains(strings.ToLower(h), "upgrade") {
			upgrade = true
			break
		}
	}
	if !upgrade {
		return false
	}

	// FIXME(kyle): Can we just check for websocket in 'Upgrade'?
	for _, h := range r.Header["Upgrade"] {
		if strings.Contains(strings.ToLower(h), "websocket") {
			return true
		}
	}
	return false
}

// websocketProxy handles websocket requests for reverse proxy calls.
// Written by @bradfitz.  See:
//
// https://groups.google.com/forum/#!msg/golang-nuts/KBx9pDlvFOc/QC5v-uC5UOgJ
func websocketProxy(target string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		d, err := net.Dial("tcp", target)
		if err != nil {
			http.Error(w, "Error contacting backend server.", 500)
			log.Printf("Error dialing websocket backend %s: %v", target, err)
			return
		}
		hj, ok := w.(http.Hijacker)
		if !ok {
			http.Error(w, "Not a hijacker?", 500)
			return
		}
		nc, _, err := hj.Hijack()
		if err != nil {
			log.Printf("Hijack error: %v", err)
			return
		}
		defer nc.Close()
		defer d.Close()

		err = r.Write(d)
		if err != nil {
			log.Printf("Error copying request to target: %v", err)
			return
		}

		errc := make(chan error, 2)
		cp := func(dst io.Writer, src io.Reader) {
			_, err := io.Copy(dst, src)
			errc <- err
		}
		go cp(d, nc)
		go cp(nc, d)
		<-errc
	})
}
<|endoftext|>


<commit_before>package model

type NodeStatus int

const (
	NodeStatusNew = iota //when new
	NodeStatusGreen        //all element ok
	NodeStatusYellow    //some ok,some failed
	NodeStatusRed        //all element failed
)

//Node is element of topology
type Node struct {
	Template *NodeTemplate
	Links    map[string][]*Node
	Status   NodeStatus //status of node
	Error    error      //error during the node process
}

//func init() {
//	Models["Class"] = classDesc()
//	Models["Operation"] = operationDesc()
//}
//
//func operationDesc() *ModelDescriptor {
//	return &ModelDescriptor{
//		Type: &Operation{},
//		New: func() interface{} {
//			return &Operation{}
//		},
//	}
//}
//
//func classDesc() *ModelDescriptor {
//	return &ModelDescriptor{
//		Type: &Class{},
//		New: func() interface{} {
//			return &Class{}
//		},
//	}
//}
<commit_msg>Fix enum<commit_after>package model

type NodeStatus int

const (
	NodeStatusNew NodeStatus = iota //when new
	NodeStatusGreen        //all element ok
	NodeStatusYellow    //some ok,some failed
	NodeStatusRed        //all element failed
)

//Node is element of topology
type Node struct {
	Template *NodeTemplate
	Links    map[string][]*Node
	Status   NodeStatus //status of node
	Error    error      //error during the node process
}

//func init() {
//	Models["Class"] = classDesc()
//	Models["Operation"] = operationDesc()
//}
//
//func operationDesc() *ModelDescriptor {
//	return &ModelDescriptor{
//		Type: &Operation{},
//		New: func() interface{} {
//			return &Operation{}
//		},
//	}
//}
//
//func classDesc() *ModelDescriptor {
//	return &ModelDescriptor{
//		Type: &Class{},
//		New: func() interface{} {
//			return &Class{}
//		},
//	}
//}
<|endoftext|>


<commit_before>package xkcd

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
)

const (
	XkcdURL              string = "http://xkcd.com/"
	RemoteJSONFilename   string = "info.0.json"
	DefaultIndexFilename string = "index.json"
)

type Comic struct {
	Num        int
	SafeTitle  string `json:"safe_title"`
	Alt        string
	Img        string
	Title      string
	Transcript string
}

type Index struct {
	Items   map[string]Comic
	Latest  int
	Missing []int
}

var ComicsIndex = Index{Latest: 0}

func LoadIndex(filename string) error {
	if len(filename) == 0 {
		filename = DefaultIndexFilename
	}
	fp, err := os.OpenFile(filename, os.O_CREATE|os.O_RDONLY, 0644)
	defer fp.Close()
	if err != nil {
		return err
	}

	err = json.NewDecoder(fp).Decode(&ComicsIndex)
	if err != nil {
		fmt.Fprintf(os.Stderr,
			"The index is corrupted, will be refreshed -- %s\n", err)
		fp.Close()
		err = UpdateIndex(filename)
	}

	return err
}

func UpdateIndex(filename string) error {
	latestRemoteComic, err := FetchComic(0) // Fetch latest
	if err != nil {
		return fmt.Errorf(
			"couldn't retrieve remote's latest comic -- %s", err)
	}
	if ComicsIndex.Latest == 0 {
		ComicsIndex.Items = make(map[string]Comic)
	}

	for i := ComicsIndex.Latest + 1; i <= latestRemoteComic.Num; i++ {
		if comic, err := FetchComic(i); err != nil {
			fmt.Fprintf(os.Stderr,
				"couldn't retrieve comic -- %s\n", err)
			ComicsIndex.Missing = append(ComicsIndex.Missing, i)
		} else {
			ComicsIndex.Items[strconv.Itoa(i)] = *comic
			ComicsIndex.Latest = i
		}
	}

	if len(filename) == 0 {
		filename = DefaultIndexFilename
	}
	fp, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
	defer fp.Close()
	if err != nil {
		return fmt.Errorf("couldn't open '%s' -- %s", filename, err)
	}

	return json.NewEncoder(fp).Encode(&ComicsIndex)
}

func RegexSearchComic(terms []string) []Comic {
	var (
		results []Comic
		rs      []*regexp.Regexp
	)

	for _, expr := range terms {
		if r, err := regexp.Compile(expr); err == nil {
			rs = append(rs, r)
		} else {
			fmt.Fprintf(os.Stderr, "Invalid regex: %s\n", expr)
		}
	}
	for _, comic := range ComicsIndex.Items {
		for _, r := range rs {
			if r.FindStringIndex(comic.Alt) != nil ||
				r.FindStringIndex(comic.Title) != nil ||
				r.FindStringIndex(comic.SafeTitle) != nil ||
				r.FindStringIndex(comic.Transcript) != nil {
				results = append(results, comic)
				break
			}
		}
	}
	return results
}

func FetchComic(comicID int) (*Comic, error) {
	var (
		comic Comic
		url   string
	)

	if comicID == 0 {
		url = strings.Join([]string{XkcdURL, RemoteJSONFilename}, "")
	} else {
		url = strings.Join([]string{XkcdURL, strconv.Itoa(comicID), "/", RemoteJSONFilename}, "")
	}

	fmt.Fprintf(os.Stderr, "Fetching remote index: %s\n", url)
	resp, err := http.Get(url)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf(
			"couldn't fetch comic '%d' -- %d", comicID, resp.StatusCode)
	}
	if err := json.NewDecoder(resp.Body).Decode(&comic); err != nil {
		return nil, err
	}

	return &comic, nil
}
<commit_msg>Don't hold the default<commit_after>package xkcd

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
)

const (
	XkcdURL            string = "http://xkcd.com/"
	RemoteJSONFilename string = "info.0.json"
)

type Comic struct {
	Num        int
	SafeTitle  string `json:"safe_title"`
	Alt        string
	Img        string
	Title      string
	Transcript string
}

type Index struct {
	Items   map[string]Comic
	Latest  int
	Missing []int
}

var ComicsIndex = Index{Latest: 0}

func LoadIndex(filename string) error {
	fp, err := os.OpenFile(filename, os.O_CREATE|os.O_RDONLY, 0644)
	defer fp.Close()
	if err != nil {
		return err
	}

	err = json.NewDecoder(fp).Decode(&ComicsIndex)
	if err != nil {
		fmt.Fprintf(os.Stderr,
			"The index is corrupted, will be refreshed -- %s\n", err)
		fp.Close()
		err = UpdateIndex(filename)
	}

	return err
}

func UpdateIndex(filename string) error {
	latestRemoteComic, err := FetchComic(0) // Fetch latest
	if err != nil {
		return fmt.Errorf(
			"couldn't retrieve remote's latest comic -- %s", err)
	}
	if ComicsIndex.Latest == 0 {
		ComicsIndex.Items = make(map[string]Comic)
	}

	for i := ComicsIndex.Latest + 1; i <= latestRemoteComic.Num; i++ {
		if comic, err := FetchComic(i); err != nil {
			fmt.Fprintf(os.Stderr,
				"couldn't retrieve comic -- %s\n", err)
			ComicsIndex.Missing = append(ComicsIndex.Missing, i)
		} else {
			ComicsIndex.Items[strconv.Itoa(i)] = *comic
			ComicsIndex.Latest = i
		}
	}

	fp, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
	defer fp.Close()
	if err != nil {
		return fmt.Errorf("couldn't open '%s' -- %s", filename, err)
	}

	return json.NewEncoder(fp).Encode(&ComicsIndex)
}

func RegexSearchComic(terms []string) []Comic {
	var (
		results []Comic
		rs      []*regexp.Regexp
	)

	for _, expr := range terms {
		if r, err := regexp.Compile(expr); err == nil {
			rs = append(rs, r)
		} else {
			fmt.Fprintf(os.Stderr, "Invalid regex: %s\n", expr)
		}
	}
	for _, comic := range ComicsIndex.Items {
		for _, r := range rs {
			if r.FindStringIndex(comic.Alt) != nil ||
				r.FindStringIndex(comic.Title) != nil ||
				r.FindStringIndex(comic.SafeTitle) != nil ||
				r.FindStringIndex(comic.Transcript) != nil {
				results = append(results, comic)
				break
			}
		}
	}
	return results
}

func FetchComic(comicID int) (*Comic, error) {
	var (
		comic Comic
		url   string
	)

	if comicID == 0 {
		url = strings.Join([]string{XkcdURL, RemoteJSONFilename}, "")
	} else {
		url = strings.Join([]string{XkcdURL, strconv.Itoa(comicID), "/", RemoteJSONFilename}, "")
	}

	fmt.Fprintf(os.Stderr, "Fetching remote index: %s\n", url)
	resp, err := http.Get(url)
	defer resp.Body.Close()
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf(
			"couldn't fetch comic '%d' -- %d", comicID, resp.StatusCode)
	}
	if err := json.NewDecoder(resp.Body).Decode(&comic); err != nil {
		return nil, err
	}

	return &comic, nil
}
<|endoftext|>


<commit_before>package linker

import (
	"fmt"
	"log"
	"strings"
	// "path/filepath"
)

import (
	"butler/null"
	proto "code.google.com/p/goprotobuf/proto"
	tp "tritium/proto"
)

type FuncMap map[string]int

type LinkingContext struct {
	objMap        map[string]int
	funList       []FuncMap
	textType      int
	types         []string
	files         []string
	Errors        []string
	ProjectFolder string
	ScriptsFolder string
	*tp.Transform
	Visiting      map[string]bool
}

type LocalDef map[string]int

func NewObjectLinkingContext(pkg *tp.Package, objs []*tp.ScriptObject, projectPath, scriptPath string) *LinkingContext {
	// Setup object lookup map!
	objScriptLookup := make(map[string]int, len(objs))
	for index, obj := range objs {
		objScriptLookup[null.GetString(obj.Name)] = index
	}
	ctx := NewLinkingContext(pkg)
	ctx.objMap = objScriptLookup
	ctx.Objects = objs
	ctx.ProjectFolder = projectPath
	ctx.ScriptsFolder = scriptPath
	ctx.Visiting = make(map[string]bool, 0)
	return ctx
}

func NewLinkingContext(pkg *tp.Package) *LinkingContext {
	// Setup the function map!
	functionLookup := make([]FuncMap, len(pkg.Types))
	types := make([]string, len(pkg.Types))

	for typeId, typeObj := range pkg.Types {
		funcMap := make(FuncMap)
		types[typeId] = null.GetString(typeObj.Name)
		//println("Type:",null.GetString(typeObj.Name))
		//println("Implements:", null.GetInt32(typeObj.Implements))
		implements := functionLookup[null.GetInt32(typeObj.Implements)]
		for index, fun := range pkg.Functions {
			stub := fun.Stub(pkg)

			funScopeId := null.GetInt32(fun.ScopeTypeId)
			inherited := false
			// funScopeId is ancestor of typeId
			if (implements != nil) && pkg.AncestorOf(funScopeId, int32(typeId)) {
				_, inherited = implements[stub]
			}
			if (funScopeId == int32(typeId)) || inherited {
				//println(null.GetString(typeObj.Name), ":", stub)
				funcMap[stub] = index
			}
		}
		functionLookup[typeId] = funcMap
	}

	// Setup the main context object
	ctx := &LinkingContext{
		funList: functionLookup,
		types:   types,
		Errors:  make([]string, 0),
		Transform: &tp.Transform{
			Pkg: pkg,
		},
	}

	// Find Text type int -- need its ID to deal with Text literals during processing
	ctx.textType = pkg.GetTypeId("Text")

	// println("TYPES")
	// for i, t := range types {
	// 	println(i, ":", t)
	// }
	// println()

	// println("FUNCTIONS")
	// for t, fm := range functionLookup {
	// 	println("TYPE", t)
	// 	for n, f:= range fm {
	// 		println(n, ":", f)
	// 	}
	// 	println()
	// }
	// println()

	return ctx
}

func (ctx *LinkingContext) Link() {
	ctx.link(0, ctx.Pkg.GetTypeId("Text"))
}

func (ctx *LinkingContext) link(objId, scopeType int) {
	obj := ctx.Objects[objId]
	//println("link object", objId)
	//println(obj.String())
	if null.GetBool(obj.Linked) == false {
		//println("Linking", null.GetString(obj.Name))
		obj.ScopeTypeId = proto.Int(scopeType)
		obj.Linked = proto.Bool(true)
		path := obj.GetName()
		ctx.files = append(ctx.files, null.GetString(obj.Name))
		ctx.ProcessInstruction(obj.Root, scopeType, path)
		ctx.files = ctx.files[:(len(ctx.files) - 1)]
	} else {
		if scopeType != int(null.GetInt32(obj.ScopeTypeId)) {
			ctx.error("script", "Imported a script in two different scopes! Not processing second import.")
		}
	}
}

func (ctx *LinkingContext) ProcessInstruction(ins *tp.Instruction, scopeType int, path string) (returnType int) {
	localScope := make(LocalDef, 0)
	return ctx.ProcessInstructionWithLocalScope(ins, scopeType, localScope, "", path)
}

func (ctx *LinkingContext) ProcessInstructionWithLocalScope(ins *tp.Instruction, scopeType int, localScope LocalDef, caller string, path string) (returnType int) {
	returnType = -1
	ins.IsValid = proto.Bool(true)
	switch *ins.Type {
	case tp.Instruction_IMPORT:
		importLocation := ins.GetValue()

		// keep track of which files we're inside of, to detect circular imports
		val, present := ctx.Visiting[importLocation]
		if present && val {
			ctx.error(ins, "Circular import detected: %s", importLocation)
			panic(fmt.Sprintf("Circular import detected: %s", importLocation))
		}
		ctx.Visiting[importLocation] = true

		// set its import_id and blank the value field
		// importValue := filepath.Join(ctx.ProjectFolder, null.GetString(ins.Value))
		//println("import: ", importValue)
		//println(null.GetInt32(ins.LineNumber))
		importId, ok := ctx.objMap[null.GetString(ins.Value)]
		if ok != true {
			ctx.error(ins, "Invalid import `%s`", ins.String())
		}
		// Make sure this object is linked with the right scopeType
		ctx.link(importId, scopeType)
		// unset this after visiting an import
		ctx.Visiting[importLocation] = false
		//println("befor", ins.String())
		ins.ObjectId = proto.Int(importId)
		ins.Value = nil
		//println("after", ins.String())
	case tp.Instruction_LOCAL_VAR:
		name := null.GetString(ins.Value)
		if name == "1" || name == "2" || name == "3" || name == "4" || name == "5" || name == "6" || name == "7" {
			if len(ins.Arguments) > 0 {
				// We are going to assign something to this variable
				returnType = ctx.ProcessInstructionWithLocalScope(ins.Arguments[0], scopeType, localScope, caller, path)
				if returnType != ctx.textType {
					ctx.error(ins, "Numeric local vars can ONLY be Text")
				}
			}
			if ins.Children != nil {
				for _, child := range ins.Children {
					ctx.ProcessInstructionWithLocalScope(child, ctx.textType, localScope, caller, path)
				}
			}
			returnType = ctx.textType
		} else { // Not numeric.
			typeId, found := localScope[name]
			if found {
				returnType = typeId
				if len(ins.Arguments) > 0 {
					ctx.error(ins, "The local variable \"%%%s\" has been assigned before and cannot be reassigned! Open a scope on it if you need to alter the contents.", name)
				} else {
					if ins.Children != nil {
						for _, child := range ins.Children {
							returnType = ctx.ProcessInstructionWithLocalScope(child, typeId, localScope, caller, path)
						}
					}
				}

			} else {
				if len(ins.Arguments) > 0 {
					// We are going to assign something to this variable
					// But first, check for possible mutation and prevent it for now.
					if ins.Children != nil {
						ctx.error(ins, "May not open a scope during initialization of local variable \"%%%s\".", name)
					}
					returnType = ctx.ProcessInstructionWithLocalScope(ins.Arguments[0], scopeType, localScope, caller, path)
					localScope[name] = returnType
				} else {
					println(ins.String())
					ctx.error(ins, "I've never seen the variable \"%%%s\" before! Please assign a value before usage.", name)
				}
			}
		}

	case tp.Instruction_FUNCTION_CALL:
		stub := null.GetString(ins.Value)
		if stub == "yield" {
			ins.YieldTypeId = proto.Int32(int32(scopeType))
		}
		// unqualifiedStub := stub
		ns := ins.GetNamespace()
		if len(ns) == 0 {
			ns = "tritium"
		}
		stub = ns + "." + stub
		// process the args
		if ins.Arguments != nil {
			for _, arg := range ins.Arguments {
				argReturn := ctx.ProcessInstructionWithLocalScope(arg, scopeType, localScope, caller, path)
				if argReturn == -1 {
					ctx.error(ins, "Invalid argument object %q", arg.String())
					return
				}
				stub = stub + "," + ctx.types[argReturn]
			}
		}
		// look up the function wrt the current context type + function name
		funcId, ok := ctx.funList[scopeType][stub]
		if ok != true {

			stubComponents := strings.SplitN(stub, ".", 2)
			ns, basicStub := stubComponents[0], stubComponents[1]
			readableCalleeStub := strings.Replace(basicStub, ",", "(", 1)
			if strings.Index(readableCalleeStub, "(") != -1 {
				readableCalleeStub = readableCalleeStub + ")"
			}
			stubComponents = strings.SplitN(caller, ".", 2)
			nsCaller, basicStubCaller := stubComponents[0], stubComponents[1]
			readableCallerStub := strings.Replace(basicStubCaller, ",", "(", 1)
			if strings.Index(readableCallerStub, "(") != -1 {
				readableCallerStub = readableCallerStub + ")"
      }

			message := fmt.Sprintf("Available functions in %s.%s:\n", ns, ctx.types[scopeType])
			nsPrefix := ns + "."
			for funcName, _ := range ctx.funList[scopeType] {
				if strings.HasPrefix(funcName, nsPrefix) {
					message = message + funcName + "\n"
				}
			}
			log.Printf("%s\n", message)

			location := ""
			if len(path) > 0 {
				location = path
			} else {
				location = "Package " + ctx.Pkg.GetName()
			}
			ctx.error(ins, "%s:%d: could not find function %s.%s.%s (called from %s.%s.%s)", location, ins.GetLineNumber(), ns, ctx.types[scopeType], readableCalleeStub, nsCaller, ctx.types[scopeType], readableCallerStub)

		} else {
			ins.FunctionId = proto.Int32(int32(funcId))
			fun := ctx.Pkg.Functions[funcId]
			returnType = int(null.GetInt32(fun.ReturnTypeId))
			opensScopeType := int(null.GetInt32(fun.OpensTypeId))
			if opensScopeType == 0 {
				// If we're a Base scope, don't mess with texas!
				opensScopeType = scopeType
			}
			// If it inherits:
			inheritedOpensScopeType := ctx.Pkg.FindDescendantType(int32(opensScopeType))
			if inheritedOpensScopeType != -1 {
				opensScopeType = inheritedOpensScopeType
			}

			// Copy the local scope
			parentScope := localScope
			localScope = make(LocalDef, len(parentScope))
			for s, t := range parentScope {
				localScope[s] = t
			}

			if ins.Children != nil {
				for _, child := range ins.Children {
					ctx.ProcessInstructionWithLocalScope(child, opensScopeType, localScope, stub, path) // thread the name of the caller through the linkages
				}
			}
		}
	case tp.Instruction_TEXT:
		returnType = ctx.textType
	case tp.Instruction_BLOCK:
		if ins.Children != nil {
			for _, child := range ins.Children {
				returnType = ctx.ProcessInstructionWithLocalScope(child, scopeType, localScope, caller, path)
			}
		}
	}
	return
}

func (ctx *LinkingContext) HasErrors() bool {
	return (len(ctx.Errors) > 0)
}

func (ctx *LinkingContext) error(obj interface{}, format string, data ...interface{}) {
	message := fmt.Sprintf(format, data...)
	ctx.Errors = append(ctx.Errors, message)
	ins, ok := obj.(*tp.Instruction)
	if ok {
		ins.IsValid = proto.Bool(false)
	}
	log.Printf("%s\n", message)
}
<commit_msg>Making the "function not found; try these" error message prettier.<commit_after>package linker

import (
	"fmt"
	"log"
	"strings"
	// "path/filepath"
)

import (
	"butler/null"
	proto "code.google.com/p/goprotobuf/proto"
	tp "tritium/proto"
)

type FuncMap map[string]int

type LinkingContext struct {
	objMap        map[string]int
	funList       []FuncMap
	textType      int
	types         []string
	files         []string
	Errors        []string
	ProjectFolder string
	ScriptsFolder string
	*tp.Transform
	Visiting      map[string]bool
}

type LocalDef map[string]int

func NewObjectLinkingContext(pkg *tp.Package, objs []*tp.ScriptObject, projectPath, scriptPath string) *LinkingContext {
	// Setup object lookup map!
	objScriptLookup := make(map[string]int, len(objs))
	for index, obj := range objs {
		objScriptLookup[null.GetString(obj.Name)] = index
	}
	ctx := NewLinkingContext(pkg)
	ctx.objMap = objScriptLookup
	ctx.Objects = objs
	ctx.ProjectFolder = projectPath
	ctx.ScriptsFolder = scriptPath
	ctx.Visiting = make(map[string]bool, 0)
	return ctx
}

func NewLinkingContext(pkg *tp.Package) *LinkingContext {
	// Setup the function map!
	functionLookup := make([]FuncMap, len(pkg.Types))
	types := make([]string, len(pkg.Types))

	for typeId, typeObj := range pkg.Types {
		funcMap := make(FuncMap)
		types[typeId] = null.GetString(typeObj.Name)
		//println("Type:",null.GetString(typeObj.Name))
		//println("Implements:", null.GetInt32(typeObj.Implements))
		implements := functionLookup[null.GetInt32(typeObj.Implements)]
		for index, fun := range pkg.Functions {
			stub := fun.Stub(pkg)

			funScopeId := null.GetInt32(fun.ScopeTypeId)
			inherited := false
			// funScopeId is ancestor of typeId
			if (implements != nil) && pkg.AncestorOf(funScopeId, int32(typeId)) {
				_, inherited = implements[stub]
			}
			if (funScopeId == int32(typeId)) || inherited {
				//println(null.GetString(typeObj.Name), ":", stub)
				funcMap[stub] = index
			}
		}
		functionLookup[typeId] = funcMap
	}

	// Setup the main context object
	ctx := &LinkingContext{
		funList: functionLookup,
		types:   types,
		Errors:  make([]string, 0),
		Transform: &tp.Transform{
			Pkg: pkg,
		},
	}

	// Find Text type int -- need its ID to deal with Text literals during processing
	ctx.textType = pkg.GetTypeId("Text")

	// println("TYPES")
	// for i, t := range types {
	// 	println(i, ":", t)
	// }
	// println()

	// println("FUNCTIONS")
	// for t, fm := range functionLookup {
	// 	println("TYPE", t)
	// 	for n, f:= range fm {
	// 		println(n, ":", f)
	// 	}
	// 	println()
	// }
	// println()

	return ctx
}

func (ctx *LinkingContext) Link() {
	ctx.link(0, ctx.Pkg.GetTypeId("Text"))
}

func (ctx *LinkingContext) link(objId, scopeType int) {
	obj := ctx.Objects[objId]
	//println("link object", objId)
	//println(obj.String())
	if null.GetBool(obj.Linked) == false {
		//println("Linking", null.GetString(obj.Name))
		obj.ScopeTypeId = proto.Int(scopeType)
		obj.Linked = proto.Bool(true)
		path := obj.GetName()
		ctx.files = append(ctx.files, null.GetString(obj.Name))
		ctx.ProcessInstruction(obj.Root, scopeType, path)
		ctx.files = ctx.files[:(len(ctx.files) - 1)]
	} else {
		if scopeType != int(null.GetInt32(obj.ScopeTypeId)) {
			ctx.error("script", "Imported a script in two different scopes! Not processing second import.")
		}
	}
}

func (ctx *LinkingContext) ProcessInstruction(ins *tp.Instruction, scopeType int, path string) (returnType int) {
	localScope := make(LocalDef, 0)
	return ctx.ProcessInstructionWithLocalScope(ins, scopeType, localScope, "", path)
}

func (ctx *LinkingContext) ProcessInstructionWithLocalScope(ins *tp.Instruction, scopeType int, localScope LocalDef, caller string, path string) (returnType int) {
	returnType = -1
	ins.IsValid = proto.Bool(true)
	switch *ins.Type {
	case tp.Instruction_IMPORT:
		importLocation := ins.GetValue()

		// keep track of which files we're inside of, to detect circular imports
		val, present := ctx.Visiting[importLocation]
		if present && val {
			ctx.error(ins, "Circular import detected: %s", importLocation)
			panic(fmt.Sprintf("Circular import detected: %s", importLocation))
		}
		ctx.Visiting[importLocation] = true

		// set its import_id and blank the value field
		// importValue := filepath.Join(ctx.ProjectFolder, null.GetString(ins.Value))
		//println("import: ", importValue)
		//println(null.GetInt32(ins.LineNumber))
		importId, ok := ctx.objMap[null.GetString(ins.Value)]
		if ok != true {
			ctx.error(ins, "Invalid import `%s`", ins.String())
		}
		// Make sure this object is linked with the right scopeType
		ctx.link(importId, scopeType)
		// unset this after visiting an import
		ctx.Visiting[importLocation] = false
		//println("befor", ins.String())
		ins.ObjectId = proto.Int(importId)
		ins.Value = nil
		//println("after", ins.String())
	case tp.Instruction_LOCAL_VAR:
		name := null.GetString(ins.Value)
		if name == "1" || name == "2" || name == "3" || name == "4" || name == "5" || name == "6" || name == "7" {
			if len(ins.Arguments) > 0 {
				// We are going to assign something to this variable
				returnType = ctx.ProcessInstructionWithLocalScope(ins.Arguments[0], scopeType, localScope, caller, path)
				if returnType != ctx.textType {
					ctx.error(ins, "Numeric local vars can ONLY be Text")
				}
			}
			if ins.Children != nil {
				for _, child := range ins.Children {
					ctx.ProcessInstructionWithLocalScope(child, ctx.textType, localScope, caller, path)
				}
			}
			returnType = ctx.textType
		} else { // Not numeric.
			typeId, found := localScope[name]
			if found {
				returnType = typeId
				if len(ins.Arguments) > 0 {
					ctx.error(ins, "The local variable \"%%%s\" has been assigned before and cannot be reassigned! Open a scope on it if you need to alter the contents.", name)
				} else {
					if ins.Children != nil {
						for _, child := range ins.Children {
							returnType = ctx.ProcessInstructionWithLocalScope(child, typeId, localScope, caller, path)
						}
					}
				}

			} else {
				if len(ins.Arguments) > 0 {
					// We are going to assign something to this variable
					// But first, check for possible mutation and prevent it for now.
					if ins.Children != nil {
						ctx.error(ins, "May not open a scope during initialization of local variable \"%%%s\".", name)
					}
					returnType = ctx.ProcessInstructionWithLocalScope(ins.Arguments[0], scopeType, localScope, caller, path)
					localScope[name] = returnType
				} else {
					println(ins.String())
					ctx.error(ins, "I've never seen the variable \"%%%s\" before! Please assign a value before usage.", name)
				}
			}
		}

	case tp.Instruction_FUNCTION_CALL:
		stub := null.GetString(ins.Value)
		if stub == "yield" {
			ins.YieldTypeId = proto.Int32(int32(scopeType))
		}
		// unqualifiedStub := stub
		ns := ins.GetNamespace()
		if len(ns) == 0 {
			ns = "tritium"
		}
		stub = ns + "." + stub
		// process the args
		if ins.Arguments != nil {
			for _, arg := range ins.Arguments {
				argReturn := ctx.ProcessInstructionWithLocalScope(arg, scopeType, localScope, caller, path)
				if argReturn == -1 {
					ctx.error(ins, "Invalid argument object %q", arg.String())
					return
				}
				stub = stub + "," + ctx.types[argReturn]
			}
		}
		// look up the function wrt the current context type + function name
		funcId, ok := ctx.funList[scopeType][stub]
		if !ok {

			// stubComponents := strings.SplitN(stub, ".", 2)
			// ns, basicStub := stubComponents[0], stubComponents[1]
			// readableCalleeStub := strings.Replace(basicStub, ",", "(", 1)
			// if strings.Index(readableCalleeStub, "(") != -1 {
			// 	readableCalleeStub = readableCalleeStub + ")"
			// }
			// stubComponents = strings.SplitN(caller, ".", 2)
			// nsCaller, basicStubCaller := stubComponents[0], stubComponents[1]
			// readableCallerStub := strings.Replace(basicStubCaller, ",", "(", 1)
			// if strings.Index(readableCallerStub, "(") != -1 {
			// 	readableCallerStub = readableCallerStub + ")"
      // }

      readableCalleeStub := readableStub(stub)
      readableCallerStub := readableStub(caller)

			message := fmt.Sprintf("Available functions in %s.%s:\n", ns, ctx.types[scopeType])
			ns := strings.SplitN(stub, ".", 2)[0]
			nsCaller := strings.SplitN(caller, ".", 2)[0]
			nsPrefix := ns + "."
			for funcName, _ := range ctx.funList[scopeType] {
				if strings.HasPrefix(funcName, nsPrefix) {
					message += "\t" + nsPrefix + readableStub(funcName) + "\n"
				}
			}
			log.Printf("%s\n", message)

			location := ""
			if len(path) > 0 {
				location = path
			} else {
				location = "Package " + ctx.Pkg.GetName()
			}
			ctx.error(ins, "%s:%d: could not find function %s.%s.%s (called from %s.%s.%s)", location, ins.GetLineNumber(), ns, ctx.types[scopeType], readableCalleeStub, nsCaller, ctx.types[scopeType], readableCallerStub)

		} else {
			ins.FunctionId = proto.Int32(int32(funcId))
			fun := ctx.Pkg.Functions[funcId]
			returnType = int(null.GetInt32(fun.ReturnTypeId))
			opensScopeType := int(null.GetInt32(fun.OpensTypeId))
			if opensScopeType == 0 {
				// If we're a Base scope, don't mess with texas!
				opensScopeType = scopeType
			}
			// If it inherits:
			inheritedOpensScopeType := ctx.Pkg.FindDescendantType(int32(opensScopeType))
			if inheritedOpensScopeType != -1 {
				opensScopeType = inheritedOpensScopeType
			}

			// Copy the local scope
			parentScope := localScope
			localScope = make(LocalDef, len(parentScope))
			for s, t := range parentScope {
				localScope[s] = t
			}

			if ins.Children != nil {
				for _, child := range ins.Children {
					ctx.ProcessInstructionWithLocalScope(child, opensScopeType, localScope, stub, path) // thread the name of the caller through the linkages
				}
			}
		}
	case tp.Instruction_TEXT:
		returnType = ctx.textType
	case tp.Instruction_BLOCK:
		if ins.Children != nil {
			for _, child := range ins.Children {
				returnType = ctx.ProcessInstructionWithLocalScope(child, scopeType, localScope, caller, path)
			}
		}
	}
	return
}

func (ctx *LinkingContext) HasErrors() bool {
	return (len(ctx.Errors) > 0)
}

func (ctx *LinkingContext) error(obj interface{}, format string, data ...interface{}) {
	message := fmt.Sprintf(format, data...)
	ctx.Errors = append(ctx.Errors, message)
	ins, ok := obj.(*tp.Instruction)
	if ok {
		ins.IsValid = proto.Bool(false)
	}
	log.Printf("%s\n", message)
}

func readableStub(stub string) string {
	basicStub := strings.SplitN(stub, ".", 2)[1]
	betterStub := strings.Replace(basicStub, ",", "(", 1)
	if strings.Index(betterStub, "(") != -1 {
		betterStub = betterStub + ")"
	}
	return betterStub
}<|endoftext|>


<commit_before>package namesys

import (
	"context"
	"strings"
	"time"

	path "github.com/ipfs/go-ipfs/path"

	ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore"
	routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing"
	peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer"
	ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto"
)

// mpns (a multi-protocol NameSystem) implements generic IPFS naming.
//
// Uses several Resolvers:
// (a) IPFS routing naming: SFS-like PKI names.
// (b) dns domains: resolves using links in DNS TXT records
// (c) proquints: interprets string as the raw byte data.
//
// It can only publish to: (a) IPFS routing naming.
//
type mpns struct {
	resolvers  map[string]resolver
	publishers map[string]Publisher
}

// NewNameSystem will construct the IPFS naming system based on Routing
func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSystem {
	return &mpns{
		resolvers: map[string]resolver{
			"dns":      newDNSResolver(),
			"proquint": new(ProquintResolver),
			"dht":      NewRoutingResolver(r, cachesize),
		},
		publishers: map[string]Publisher{
			"/ipns/": NewRoutingPublisher(r, ds),
		},
	}
}

const DefaultResolverCacheTTL = time.Minute

// Resolve implements Resolver.
func (ns *mpns) Resolve(ctx context.Context, name string) (path.Path, error) {
	return ns.ResolveN(ctx, name, DefaultDepthLimit)
}

// ResolveN implements Resolver.
func (ns *mpns) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
	if strings.HasPrefix(name, "/ipfs/") {
		return path.ParsePath(name)
	}

	if !strings.HasPrefix(name, "/") {
		return path.ParsePath("/ipfs/" + name)
	}

	return resolve(ctx, ns, name, depth, "/ipns/")
}

// resolveOnce implements resolver.
func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) {
	if !strings.HasPrefix(name, "/ipns/") {
		name = "/ipns/" + name
	}
	segments := strings.SplitN(name, "/", 4)
	if len(segments) < 3 || segments[0] != "" {
		log.Warningf("Invalid name syntax for %s", name)
		return "", ErrResolveFailed
	}

	for protocol, resolver := range ns.resolvers {
		log.Debugf("Attempting to resolve %s with %s", segments[2], protocol)
		p, err := resolver.resolveOnce(ctx, segments[2])
		if err == nil {
			if len(segments) > 3 {
				return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3])
			} else {
				return p, err
			}
		}
	}
	log.Warningf("No resolver found for %s", name)
	return "", ErrResolveFailed
}

// Publish implements Publisher
func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
	err := ns.publishers["/ipns/"].Publish(ctx, name, value)
	if err != nil {
		return err
	}
	ns.addToDHTCache(name, value, time.Now().Add(DefaultRecordTTL))
	return nil
}

func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error {
	err := ns.publishers["/ipns/"].PublishWithEOL(ctx, name, value, eol)
	if err != nil {
		return err
	}
	ns.addToDHTCache(name, value, eol)
	return nil
}

func (ns *mpns) addToDHTCache(key ci.PrivKey, value path.Path, eol time.Time) {
	var err error
	value, err = path.ParsePath(value.String())
	if err != nil {
		log.Error("could not parse path")
		return
	}

	name, err := peer.IDFromPrivateKey(key)
	if err != nil {
		log.Error("while adding to cache, could not get peerid from private key")
		return
	}

	rr, ok := ns.resolvers["dht"].(*routingResolver)
	if !ok {
		// should never happen, purely for sanity
		log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["dht"])
	}
	if time.Now().Add(DefaultResolverCacheTTL).Before(eol) {
		eol = time.Now().Add(DefaultResolverCacheTTL)
	}
	rr.cache.Add(name.Pretty(), cacheEntry{
		val: value,
		eol: eol,
	})
}
<commit_msg>namesys: fix case where there is no cache<commit_after>package namesys

import (
	"context"
	"strings"
	"time"

	path "github.com/ipfs/go-ipfs/path"

	ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore"
	routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing"
	peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer"
	ci "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto"
)

// mpns (a multi-protocol NameSystem) implements generic IPFS naming.
//
// Uses several Resolvers:
// (a) IPFS routing naming: SFS-like PKI names.
// (b) dns domains: resolves using links in DNS TXT records
// (c) proquints: interprets string as the raw byte data.
//
// It can only publish to: (a) IPFS routing naming.
//
type mpns struct {
	resolvers  map[string]resolver
	publishers map[string]Publisher
}

// NewNameSystem will construct the IPFS naming system based on Routing
func NewNameSystem(r routing.ValueStore, ds ds.Datastore, cachesize int) NameSystem {
	return &mpns{
		resolvers: map[string]resolver{
			"dns":      newDNSResolver(),
			"proquint": new(ProquintResolver),
			"dht":      NewRoutingResolver(r, cachesize),
		},
		publishers: map[string]Publisher{
			"/ipns/": NewRoutingPublisher(r, ds),
		},
	}
}

const DefaultResolverCacheTTL = time.Minute

// Resolve implements Resolver.
func (ns *mpns) Resolve(ctx context.Context, name string) (path.Path, error) {
	return ns.ResolveN(ctx, name, DefaultDepthLimit)
}

// ResolveN implements Resolver.
func (ns *mpns) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
	if strings.HasPrefix(name, "/ipfs/") {
		return path.ParsePath(name)
	}

	if !strings.HasPrefix(name, "/") {
		return path.ParsePath("/ipfs/" + name)
	}

	return resolve(ctx, ns, name, depth, "/ipns/")
}

// resolveOnce implements resolver.
func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) {
	if !strings.HasPrefix(name, "/ipns/") {
		name = "/ipns/" + name
	}
	segments := strings.SplitN(name, "/", 4)
	if len(segments) < 3 || segments[0] != "" {
		log.Warningf("Invalid name syntax for %s", name)
		return "", ErrResolveFailed
	}

	for protocol, resolver := range ns.resolvers {
		log.Debugf("Attempting to resolve %s with %s", segments[2], protocol)
		p, err := resolver.resolveOnce(ctx, segments[2])
		if err == nil {
			if len(segments) > 3 {
				return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3])
			} else {
				return p, err
			}
		}
	}
	log.Warningf("No resolver found for %s", name)
	return "", ErrResolveFailed
}

// Publish implements Publisher
func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
	err := ns.publishers["/ipns/"].Publish(ctx, name, value)
	if err != nil {
		return err
	}
	ns.addToDHTCache(name, value, time.Now().Add(DefaultRecordTTL))
	return nil
}

func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error {
	err := ns.publishers["/ipns/"].PublishWithEOL(ctx, name, value, eol)
	if err != nil {
		return err
	}
	ns.addToDHTCache(name, value, eol)
	return nil
}

func (ns *mpns) addToDHTCache(key ci.PrivKey, value path.Path, eol time.Time) {
	rr, ok := ns.resolvers["dht"].(*routingResolver)
	if !ok {
		// should never happen, purely for sanity
		log.Panicf("unexpected type %T as DHT resolver.", ns.resolvers["dht"])
	}
	if rr.cache == nil {
		// resolver has no caching
		return
	}

	var err error
	value, err = path.ParsePath(value.String())
	if err != nil {
		log.Error("could not parse path")
		return
	}

	name, err := peer.IDFromPrivateKey(key)
	if err != nil {
		log.Error("while adding to cache, could not get peerid from private key")
		return
	}

	if time.Now().Add(DefaultResolverCacheTTL).Before(eol) {
		eol = time.Now().Add(DefaultResolverCacheTTL)
	}
	rr.cache.Add(name.Pretty(), cacheEntry{
		val: value,
		eol: eol,
	})
}
<|endoftext|>


<commit_before>package bus

import (
    "strconv"
    "log"
    "net"
    "crypto/tls"
    "golem/secure"

)

/*
This opens the communication CommSocketListener
peons will connect to and use this socket
//TODO:  Connect the socket bus here with the control bus
*/
func SocketListener(ip string, port int, role string) {
    //tlsCfg.InsecureSkipVerify = true
    //tlsListener, err := tls.Listen("tcp4", ip + ":" + strconv.Itoa(port), &tlsCfg)

   listener := initListener(ip, port)
    if role == "comm" {
        startCommPort(listener)
    } else {
        go startDataPort(listener)
    }
}

/*
Creates the socket listener
*/
func initListener(ip string, port int) (net.Listener) {
    log.Println("Listening for Comm on socket: ", strconv.Itoa(port))
    cert, err := tls.LoadX509KeyPair(secure.MasterPubCert, secure.MasterPrivateKey)
    if err != nil {
        log.Fatal(err)
        panic(err)
    }

    tlsCfg := &tls.Config{Certificates: []tls.Certificate{cert}}
    tlsCfg.InsecureSkipVerify = true
    l, err := tls.Listen("tcp4", ip + ":" + strconv.Itoa(port), tlsCfg)

    //l, err := net.Listen("tcp", ip + ":" + strconv.Itoa(port))
    if err != nil {
        log.Fatal(err)
        panic(err)
    }
    defer l.Close()
    return l
}

func startCommPort(l net.Listener) {
    for {
        fd, err := l.Accept()
        if err != nil {
            log.Println("Something went wrong: ", err)
        }
        go processConnection(fd)
    }
}

func startDataPort(l net.Listener) {
    for {
        fd, err := l.Accept()
        if err != nil {
            log.Println("Something went awry: ", err)
        }
        go attachDataPort(fd)
    }
}<commit_msg>Removed the defer to test why the listener blew up.<commit_after>package bus

import (
    "strconv"
    "log"
    "net"
    "crypto/tls"
    "golem/secure"

)

/*
This opens the communication CommSocketListener
peons will connect to and use this socket
//TODO:  Connect the socket bus here with the control bus
*/
func SocketListener(ip string, port int, role string) {
    //tlsCfg.InsecureSkipVerify = true
    //tlsListener, err := tls.Listen("tcp4", ip + ":" + strconv.Itoa(port), &tlsCfg)

   listener := initListener(ip, port)
    if role == "comm" {
        startCommPort(listener)
    } else {
        go startDataPort(listener)
    }
}

/*
Creates the socket listener
*/
func initListener(ip string, port int) (net.Listener) {
    log.Println("Listening for Comm on socket: ", strconv.Itoa(port))
    cert, err := tls.LoadX509KeyPair(secure.MasterPubCert, secure.MasterPrivateKey)
    if err != nil {
        log.Fatal(err)
        panic(err)
    }

    tlsCfg := &tls.Config{Certificates: []tls.Certificate{cert}}
    tlsCfg.InsecureSkipVerify = true
    l, err := tls.Listen("tcp4", ip + ":" + strconv.Itoa(port), tlsCfg)

    //l, err := net.Listen("tcp", ip + ":" + strconv.Itoa(port))
    if err != nil {
        log.Fatal(err)
        panic(err)
    }
    return l
}

func startCommPort(l net.Listener) {
    for {
        fd, err := l.Accept()
        if err != nil {
            log.Println("Something went wrong: ", err)
        }
        go processConnection(fd)
    }
}

func startDataPort(l net.Listener) {
    for {
        fd, err := l.Accept()
        if err != nil {
            log.Println("Something went awry: ", err)
        }
        go attachDataPort(fd)
    }
}<|endoftext|>