<commit_before>package main

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

	"github.com/gosexy/gettext"
	"golang.org/x/crypto/ssh/terminal"
	"gopkg.in/yaml.v2"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

type configCmd struct {
	httpAddr string
}

func (c *configCmd) showByDefault() bool {
	return true
}

var configEditHelp string = gettext.Gettext(
	"### This is a yaml representation of the configuration.\n" +
		"### Any line starting with a '# will be ignored.\n" +
		"###\n" +
		"### A sample configuration looks like:\n" +
		"### name: container1\n" +
		"### profiles:\n" +
		"### - default\n" +
		"### config:\n" +
		"###   volatile.eth0.hwaddr: 00:16:3e:e9:f8:7f\n" +
		"### devices:\n" +
		"###   homedir:\n" +
		"###     path: /extra\n" +
		"###     source: /home/user\n" +
		"###     type: disk\n" +
		"### ephemeral: false\n" +
		"###\n" +
		"### Note that the name is shown but cannot be changed\n")

func (c *configCmd) usage() string {
	return gettext.Gettext(
		"Manage configuration.\n" +
			"\n" +
			"lxc config device add <container> <name> <type> [key=value]...\n" +
			"               Add a device to a container\n" +
			"lxc config device list <container>                     List devices for container\n" +
			"lxc config device show <container>                     Show full device details for container\n" +
			"lxc config device remove <container> <name>            Remove device from container\n" +
			"lxc config edit <container>                            Edit container configuration in external editor\n" +
			"lxc config get <container> key                         Get configuration key\n" +
			"lxc config set <container> key value                   Set container configuration key\n" +
			"lxc config unset <container> key                       Unset container configuration key\n" +
			"lxc config set key value                               Set server configuration key\n" +
			"lxc config unset key                                   Unset server configuration key\n" +
			"lxc config show <container>                            Show container configuration\n" +
			"lxc config trust list [remote]                         List all trusted certs.\n" +
			"lxc config trust add [remote] [certfile.crt]           Add certfile.crt to trusted hosts.\n" +
			"lxc config trust remove [remote] [hostname|fingerprint]\n" +
			"               Remove the cert from trusted hosts.\n" +
			"\n" +
			"Examples:\n" +
			"To mount host's /share/c1 onto /opt in the container:\n" +
			"\tlxc config device add container1 mntdir disk source=/share/c1 path=opt\n" +
			"To set an lxc config value:\n" +
			"\tlxc config set <container> raw.lxc 'lxc.aa_allow_incomplete = 1'\n" +
			"To set the server trust password:\n" +
			"\tlxc config set core.trust_password blah\n")
}

func (c *configCmd) flags() {}

func doSet(config *lxd.Config, args []string) error {
	if len(args) != 4 {
		return errArgs
	}

	// [[lxc config]] set dakara:c1 limits.memory 200000
	remote, container := config.ParseRemoteAndContainer(args[1])
	d, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	key := args[2]
	value := args[3]
	resp, err := d.SetContainerConfig(container, key, value)
	if err != nil {
		return err
	}
	return d.WaitForSuccess(resp.Operation)
}

func (c *configCmd) run(config *lxd.Config, args []string) error {
	if len(args) < 1 {
		return errArgs
	}

	switch args[0] {

	case "unset":
		if len(args) < 2 {
			return errArgs
		}

		if len(args) == 2 {
			key := args[1]
			c, err := lxd.NewClient(config, "")
			if err != nil {
				return err
			}
			_, err = c.SetServerConfig(key, "")
			return err
		}

		return doSet(config, append(args, ""))

	case "set":
		if len(args) < 3 {
			return errArgs
		}

		if len(args) == 3 {
			key := args[1]
			c, err := lxd.NewClient(config, "")
			if err != nil {
				return err
			}
			_, err = c.SetServerConfig(key, args[2])
			return err
		}

		return doSet(config, args)

	case "trust":
		if len(args) < 2 {
			return errArgs
		}

		switch args[1] {
		case "list":
			var remote string
			if len(args) == 3 {
				remote = config.ParseRemote(args[2])
			} else {
				remote = config.DefaultRemote
			}

			d, err := lxd.NewClient(config, remote)
			if err != nil {
				return err
			}

			trust, err := d.CertificateList()
			if err != nil {
				return err
			}

			for _, fingerprint := range trust {
				fmt.Println(fmt.Sprintf("%s", fingerprint))
			}

			return nil
		case "add":
			var remote string
			if len(args) < 3 {
				return fmt.Errorf(gettext.Gettext("No cert provided to add"))
			} else if len(args) == 4 {
				remote = config.ParseRemote(args[2])
			} else {
				remote = config.DefaultRemote
			}

			d, err := lxd.NewClient(config, remote)
			if err != nil {
				return err
			}

			fname := args[len(args)-1]
			cert, err := shared.ReadCert(fname)
			if err != nil {
				return err
			}

			name, _ := shared.SplitExt(fname)
			return d.CertificateAdd(cert, name)
		case "remove":
			var remote string
			if len(args) < 3 {
				return fmt.Errorf(gettext.Gettext("No fingerprint specified."))
			} else if len(args) == 4 {
				remote = config.ParseRemote(args[2])
			} else {
				remote = config.DefaultRemote
			}

			d, err := lxd.NewClient(config, remote)
			if err != nil {
				return err
			}

			return d.CertificateRemove(args[len(args)-1])
		default:
			return fmt.Errorf(gettext.Gettext("Unkonwn config trust command %s"), args[1])
		}

	case "show":
		remote := ""
		container := ""
		if len(args) > 1 {
			remote, container = config.ParseRemoteAndContainer(args[1])
			if container == "" {
				return fmt.Errorf(gettext.Gettext("Show for remotes is not yet supported\n"))
			}
		}

		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		var data []byte

		if len(args) == 1 || container == "" {
			config, err := d.ServerStatus()
			if err != nil {
				return err
			}

			brief := config.BriefState()
			data, err = yaml.Marshal(&brief)
		} else {
			config, err := d.ContainerStatus(container, false)
			if err != nil {
				return err
			}

			brief := config.BriefState()
			data, err = yaml.Marshal(&brief)
		}

		fmt.Printf("%s", data)

		return nil

	case "get":
		if len(args) != 3 {
			return errArgs
		}

		remote, container := config.ParseRemoteAndContainer(args[1])
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		resp, err := d.ContainerStatus(container, false)
		if err != nil {
			return err
		}
		fmt.Printf("%s: %s\n", args[2], resp.Config[args[2]])
		return nil

	case "profile":
	case "device":
		if len(args) < 2 {
			return errArgs
		}
		switch args[1] {
		case "list":
			return deviceList(config, "container", args)
		case "add":
			return deviceAdd(config, "container", args)
		case "remove":
			return deviceRm(config, "container", args)
		case "show":
			return deviceShow(config, "container", args)
		default:
			return errArgs
		}

	case "edit":
		if len(args) != 2 {
			return errArgs
		}

		remote, container := config.ParseRemoteAndContainer(args[1])
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		return doConfigEdit(d, container)

	default:
		return errArgs
	}

	return errArgs
}

func doConfigEdit(client *lxd.Client, cont string) error {
	if !terminal.IsTerminal(syscall.Stdin) {
		contents, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return err
		}

		newdata := shared.BriefContainerState{}
		err = yaml.Unmarshal(contents, &newdata)
		if err != nil {
			return err
		}
		return client.UpdateContainerConfig(cont, newdata)
	}

	config, err := client.ContainerStatus(cont, false)
	if err != nil {
		return err
	}

	brief := config.BriefState()

	editor := os.Getenv("VISUAL")
	if editor == "" {
		editor = os.Getenv("EDITOR")
		if editor == "" {
			editor = "vi"
		}
	}
	data, err := yaml.Marshal(&brief)
	f, err := ioutil.TempFile("", "lxd_lxc_config_")
	if err != nil {
		return err
	}
	fname := f.Name()
	if err = f.Chmod(0600); err != nil {
		f.Close()
		os.Remove(fname)
		return err
	}
	f.Write([]byte(configEditHelp))
	f.Write(data)
	f.Close()
	defer os.Remove(fname)

	for {
		cmd := exec.Command(editor, fname)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Run()
		if err != nil {
			return err
		}
		contents, err := ioutil.ReadFile(fname)
		if err != nil {
			return err
		}
		newdata := shared.BriefContainerState{}
		err = yaml.Unmarshal(contents, &newdata)
		if err != nil {
			fmt.Fprintf(os.Stderr, gettext.Gettext("YAML parse error %v\n"), err)
			fmt.Printf("Press enter to play again ")
			_, err := os.Stdin.Read(make([]byte, 1))
			if err != nil {
				return err
			}

			continue
		}
		err = client.UpdateContainerConfig(cont, newdata)
		break
	}
	return err
}

func deviceAdd(config *lxd.Config, which string, args []string) error {
	if len(args) < 5 {
		return errArgs
	}
	remote, name := config.ParseRemoteAndContainer(args[2])

	client, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	devname := args[3]
	devtype := args[4]
	var props []string
	if len(args) > 5 {
		props = args[5:]
	} else {
		props = []string{}
	}

	var resp *lxd.Response
	if which == "profile" {
		resp, err = client.ProfileDeviceAdd(name, devname, devtype, props)
	} else {
		resp, err = client.ContainerDeviceAdd(name, devname, devtype, props)
	}
	if err != nil {
		return err
	}
	fmt.Printf(gettext.Gettext("Device %s added to %s\n"), devname, name)
	if which == "profile" {
		return nil
	}
	return client.WaitForSuccess(resp.Operation)
}

func deviceRm(config *lxd.Config, which string, args []string) error {
	if len(args) < 4 {
		return errArgs
	}
	remote, name := config.ParseRemoteAndContainer(args[2])

	client, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	devname := args[3]
	var resp *lxd.Response
	if which == "profile" {
		resp, err = client.ProfileDeviceDelete(name, devname)
	} else {
		resp, err = client.ContainerDeviceDelete(name, devname)
	}
	if err != nil {
		return err
	}
	fmt.Printf(gettext.Gettext("Device %s removed from %s\n"), devname, name)
	if which == "profile" {
		return nil
	}
	return client.WaitForSuccess(resp.Operation)
}

func deviceList(config *lxd.Config, which string, args []string) error {
	if len(args) < 3 {
		return errArgs
	}
	remote, name := config.ParseRemoteAndContainer(args[2])

	client, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	var resp []string
	if which == "profile" {
		resp, err = client.ProfileListDevices(name)
	} else {
		resp, err = client.ContainerListDevices(name)
	}
	if err != nil {
		return err
	}
	fmt.Printf("%s\n", strings.Join(resp, "\n"))

	return nil
}

func deviceShow(config *lxd.Config, which string, args []string) error {
	if len(args) < 3 {
		return errArgs
	}
	remote, name := config.ParseRemoteAndContainer(args[2])

	client, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	var devices map[string]shared.Device
	if which == "profile" {
		resp, err := client.ProfileConfig(name)
		if err != nil {
			return err
		}

		devices = resp.Devices

	} else {
		resp, err := client.ContainerStatus(name, false)
		if err != nil {
			return err
		}

		devices = resp.Devices
	}

	for n, d := range devices {
		fmt.Printf("%s\n", n)
		for attr, val := range d {
			fmt.Printf("  %s: %s\n", attr, val)
		}
	}

	return nil
}
<commit_msg>Add comments to clarify set/unset argcount logic<commit_after>package main

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

	"github.com/gosexy/gettext"
	"golang.org/x/crypto/ssh/terminal"
	"gopkg.in/yaml.v2"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

type configCmd struct {
	httpAddr string
}

func (c *configCmd) showByDefault() bool {
	return true
}

var configEditHelp string = gettext.Gettext(
	"### This is a yaml representation of the configuration.\n" +
		"### Any line starting with a '# will be ignored.\n" +
		"###\n" +
		"### A sample configuration looks like:\n" +
		"### name: container1\n" +
		"### profiles:\n" +
		"### - default\n" +
		"### config:\n" +
		"###   volatile.eth0.hwaddr: 00:16:3e:e9:f8:7f\n" +
		"### devices:\n" +
		"###   homedir:\n" +
		"###     path: /extra\n" +
		"###     source: /home/user\n" +
		"###     type: disk\n" +
		"### ephemeral: false\n" +
		"###\n" +
		"### Note that the name is shown but cannot be changed\n")

func (c *configCmd) usage() string {
	return gettext.Gettext(
		"Manage configuration.\n" +
			"\n" +
			"lxc config device add <container> <name> <type> [key=value]...\n" +
			"               Add a device to a container\n" +
			"lxc config device list <container>                     List devices for container\n" +
			"lxc config device show <container>                     Show full device details for container\n" +
			"lxc config device remove <container> <name>            Remove device from container\n" +
			"lxc config edit <container>                            Edit container configuration in external editor\n" +
			"lxc config get <container> key                         Get configuration key\n" +
			"lxc config set <container> key value                   Set container configuration key\n" +
			"lxc config unset <container> key                       Unset container configuration key\n" +
			"lxc config set key value                               Set server configuration key\n" +
			"lxc config unset key                                   Unset server configuration key\n" +
			"lxc config show <container>                            Show container configuration\n" +
			"lxc config trust list [remote]                         List all trusted certs.\n" +
			"lxc config trust add [remote] [certfile.crt]           Add certfile.crt to trusted hosts.\n" +
			"lxc config trust remove [remote] [hostname|fingerprint]\n" +
			"               Remove the cert from trusted hosts.\n" +
			"\n" +
			"Examples:\n" +
			"To mount host's /share/c1 onto /opt in the container:\n" +
			"\tlxc config device add container1 mntdir disk source=/share/c1 path=opt\n" +
			"To set an lxc config value:\n" +
			"\tlxc config set <container> raw.lxc 'lxc.aa_allow_incomplete = 1'\n" +
			"To set the server trust password:\n" +
			"\tlxc config set core.trust_password blah\n")
}

func (c *configCmd) flags() {}

func doSet(config *lxd.Config, args []string) error {
	if len(args) != 4 {
		return errArgs
	}

	// [[lxc config]] set dakara:c1 limits.memory 200000
	remote, container := config.ParseRemoteAndContainer(args[1])
	d, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	key := args[2]
	value := args[3]
	resp, err := d.SetContainerConfig(container, key, value)
	if err != nil {
		return err
	}
	return d.WaitForSuccess(resp.Operation)
}

func (c *configCmd) run(config *lxd.Config, args []string) error {
	if len(args) < 1 {
		return errArgs
	}

	switch args[0] {

	case "unset":
		if len(args) < 2 {
			return errArgs
		}

		// 2 args means we're unsetting a server key
		if len(args) == 2 {
			key := args[1]
			c, err := lxd.NewClient(config, "")
			if err != nil {
				return err
			}
			_, err = c.SetServerConfig(key, "")
			return err
		}

		// 3 args is a container config key
		return doSet(config, append(args, ""))

	case "set":
		if len(args) < 3 {
			return errArgs
		}

		// 3 args means we're setting a server key
		if len(args) == 3 {
			key := args[1]
			c, err := lxd.NewClient(config, "")
			if err != nil {
				return err
			}
			_, err = c.SetServerConfig(key, args[2])
			return err
		}

		// 4 args is a container config key
		return doSet(config, args)

	case "trust":
		if len(args) < 2 {
			return errArgs
		}

		switch args[1] {
		case "list":
			var remote string
			if len(args) == 3 {
				remote = config.ParseRemote(args[2])
			} else {
				remote = config.DefaultRemote
			}

			d, err := lxd.NewClient(config, remote)
			if err != nil {
				return err
			}

			trust, err := d.CertificateList()
			if err != nil {
				return err
			}

			for _, fingerprint := range trust {
				fmt.Println(fmt.Sprintf("%s", fingerprint))
			}

			return nil
		case "add":
			var remote string
			if len(args) < 3 {
				return fmt.Errorf(gettext.Gettext("No cert provided to add"))
			} else if len(args) == 4 {
				remote = config.ParseRemote(args[2])
			} else {
				remote = config.DefaultRemote
			}

			d, err := lxd.NewClient(config, remote)
			if err != nil {
				return err
			}

			fname := args[len(args)-1]
			cert, err := shared.ReadCert(fname)
			if err != nil {
				return err
			}

			name, _ := shared.SplitExt(fname)
			return d.CertificateAdd(cert, name)
		case "remove":
			var remote string
			if len(args) < 3 {
				return fmt.Errorf(gettext.Gettext("No fingerprint specified."))
			} else if len(args) == 4 {
				remote = config.ParseRemote(args[2])
			} else {
				remote = config.DefaultRemote
			}

			d, err := lxd.NewClient(config, remote)
			if err != nil {
				return err
			}

			return d.CertificateRemove(args[len(args)-1])
		default:
			return fmt.Errorf(gettext.Gettext("Unkonwn config trust command %s"), args[1])
		}

	case "show":
		remote := ""
		container := ""
		if len(args) > 1 {
			remote, container = config.ParseRemoteAndContainer(args[1])
			if container == "" {
				return fmt.Errorf(gettext.Gettext("Show for remotes is not yet supported\n"))
			}
		}

		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		var data []byte

		if len(args) == 1 || container == "" {
			config, err := d.ServerStatus()
			if err != nil {
				return err
			}

			brief := config.BriefState()
			data, err = yaml.Marshal(&brief)
		} else {
			config, err := d.ContainerStatus(container, false)
			if err != nil {
				return err
			}

			brief := config.BriefState()
			data, err = yaml.Marshal(&brief)
		}

		fmt.Printf("%s", data)

		return nil

	case "get":
		if len(args) != 3 {
			return errArgs
		}

		remote, container := config.ParseRemoteAndContainer(args[1])
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		resp, err := d.ContainerStatus(container, false)
		if err != nil {
			return err
		}
		fmt.Printf("%s: %s\n", args[2], resp.Config[args[2]])
		return nil

	case "profile":
	case "device":
		if len(args) < 2 {
			return errArgs
		}
		switch args[1] {
		case "list":
			return deviceList(config, "container", args)
		case "add":
			return deviceAdd(config, "container", args)
		case "remove":
			return deviceRm(config, "container", args)
		case "show":
			return deviceShow(config, "container", args)
		default:
			return errArgs
		}

	case "edit":
		if len(args) != 2 {
			return errArgs
		}

		remote, container := config.ParseRemoteAndContainer(args[1])
		d, err := lxd.NewClient(config, remote)
		if err != nil {
			return err
		}

		return doConfigEdit(d, container)

	default:
		return errArgs
	}

	return errArgs
}

func doConfigEdit(client *lxd.Client, cont string) error {
	if !terminal.IsTerminal(syscall.Stdin) {
		contents, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return err
		}

		newdata := shared.BriefContainerState{}
		err = yaml.Unmarshal(contents, &newdata)
		if err != nil {
			return err
		}
		return client.UpdateContainerConfig(cont, newdata)
	}

	config, err := client.ContainerStatus(cont, false)
	if err != nil {
		return err
	}

	brief := config.BriefState()

	editor := os.Getenv("VISUAL")
	if editor == "" {
		editor = os.Getenv("EDITOR")
		if editor == "" {
			editor = "vi"
		}
	}
	data, err := yaml.Marshal(&brief)
	f, err := ioutil.TempFile("", "lxd_lxc_config_")
	if err != nil {
		return err
	}
	fname := f.Name()
	if err = f.Chmod(0600); err != nil {
		f.Close()
		os.Remove(fname)
		return err
	}
	f.Write([]byte(configEditHelp))
	f.Write(data)
	f.Close()
	defer os.Remove(fname)

	for {
		cmd := exec.Command(editor, fname)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Run()
		if err != nil {
			return err
		}
		contents, err := ioutil.ReadFile(fname)
		if err != nil {
			return err
		}
		newdata := shared.BriefContainerState{}
		err = yaml.Unmarshal(contents, &newdata)
		if err != nil {
			fmt.Fprintf(os.Stderr, gettext.Gettext("YAML parse error %v\n"), err)
			fmt.Printf("Press enter to play again ")
			_, err := os.Stdin.Read(make([]byte, 1))
			if err != nil {
				return err
			}

			continue
		}
		err = client.UpdateContainerConfig(cont, newdata)
		break
	}
	return err
}

func deviceAdd(config *lxd.Config, which string, args []string) error {
	if len(args) < 5 {
		return errArgs
	}
	remote, name := config.ParseRemoteAndContainer(args[2])

	client, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	devname := args[3]
	devtype := args[4]
	var props []string
	if len(args) > 5 {
		props = args[5:]
	} else {
		props = []string{}
	}

	var resp *lxd.Response
	if which == "profile" {
		resp, err = client.ProfileDeviceAdd(name, devname, devtype, props)
	} else {
		resp, err = client.ContainerDeviceAdd(name, devname, devtype, props)
	}
	if err != nil {
		return err
	}
	fmt.Printf(gettext.Gettext("Device %s added to %s\n"), devname, name)
	if which == "profile" {
		return nil
	}
	return client.WaitForSuccess(resp.Operation)
}

func deviceRm(config *lxd.Config, which string, args []string) error {
	if len(args) < 4 {
		return errArgs
	}
	remote, name := config.ParseRemoteAndContainer(args[2])

	client, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	devname := args[3]
	var resp *lxd.Response
	if which == "profile" {
		resp, err = client.ProfileDeviceDelete(name, devname)
	} else {
		resp, err = client.ContainerDeviceDelete(name, devname)
	}
	if err != nil {
		return err
	}
	fmt.Printf(gettext.Gettext("Device %s removed from %s\n"), devname, name)
	if which == "profile" {
		return nil
	}
	return client.WaitForSuccess(resp.Operation)
}

func deviceList(config *lxd.Config, which string, args []string) error {
	if len(args) < 3 {
		return errArgs
	}
	remote, name := config.ParseRemoteAndContainer(args[2])

	client, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	var resp []string
	if which == "profile" {
		resp, err = client.ProfileListDevices(name)
	} else {
		resp, err = client.ContainerListDevices(name)
	}
	if err != nil {
		return err
	}
	fmt.Printf("%s\n", strings.Join(resp, "\n"))

	return nil
}

func deviceShow(config *lxd.Config, which string, args []string) error {
	if len(args) < 3 {
		return errArgs
	}
	remote, name := config.ParseRemoteAndContainer(args[2])

	client, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	var devices map[string]shared.Device
	if which == "profile" {
		resp, err := client.ProfileConfig(name)
		if err != nil {
			return err
		}

		devices = resp.Devices

	} else {
		resp, err := client.ContainerStatus(name, false)
		if err != nil {
			return err
		}

		devices = resp.Devices
	}

	for n, d := range devices {
		fmt.Printf("%s\n", n)
		for attr, val := range d {
			fmt.Printf("  %s: %s\n", attr, val)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// +build appengine

package data

import (
	"errors"
	"math/rand"
	"sync"
	"time"

	"appengine"
	"appengine/datastore"
)

// User is the datastore information for an individual user
type User struct {
	Name         string `datastore:",noindex"`
	SecureKey    string `datastore:",noindex"`
	EMailAddress string `datastore:",index"`
}

// StoreUser stores the user in the database
func (u *User) Store(c appengine.Context) (*datastore.Key, error) {

	u.SecureKey = generateSecureKey()

	k := datastore.NewKey(c, "User", "", 0, nil)
	k, err := datastore.Put(c, k, u)

	if err != nil {
		c.Errorf("Error while storing user in datastore. User: %v. Error: %v", u, err)
	}

	return k, err
}

// DeleteUser removes a user and all its data from the database
func DeleteUser(c appengine.Context, k *datastore.Key, secureKey string) error {

	u := GetUserByKey(c, k)
	if u == nil {
		c.Infof("User not found. Key invalid")
		return errors.New("Invalid key.")
	}

	// validate secure key
	if u.SecureKey != secureKey {
		c.Infof("Wrong secure key for deleting User %v", u.EMailAddress)
		return errors.New("Invalid key. " + secureKey + " - " + u.SecureKey)
	}

	err := datastore.Delete(c, k)
	if err != nil {
		c.Errorf("Error while deleting user from datastore. Error: %v", err)
	}

	// TODO delete every user data

	return err
}

// GetUserKeyByEmail returns the user key based on the email
func GetUserKeyByEmail(c appengine.Context, email string) *datastore.Key {
	c.Infof("Searching for user: %v", email)

	q := datastore.NewQuery("User").
		Filter("EMailAddress =", email).
		Limit(1).
		KeysOnly()

	keys, err := q.GetAll(c, nil)
	if err != nil {
		c.Errorf("Error at user key query: %v", err)
		return nil
	}

	if len(keys) == 0 {
		c.Infof("Not found.")
		return nil
	}

	return keys[0]
}

// GetUserByKey returns the user based on its key
func GetUserByKey(c appengine.Context, key *datastore.Key) *User {
	var u User
	if err := datastore.Get(c, key, &u); err != nil {
		c.Errorf("Error while getting user based on key: %v", err)
		return nil
	}

	return &u
}

// Used to initialize the seed for random just once
var randomSeedInit sync.Once

func generateSecureKey() string {
	randomSeedInit.Do(func() {
		rand.Seed(time.Now().UTC().UnixNano())
	})

	keySize := 32
	var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	b := make([]rune, keySize)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}
<commit_msg>Some cleanup.<commit_after>// +build appengine

package data

import (
	"errors"
	"math/rand"
	"sync"
	"time"

	"appengine"
	"appengine/datastore"
)

// User is the datastore information for an individual user
type User struct {
	Name         string `datastore:",noindex"`
	SecureKey    string `datastore:",noindex"`
	EMailAddress string `datastore:",index"`
}

// Store stores the user in the database
func (u *User) Store(c appengine.Context) (*datastore.Key, error) {

	u.SecureKey = generateSecureKey()

	k := datastore.NewKey(c, "User", "", 0, nil)
	k, err := datastore.Put(c, k, u)

	if err != nil {
		c.Errorf("Error while storing user in datastore. User: %v. Error: %v", u, err)
	}

	return k, err
}

// DeleteUser removes a user and all its data from the database
func DeleteUser(c appengine.Context, k *datastore.Key, secureKey string) error {

	u := GetUserByKey(c, k)
	if u == nil {
		c.Infof("User not found. Key invalid")
		return errors.New("Invalid key.")
	}

	// validate secure key
	if u.SecureKey != secureKey {
		c.Infof("Wrong secure key for deleting User %v", u.EMailAddress)
		return errors.New("Invalid key. " + secureKey + " - " + u.SecureKey)
	}

	err := datastore.Delete(c, k)
	if err != nil {
		c.Errorf("Error while deleting user from datastore. Error: %v", err)
	}

	// TODO delete every user data

	return err
}

// GetUserKeyByEmail returns the user key based on the email
func GetUserKeyByEmail(c appengine.Context, email string) *datastore.Key {
	c.Infof("Searching for user: %v", email)

	q := datastore.NewQuery("User").
		Filter("EMailAddress =", email).
		Limit(1).
		KeysOnly()

	keys, err := q.GetAll(c, nil)
	if err != nil {
		c.Errorf("Error at user key query: %v", err)
		return nil
	}

	if len(keys) == 0 {
		c.Infof("Not found.")
		return nil
	}

	return keys[0]
}

// GetUserByKey returns the user based on its key
func GetUserByKey(c appengine.Context, key *datastore.Key) *User {
	var u User
	if err := datastore.Get(c, key, &u); err != nil {
		c.Errorf("Error while getting user based on key: %v", err)
		return nil
	}

	return &u
}

// Used to initialize the seed for random just once
var randomSeedInit sync.Once

// Alphabet for the secure key
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

// Length of the secure key
const keySize = 32

func generateSecureKey() string {
	randomSeedInit.Do(func() {
		rand.Seed(time.Now().UTC().UnixNano())
	})

	b := make([]rune, keySize)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Stuart Glenn
// All rights reserved
// Use of this source code is goverened by a BSD 3-clause license,
// see included LICENSE file for details
// Datastore for perstiance of access rules, clients & keys
package atm

import (
	"database/sql"
	"errors"
	"fmt"
)

type Datastore struct {
	pool        *sql.DB
	signingKeys *Cache
}

type Account struct {
	Id   string `json:id`
	Name string `json:name`
}

func NewDatastore(driver, dsn string) (*Datastore, error) {
	ds := &Datastore{}
	var err error
	ds.pool, err = sql.Open(driver, dsn)
	if nil != err {
		return nil, err
	}
	err = ds.Ping()
	if nil != err {
		return nil, err
	}
	ds.signingKeys = NewCache()
	return ds, nil
}

func (d *Datastore) Ping() error {
	return d.pool.Ping()
}

func (d *Datastore) Close() error {
	return d.pool.Close()
}

func (d *Datastore) RemoveSigningKeyForAccount(account string) {
	d.signingKeys.Delete(account)
}

func (d *Datastore) AddSigningKeyForAccount(key, account string) {
	d.signingKeys.Set(account, key)
}

func (d *Datastore) signingKeyFor(account string) string {
	if k, found := d.signingKeys.Get(account); found {
		return k.(string)
	}
	return ""
}

func (d *Datastore) Account(name string) (*Account, error) {
	a := &Account{}
	stmt, err := d.pool.Prepare("SELECT id, name from accounts where name = ?")
	if nil != err {
		return a, err
	}
	defer stmt.Close()
	rows, err := stmt.Query(name)
	if nil != err {
		return a, err
	}
	defer rows.Close()
	numRows := 0
	for rows.Next() {
		if numRows > 1 {
			return a, errors.New("Too many results")
		}
		err := rows.Scan(&a.Id, &a.Name)
		if nil != err {
			return a, err
		}
		err = rows.Err()
		if nil != err {
			return a, err
		}
		numRows++
	}
	return a, nil
}

func (d *Datastore) KeyForRequest(u *UrlRequest, appId string) (string, int64, error) {
	var signing_key string
	var duration int64
	stmt, err := d.pool.Prepare("SELECT a.id, r.duration as duration from accounts a, rules r " +
		"WHERE r.account_id=a.id AND requestor_id = ? AND a.name = ? AND " +
		"? REGEXP r.container AND ? REGEXP r.object AND r.method = ?")
	if nil != err {
		return signing_key, duration, err
	}
	defer stmt.Close()
	rows, err := stmt.Query(appId, u.Account, u.Container, u.Object, u.Method)
	if nil != err {
		return signing_key, duration, err
	}
	defer rows.Close()
	numRows := 0
	var grantingAccountId string
	for rows.Next() {
		//if numRows > 1 {
		//return signing_key, duration, errors.New("Too many results")
		//}
		err := rows.Scan(&grantingAccountId, &duration)
		if nil != err {
			return signing_key, duration, err
		}
		err = rows.Err()
		if nil != err {
			return signing_key, duration, err
		}
		numRows++
	}
	if 0 == numRows {
		return "", 0, nil
	}

	signing_key = d.signingKeyFor(grantingAccountId)
	if "" == signing_key {
		return signing_key, 0, errors.New(fmt.Sprintf("Key not set for %s", u.Account))
	}

	return signing_key, duration, nil
}

func (d *Datastore) ApiKeySecret(apiKey string) (string, error) {
	var secret string
	stmt, err := d.pool.Prepare("SELECT secret from accounts where id = ?")
	if nil != err {
		return secret, err
	}
	defer stmt.Close()
	rows, err := stmt.Query(apiKey)
	if nil != err {
		return secret, err
	}
	defer rows.Close()
	numRows := 0
	for rows.Next() {
		if numRows > 1 {
			return secret, errors.New("Too many results")
		}
		err := rows.Scan(&secret)
		if nil != err {
			return "", err
		}
		err = rows.Err()
		if nil != err {
			return "", err
		}
		numRows++
	}
	if 0 == numRows || "" == secret {
		return "", errors.New(fmt.Sprintf("No secret key for api: %s", apiKey))
	}

	return secret, nil
}
<commit_msg>Add an expiring hash cache thing for temp nonce store<commit_after>// Copyright (c) 2015 Stuart Glenn
// All rights reserved
// Use of this source code is goverened by a BSD 3-clause license,
// see included LICENSE file for details
// Datastore for perstiance of access rules, clients & keys
package atm

import (
	"database/sql"
	"errors"
	"fmt"
	"time"
)

type NonceStore struct {
	nonces *ExpiringCache
}

func NewNonceStore() NonceStore {
	return NonceStore{nonces: NewExpiringCache(5 * time.Minute)}
}

func (d NonceStore) Add(n string) {
	d.nonces.Set(n, "", 10*time.Minute)
}

func (d NonceStore) Valid(n string) bool {
	_, found := d.nonces.Get(n)
	return !found
}

type Datastore struct {
	pool        *sql.DB
	signingKeys *Cache
}

type Account struct {
	Id   string `json:id`
	Name string `json:name`
}

func NewDatastore(driver, dsn string) (*Datastore, error) {
	ds := &Datastore{}
	var err error
	ds.pool, err = sql.Open(driver, dsn)
	if nil != err {
		return nil, err
	}
	err = ds.Ping()
	if nil != err {
		return nil, err
	}
	ds.signingKeys = NewCache()
	return ds, nil
}

func (d *Datastore) Ping() error {
	return d.pool.Ping()
}

func (d *Datastore) Close() error {
	return d.pool.Close()
}

func (d *Datastore) RemoveSigningKeyForAccount(account string) {
	d.signingKeys.Delete(account)
}

func (d *Datastore) AddSigningKeyForAccount(key, account string) {
	d.signingKeys.Set(account, key)
}

func (d *Datastore) signingKeyFor(account string) string {
	if k, found := d.signingKeys.Get(account); found {
		return k.(string)
	}
	return ""
}

func (d *Datastore) Account(name string) (*Account, error) {
	a := &Account{}
	stmt, err := d.pool.Prepare("SELECT id, name from accounts where name = ?")
	if nil != err {
		return a, err
	}
	defer stmt.Close()
	rows, err := stmt.Query(name)
	if nil != err {
		return a, err
	}
	defer rows.Close()
	numRows := 0
	for rows.Next() {
		if numRows > 1 {
			return a, errors.New("Too many results")
		}
		err := rows.Scan(&a.Id, &a.Name)
		if nil != err {
			return a, err
		}
		err = rows.Err()
		if nil != err {
			return a, err
		}
		numRows++
	}
	return a, nil
}

func (d *Datastore) KeyForRequest(u *UrlRequest, appId string) (string, int64, error) {
	var signing_key string
	var duration int64
	stmt, err := d.pool.Prepare("SELECT a.id, r.duration as duration from accounts a, rules r " +
		"WHERE r.account_id=a.id AND requestor_id = ? AND a.name = ? AND " +
		"? REGEXP r.container AND ? REGEXP r.object AND r.method = ?")
	if nil != err {
		return signing_key, duration, err
	}
	defer stmt.Close()
	rows, err := stmt.Query(appId, u.Account, u.Container, u.Object, u.Method)
	if nil != err {
		return signing_key, duration, err
	}
	defer rows.Close()
	numRows := 0
	var grantingAccountId string
	for rows.Next() {
		//if numRows > 1 {
		//return signing_key, duration, errors.New("Too many results")
		//}
		err := rows.Scan(&grantingAccountId, &duration)
		if nil != err {
			return signing_key, duration, err
		}
		err = rows.Err()
		if nil != err {
			return signing_key, duration, err
		}
		numRows++
	}
	if 0 == numRows {
		return "", 0, nil
	}

	signing_key = d.signingKeyFor(grantingAccountId)
	if "" == signing_key {
		return signing_key, 0, errors.New(fmt.Sprintf("Key not set for %s", u.Account))
	}

	return signing_key, duration, nil
}

func (d *Datastore) ApiKeySecret(apiKey string) (string, error) {
	var secret string
	stmt, err := d.pool.Prepare("SELECT secret from accounts where id = ?")
	if nil != err {
		return secret, err
	}
	defer stmt.Close()
	rows, err := stmt.Query(apiKey)
	if nil != err {
		return secret, err
	}
	defer rows.Close()
	numRows := 0
	for rows.Next() {
		if numRows > 1 {
			return secret, errors.New("Too many results")
		}
		err := rows.Scan(&secret)
		if nil != err {
			return "", err
		}
		err = rows.Err()
		if nil != err {
			return "", err
		}
		numRows++
	}
	if 0 == numRows || "" == secret {
		return "", errors.New(fmt.Sprintf("No secret key for api: %s", apiKey))
	}

	return secret, nil
}
<|endoftext|>


<commit_before>package date

import (
	"bytes"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var marchMayChecker = regexp.MustCompile("M([^a]|$)")

func GetCurrentTime() time.Time {
	return time.Now().UTC()
}

func GenerateTimeAgo(date *time.Time) []byte {
	timeAgo := GetCurrentTime().Sub(*date)
	if timeAgo.Minutes() < 1 {
		return []byte("a few seconds ago")
	}
	if timeAgo.Minutes() < 2 {
		return []byte("a minute ago")
	}
	if timeAgo.Minutes() < 60 {
		var buffer bytes.Buffer
		buffer.WriteString(strconv.Itoa(int(timeAgo.Minutes())))
		buffer.WriteString(" minutes ago")
		return buffer.Bytes()
	}
	if timeAgo.Hours() < 2 {
		return []byte("an hour ago")
	}
	if timeAgo.Hours() < 24 {
		var buffer bytes.Buffer
		buffer.WriteString(strconv.Itoa(int(timeAgo.Hours())))
		buffer.WriteString(" hours ago")
		return buffer.Bytes()
	}
	if timeAgo.Hours() < 48 {
		return []byte("a day ago")
	}
	days := int(timeAgo.Hours() / 24)
	if days < 25 {
		var buffer bytes.Buffer
		buffer.WriteString(strconv.Itoa(days))
		buffer.WriteString(" days ago")
		return buffer.Bytes()
	}
	if days < 45 {
		return []byte("a month ago")
	}
	if days < 345 {
		months := days / 30
		if months < 2 {
			months = 2
		}
		var buffer bytes.Buffer
		buffer.WriteString(strconv.Itoa(months))
		buffer.WriteString(" months ago")
		return buffer.Bytes()
	}
	if days < 548 {
		return []byte("a year ago")
	}
	years := days / 365
	if years < 2 {
		years = 2
	}
	var buffer bytes.Buffer
	buffer.WriteString(strconv.Itoa(years))
	buffer.WriteString(" years ago")
	return buffer.Bytes()
}

func FormatDate(format string, date *time.Time) []byte {

	// Do these first (so they don't accidentally replace something the others insert)
	if strings.Contains(format, "h") {
		format = strings.Replace(format, "h", replaceh(date), -1)
	}
	format = strings.Replace(format, "s", strconv.Itoa(date.Second()), -1)

	// Year, month, and day
	if strings.Contains(format, "Do") {
		format = strings.Replace(format, "Do", replaceDo(date), -1)
	}
	format = strings.Replace(format, "YYYY", strconv.Itoa(date.Year()), -1)
	if date.Year() > 99 {
		format = strings.Replace(format, "YY", strconv.Itoa(date.Year())[2:], -1)
	}
	format = strings.Replace(format, "Q", strconv.Itoa(((int(date.Month())-1)/3)+1), -1)
	if strings.Contains(format, "DDDD") {
		format = strings.Replace(format, "DDDD", replaceDDDD(date), -1)
	}
	if strings.Contains(format, "DDD") {
		format = strings.Replace(format, "DDD", replaceDDD(date), -1)
	}
	if strings.Contains(format, "DD") {
		format = strings.Replace(format, "DD", replaceDD(date), -1)
	}
	format = strings.Replace(format, "X", strconv.FormatInt(date.Unix(), 10), -1)
	// Unix ms ('x') is not used by ghost. Excluding it for now.
	// format = strings.Replace(format, "x", strconv.FormatInt((date.UnixNano()/1000000), 10), -1)

	// Locale formats. Not supported yet
	format = strings.Replace(format, "gggg", strconv.Itoa(date.Year()), -1)
	if date.Year() > 99 {
		format = strings.Replace(format, "gg", strconv.Itoa(date.Year())[2:], -1)
	}
	if strings.Contains(format, "ww") {
		format = strings.Replace(format, "ww", replaceww(date), -1)
	}
	if strings.Contains(format, "w") {
		format = strings.Replace(format, "w", replacew(date), -1)
	}
	format = strings.Replace(format, "e", strconv.Itoa(int(date.Weekday())), -1)

	// ISO week date formats. Not supported yet - https://en.wikipedia.org/wiki/ISO_week_date
	format = strings.Replace(format, "GGGG", strconv.Itoa(date.Year()), -1)
	if date.Year() > 99 {
		format = strings.Replace(format, "GG", strconv.Itoa(date.Year())[2:], -1)
	}
	if strings.Contains(format, "WW") {
		format = strings.Replace(format, "WW", replaceww(date), -1)
	}
	if strings.Contains(format, "W") {
		format = strings.Replace(format, "W", replacew(date), -1)
	}
	format = strings.Replace(format, "E", strconv.Itoa(int(date.Weekday())), -1)

	// Hour, minute, second, millisecond, and offset
	if strings.Contains(format, "HH") {
		format = strings.Replace(format, "HH", replaceHH(date), -1)
	}
	format = strings.Replace(format, "H", strconv.Itoa(date.Hour()), -1)
	if strings.Contains(format, "hh") {
		format = strings.Replace(format, "hh", replacehh(date), -1)
	}
	if strings.Contains(format, "a") {
		format = strings.Replace(format, "a", replacea(date), -1)
	}
	if strings.Contains(format, "A") {
		format = strings.Replace(format, "A", replaceA(date), -1)
	}
	if strings.Contains(format, "mm") {
		format = strings.Replace(format, "mm", replacemm(date), -1)
	}
	format = strings.Replace(format, "m", strconv.Itoa(date.Minute()), -1)
	if strings.Contains(format, "ss") {
		format = strings.Replace(format, "ss", replacess(date), -1)
	}
	format = strings.Replace(format, "SSS", strconv.Itoa(date.Nanosecond()/1000000), -1)
	format = strings.Replace(format, "SS", strconv.Itoa(date.Nanosecond()/10000000), -1)
	format = strings.Replace(format, "S", strconv.Itoa(date.Nanosecond()/100000000), -1)
	if strings.Contains(format, "ZZ") {
		format = strings.Replace(format, "ZZ", replaceZZ(date), -1)
	}
	if strings.Contains(format, "Z") {
		format = strings.Replace(format, "Z", replaceZ(date), -1)
	}

	// Not documented for moment.js, but seems to be used by ghost themes
	if strings.Contains(format, "dddd") {
		format = strings.Replace(format, "dddd", date.Weekday().String(), -1)
	}

	// This needs to be last so that month strings don't interfere with the other replace functions
	format = strings.Replace(format, "MMMM", date.Month().String(), -1)
	if len(date.Month().String()) > 2 {
		format = strings.Replace(format, "MMM", date.Month().String()[:3], -1)
	}
	if strings.Contains(format, "MM") {
		format = strings.Replace(format, "MM", replaceMM(date), -1)
	}
	// Replace M - make sure the Ms in March and May don't get replaced. TODO: Regex could be improved, only recognizes 'M's that are not followed by 'a's.
	format = marchMayChecker.ReplaceAllString(format, strconv.Itoa(int(date.Month())))

	return []byte(format)
}

func replaceMM(date *time.Time) string {
	var buffer bytes.Buffer
	month := int(date.Month())
	if month < 10 {
		buffer.WriteString("0")
		buffer.WriteString(strconv.Itoa(month))
	} else {
		buffer.WriteString(strconv.Itoa(month))
	}
	return buffer.String()
}

func replaceDDDD(date *time.Time) string {
	var buffer bytes.Buffer
	startOfYear := time.Date((date.Year() - 1), time.December, 31, 0, 0, 0, 0, time.UTC)
	days := int(date.Sub(startOfYear) / (24 * time.Hour))
	if days < 10 {
		buffer.WriteString("00")
	} else if days < 100 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(days))
	return buffer.String()
}

func replaceDDD(date *time.Time) string {
	startOfYear := time.Date((date.Year() - 1), time.December, 31, 0, 0, 0, 0, time.UTC)
	return strconv.Itoa(int(date.Sub(startOfYear) / (24 * time.Hour)))
}

func replaceDD(date *time.Time) string {
	var buffer bytes.Buffer
	if date.Day() < 10 {
		buffer.WriteString("0")
		buffer.WriteString(strconv.Itoa(date.Day()))
	} else {
		buffer.WriteString(strconv.Itoa(date.Day()))
	}
	return buffer.String()
}

func replaceDo(date *time.Time) string {
	var buffer bytes.Buffer
	buffer.WriteString(strconv.Itoa(date.Day()))
	if date.Day() == 1 || date.Day() == 21 || date.Day() == 31 {
		buffer.WriteString("st")
	} else if date.Day() == 2 || date.Day() == 22 {
		buffer.WriteString("nd")
	} else if date.Day() == 3 || date.Day() == 23 {
		buffer.WriteString("rd")
	} else {
		buffer.WriteString("th")
	}
	return buffer.String()
}

func replaceww(date *time.Time) string {
	var buffer bytes.Buffer
	startOfYear := time.Date((date.Year() - 1), time.December, 25, 0, 0, 0, 0, time.UTC)
	weeks := int(date.Sub(startOfYear) / (24 * time.Hour * 7))
	if weeks < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(weeks))
	return buffer.String()
}

func replacew(date *time.Time) string {
	startOfYear := time.Date((date.Year() - 1), time.December, 25, 0, 0, 0, 0, time.UTC)
	return strconv.Itoa(int(date.Sub(startOfYear) / (24 * time.Hour * 7)))
}

func replaceHH(date *time.Time) string {
	var buffer bytes.Buffer
	hour := date.Hour()
	if hour < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(hour))
	return buffer.String()
}

func replacehh(date *time.Time) string {
	var buffer bytes.Buffer
	hour := date.Hour()
	if hour == 0 {
		hour = 12
	} else if hour > 12 {
		hour = hour - 12
	}
	if hour < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(hour))
	return buffer.String()
}

func replaceh(date *time.Time) string {
	var buffer bytes.Buffer
	hour := date.Hour()
	if hour == 0 {
		hour = 12
	} else if hour > 12 {
		hour = hour - 12
	}
	buffer.WriteString(strconv.Itoa(hour))
	return buffer.String()
}

func replacea(date *time.Time) string {
	if date.Hour() < 12 {
		return "am"
	}
	return "pm"
}

func replaceA(date *time.Time) string {
	if date.Hour() < 12 {
		return "AM"
	}
	return "PM"
}

func replacemm(date *time.Time) string {
	var buffer bytes.Buffer
	minute := date.Minute()
	if minute < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(minute))
	return buffer.String()
}

func replacess(date *time.Time) string {
	var buffer bytes.Buffer
	second := date.Second()
	if second < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(second))
	return buffer.String()
}

func replaceZZ(date *time.Time) string {
	var buffer bytes.Buffer
	_, offset := date.In(time.Local).Zone()
	offset = offset / 3600
	if offset > 0 {
		buffer.WriteString("+")
	} else {
		buffer.WriteString("-")
	}
	if offset < 0 {
		offset = -offset
	}
	if offset < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(offset))
	buffer.WriteString("00")
	return buffer.String()
}

func replaceZ(date *time.Time) string {
	var buffer bytes.Buffer
	_, offset := date.In(time.Local).Zone()
	offset = offset / 3600
	if offset > 0 {
		buffer.WriteString("+")
	} else {
		buffer.WriteString("-")
	}
	if offset < 0 {
		offset = -offset
	}
	if offset < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(offset))
	buffer.WriteString(":00")
	return buffer.String()
}
<commit_msg>Added comment to date.GetCurrentTime()<commit_after>package date

import (
	"bytes"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var marchMayChecker = regexp.MustCompile("M([^a]|$)")

// Whenever we need time.Now(), we use this function instead so that we always use UTC in journey
func GetCurrentTime() time.Time {
	return time.Now().UTC()
}

func GenerateTimeAgo(date *time.Time) []byte {
	timeAgo := GetCurrentTime().Sub(*date)
	if timeAgo.Minutes() < 1 {
		return []byte("a few seconds ago")
	}
	if timeAgo.Minutes() < 2 {
		return []byte("a minute ago")
	}
	if timeAgo.Minutes() < 60 {
		var buffer bytes.Buffer
		buffer.WriteString(strconv.Itoa(int(timeAgo.Minutes())))
		buffer.WriteString(" minutes ago")
		return buffer.Bytes()
	}
	if timeAgo.Hours() < 2 {
		return []byte("an hour ago")
	}
	if timeAgo.Hours() < 24 {
		var buffer bytes.Buffer
		buffer.WriteString(strconv.Itoa(int(timeAgo.Hours())))
		buffer.WriteString(" hours ago")
		return buffer.Bytes()
	}
	if timeAgo.Hours() < 48 {
		return []byte("a day ago")
	}
	days := int(timeAgo.Hours() / 24)
	if days < 25 {
		var buffer bytes.Buffer
		buffer.WriteString(strconv.Itoa(days))
		buffer.WriteString(" days ago")
		return buffer.Bytes()
	}
	if days < 45 {
		return []byte("a month ago")
	}
	if days < 345 {
		months := days / 30
		if months < 2 {
			months = 2
		}
		var buffer bytes.Buffer
		buffer.WriteString(strconv.Itoa(months))
		buffer.WriteString(" months ago")
		return buffer.Bytes()
	}
	if days < 548 {
		return []byte("a year ago")
	}
	years := days / 365
	if years < 2 {
		years = 2
	}
	var buffer bytes.Buffer
	buffer.WriteString(strconv.Itoa(years))
	buffer.WriteString(" years ago")
	return buffer.Bytes()
}

func FormatDate(format string, date *time.Time) []byte {

	// Do these first (so they don't accidentally replace something the others insert)
	if strings.Contains(format, "h") {
		format = strings.Replace(format, "h", replaceh(date), -1)
	}
	format = strings.Replace(format, "s", strconv.Itoa(date.Second()), -1)

	// Year, month, and day
	if strings.Contains(format, "Do") {
		format = strings.Replace(format, "Do", replaceDo(date), -1)
	}
	format = strings.Replace(format, "YYYY", strconv.Itoa(date.Year()), -1)
	if date.Year() > 99 {
		format = strings.Replace(format, "YY", strconv.Itoa(date.Year())[2:], -1)
	}
	format = strings.Replace(format, "Q", strconv.Itoa(((int(date.Month())-1)/3)+1), -1)
	if strings.Contains(format, "DDDD") {
		format = strings.Replace(format, "DDDD", replaceDDDD(date), -1)
	}
	if strings.Contains(format, "DDD") {
		format = strings.Replace(format, "DDD", replaceDDD(date), -1)
	}
	if strings.Contains(format, "DD") {
		format = strings.Replace(format, "DD", replaceDD(date), -1)
	}
	format = strings.Replace(format, "X", strconv.FormatInt(date.Unix(), 10), -1)
	// Unix ms ('x') is not used by ghost. Excluding it for now.
	// format = strings.Replace(format, "x", strconv.FormatInt((date.UnixNano()/1000000), 10), -1)

	// Locale formats. Not supported yet
	format = strings.Replace(format, "gggg", strconv.Itoa(date.Year()), -1)
	if date.Year() > 99 {
		format = strings.Replace(format, "gg", strconv.Itoa(date.Year())[2:], -1)
	}
	if strings.Contains(format, "ww") {
		format = strings.Replace(format, "ww", replaceww(date), -1)
	}
	if strings.Contains(format, "w") {
		format = strings.Replace(format, "w", replacew(date), -1)
	}
	format = strings.Replace(format, "e", strconv.Itoa(int(date.Weekday())), -1)

	// ISO week date formats. Not supported yet - https://en.wikipedia.org/wiki/ISO_week_date
	format = strings.Replace(format, "GGGG", strconv.Itoa(date.Year()), -1)
	if date.Year() > 99 {
		format = strings.Replace(format, "GG", strconv.Itoa(date.Year())[2:], -1)
	}
	if strings.Contains(format, "WW") {
		format = strings.Replace(format, "WW", replaceww(date), -1)
	}
	if strings.Contains(format, "W") {
		format = strings.Replace(format, "W", replacew(date), -1)
	}
	format = strings.Replace(format, "E", strconv.Itoa(int(date.Weekday())), -1)

	// Hour, minute, second, millisecond, and offset
	if strings.Contains(format, "HH") {
		format = strings.Replace(format, "HH", replaceHH(date), -1)
	}
	format = strings.Replace(format, "H", strconv.Itoa(date.Hour()), -1)
	if strings.Contains(format, "hh") {
		format = strings.Replace(format, "hh", replacehh(date), -1)
	}
	if strings.Contains(format, "a") {
		format = strings.Replace(format, "a", replacea(date), -1)
	}
	if strings.Contains(format, "A") {
		format = strings.Replace(format, "A", replaceA(date), -1)
	}
	if strings.Contains(format, "mm") {
		format = strings.Replace(format, "mm", replacemm(date), -1)
	}
	format = strings.Replace(format, "m", strconv.Itoa(date.Minute()), -1)
	if strings.Contains(format, "ss") {
		format = strings.Replace(format, "ss", replacess(date), -1)
	}
	format = strings.Replace(format, "SSS", strconv.Itoa(date.Nanosecond()/1000000), -1)
	format = strings.Replace(format, "SS", strconv.Itoa(date.Nanosecond()/10000000), -1)
	format = strings.Replace(format, "S", strconv.Itoa(date.Nanosecond()/100000000), -1)
	if strings.Contains(format, "ZZ") {
		format = strings.Replace(format, "ZZ", replaceZZ(date), -1)
	}
	if strings.Contains(format, "Z") {
		format = strings.Replace(format, "Z", replaceZ(date), -1)
	}

	// Not documented for moment.js, but seems to be used by ghost themes
	if strings.Contains(format, "dddd") {
		format = strings.Replace(format, "dddd", date.Weekday().String(), -1)
	}

	// This needs to be last so that month strings don't interfere with the other replace functions
	format = strings.Replace(format, "MMMM", date.Month().String(), -1)
	if len(date.Month().String()) > 2 {
		format = strings.Replace(format, "MMM", date.Month().String()[:3], -1)
	}
	if strings.Contains(format, "MM") {
		format = strings.Replace(format, "MM", replaceMM(date), -1)
	}
	// Replace M - make sure the Ms in March and May don't get replaced. TODO: Regex could be improved, only recognizes 'M's that are not followed by 'a's.
	format = marchMayChecker.ReplaceAllString(format, strconv.Itoa(int(date.Month())))

	return []byte(format)
}

func replaceMM(date *time.Time) string {
	var buffer bytes.Buffer
	month := int(date.Month())
	if month < 10 {
		buffer.WriteString("0")
		buffer.WriteString(strconv.Itoa(month))
	} else {
		buffer.WriteString(strconv.Itoa(month))
	}
	return buffer.String()
}

func replaceDDDD(date *time.Time) string {
	var buffer bytes.Buffer
	startOfYear := time.Date((date.Year() - 1), time.December, 31, 0, 0, 0, 0, time.UTC)
	days := int(date.Sub(startOfYear) / (24 * time.Hour))
	if days < 10 {
		buffer.WriteString("00")
	} else if days < 100 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(days))
	return buffer.String()
}

func replaceDDD(date *time.Time) string {
	startOfYear := time.Date((date.Year() - 1), time.December, 31, 0, 0, 0, 0, time.UTC)
	return strconv.Itoa(int(date.Sub(startOfYear) / (24 * time.Hour)))
}

func replaceDD(date *time.Time) string {
	var buffer bytes.Buffer
	if date.Day() < 10 {
		buffer.WriteString("0")
		buffer.WriteString(strconv.Itoa(date.Day()))
	} else {
		buffer.WriteString(strconv.Itoa(date.Day()))
	}
	return buffer.String()
}

func replaceDo(date *time.Time) string {
	var buffer bytes.Buffer
	buffer.WriteString(strconv.Itoa(date.Day()))
	if date.Day() == 1 || date.Day() == 21 || date.Day() == 31 {
		buffer.WriteString("st")
	} else if date.Day() == 2 || date.Day() == 22 {
		buffer.WriteString("nd")
	} else if date.Day() == 3 || date.Day() == 23 {
		buffer.WriteString("rd")
	} else {
		buffer.WriteString("th")
	}
	return buffer.String()
}

func replaceww(date *time.Time) string {
	var buffer bytes.Buffer
	startOfYear := time.Date((date.Year() - 1), time.December, 25, 0, 0, 0, 0, time.UTC)
	weeks := int(date.Sub(startOfYear) / (24 * time.Hour * 7))
	if weeks < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(weeks))
	return buffer.String()
}

func replacew(date *time.Time) string {
	startOfYear := time.Date((date.Year() - 1), time.December, 25, 0, 0, 0, 0, time.UTC)
	return strconv.Itoa(int(date.Sub(startOfYear) / (24 * time.Hour * 7)))
}

func replaceHH(date *time.Time) string {
	var buffer bytes.Buffer
	hour := date.Hour()
	if hour < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(hour))
	return buffer.String()
}

func replacehh(date *time.Time) string {
	var buffer bytes.Buffer
	hour := date.Hour()
	if hour == 0 {
		hour = 12
	} else if hour > 12 {
		hour = hour - 12
	}
	if hour < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(hour))
	return buffer.String()
}

func replaceh(date *time.Time) string {
	var buffer bytes.Buffer
	hour := date.Hour()
	if hour == 0 {
		hour = 12
	} else if hour > 12 {
		hour = hour - 12
	}
	buffer.WriteString(strconv.Itoa(hour))
	return buffer.String()
}

func replacea(date *time.Time) string {
	if date.Hour() < 12 {
		return "am"
	}
	return "pm"
}

func replaceA(date *time.Time) string {
	if date.Hour() < 12 {
		return "AM"
	}
	return "PM"
}

func replacemm(date *time.Time) string {
	var buffer bytes.Buffer
	minute := date.Minute()
	if minute < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(minute))
	return buffer.String()
}

func replacess(date *time.Time) string {
	var buffer bytes.Buffer
	second := date.Second()
	if second < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(second))
	return buffer.String()
}

func replaceZZ(date *time.Time) string {
	var buffer bytes.Buffer
	_, offset := date.In(time.Local).Zone()
	offset = offset / 3600
	if offset > 0 {
		buffer.WriteString("+")
	} else {
		buffer.WriteString("-")
	}
	if offset < 0 {
		offset = -offset
	}
	if offset < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(offset))
	buffer.WriteString("00")
	return buffer.String()
}

func replaceZ(date *time.Time) string {
	var buffer bytes.Buffer
	_, offset := date.In(time.Local).Zone()
	offset = offset / 3600
	if offset > 0 {
		buffer.WriteString("+")
	} else {
		buffer.WriteString("-")
	}
	if offset < 0 {
		offset = -offset
	}
	if offset < 10 {
		buffer.WriteString("0")
	}
	buffer.WriteString(strconv.Itoa(offset))
	buffer.WriteString(":00")
	return buffer.String()
}
<|endoftext|>


<commit_before>package framework

import (
	"fmt"
	"regexp"
	"sync"
	"time"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/vault/logical"
)

// Backend is an implementation of logical.Backend that allows
// the implementer to code a backend using a much more programmer-friendly
// framework that handles a lot of the routing and validation for you.
//
// This is recommended over implementing logical.Backend directly.
type Backend struct {
	// Paths are the various routes that the backend responds to.
	// This cannot be modified after construction (i.e. dynamically changing
	// paths, including adding or removing, is not allowed once the
	// backend is in use).
	//
	// PathsSpecial is the list of path patterns that denote the
	// paths above that require special privileges. These can't be
	// regular expressions, it is either exact match or prefix match.
	// For prefix match, append '*' as a suffix.
	Paths        []*Path
	PathsSpecial *logical.Paths

	// Secrets is the list of secret types that this backend can
	// return. It is used to automatically generate proper responses,
	// and ease specifying callbacks for revocation, renewal, etc.
	Secrets []*Secret

	// Rollback is called when a WAL entry (see wal.go) has to be rolled
	// back. It is called with the data from the entry.
	//
	// RollbackMinAge is the minimum age of a WAL entry before it is attempted
	// to be rolled back. This should be longer than the maximum time it takes
	// to successfully create a secret.
	Rollback       RollbackFunc
	RollbackMinAge time.Duration

	once    sync.Once
	pathsRe []*regexp.Regexp
}

// OperationFunc is the callback called for an operation on a path.
type OperationFunc func(*logical.Request, *FieldData) (*logical.Response, error)

// RollbackFunc is the callback for rollbacks.
type RollbackFunc func(*logical.Request, string, interface{}) error

// logical.Backend impl.
func (b *Backend) HandleRequest(req *logical.Request) (*logical.Response, error) {
	// Check for special cased global operations. These don't route
	// to a specific Path.
	switch req.Operation {
	case logical.RenewOperation:
		fallthrough
	case logical.RevokeOperation:
		return b.handleRevokeRenew(req)
	case logical.RollbackOperation:
		return b.handleRollback(req)
	}

	// Find the matching route
	path, captures := b.route(req.Path)
	if path == nil {
		return nil, logical.ErrUnsupportedPath
	}

	// Build up the data for the route, with the URL taking priority
	// for the fields over the PUT data.
	raw := make(map[string]interface{}, len(path.Fields))
	for k, v := range req.Data {
		raw[k] = v
	}
	for k, v := range captures {
		raw[k] = v
	}

	// Look up the callback for this operation
	var callback OperationFunc
	var ok bool
	if path.Callbacks != nil {
		callback, ok = path.Callbacks[req.Operation]
	}
	if !ok {
		if req.Operation == logical.HelpOperation && path.HelpSynopsis != "" {
			callback = path.helpCallback
			ok = true
		}
	}
	if !ok {
		return nil, logical.ErrUnsupportedOperation
	}

	// Call the callback with the request and the data
	return callback(req, &FieldData{
		Raw:    raw,
		Schema: path.Fields,
	})
}

// logical.Backend impl.
func (b *Backend) SpecialPaths() *logical.Paths {
	return b.PathsSpecial
}

// Route looks up the path that would be used for a given path string.
func (b *Backend) Route(path string) *Path {
	result, _ := b.route(path)
	return result
}

// Secret is used to look up the secret with the given type.
func (b *Backend) Secret(k string) *Secret {
	for _, s := range b.Secrets {
		if s.Type == k {
			return s
		}
	}

	return nil
}

func (b *Backend) init() {
	b.pathsRe = make([]*regexp.Regexp, len(b.Paths))
	for i, p := range b.Paths {
		b.pathsRe[i] = regexp.MustCompile(p.Pattern)
	}
}

func (b *Backend) route(path string) (*Path, map[string]string) {
	b.once.Do(b.init)

	for i, re := range b.pathsRe {
		matches := re.FindStringSubmatch(path)
		if matches == nil {
			continue
		}

		// We have a match, determine the mapping of the captures and
		// store that for returning.
		var captures map[string]string
		path := b.Paths[i]
		if captureNames := re.SubexpNames(); len(captureNames) > 1 {
			captures = make(map[string]string, len(captureNames))
			for i, name := range captureNames {
				if name != "" {
					captures[name] = matches[i]
				}
			}
		}

		return path, captures
	}

	return nil, nil
}

func (b *Backend) handleRevokeRenew(
	req *logical.Request) (*logical.Response, error) {
	if req.Secret == nil {
		return nil, fmt.Errorf("request has no secret")
	}

	rawSecretType, ok := req.Secret.InternalData["secret_type"]
	if !ok {
		return nil, fmt.Errorf("secret is unsupported by this backend")
	}
	secretType, ok := rawSecretType.(string)
	if !ok {
		return nil, fmt.Errorf("secret is unsupported by this backend")
	}

	secret := b.Secret(secretType)
	if secret == nil {
		return nil, fmt.Errorf("secret is unsupported by this backend")
	}

	switch req.Operation {
	case logical.RenewOperation:
		return secret.HandleRenew(req)
	case logical.RevokeOperation:
		return secret.HandleRevoke(req)
	default:
		return nil, fmt.Errorf(
			"invalid operation for revoke/renew: %s", req.Operation)
	}
}

func (b *Backend) handleRollback(
	req *logical.Request) (*logical.Response, error) {
	if b.Rollback == nil {
		return nil, logical.ErrUnsupportedOperation
	}

	var merr error
	keys, err := ListWAL(req.Storage)
	if err != nil {
		return logical.ErrorResponse(err.Error()), nil
	}
	if len(keys) == 0 {
		return nil, nil
	}

	// Calculate the minimum time that the WAL entries could be
	// created in order to be rolled back.
	age := b.RollbackMinAge
	if age == 0 {
		age = 10 * time.Minute
	}
	minAge := time.Now().UTC().Add(-1 * age)
	if _, ok := req.Data["immediate"]; ok {
		minAge = time.Now().UTC().Add(1000 * time.Hour)
	}

	for _, k := range keys {
		entry, err := GetWAL(req.Storage, k)
		if err != nil {
			merr = multierror.Append(merr, err)
			continue
		}
		if entry == nil {
			continue
		}

		// If the entry isn't old enough, then don't roll it back
		if !time.Unix(entry.CreatedAt, 0).Before(minAge) {
			continue
		}

		// Attempt a rollback
		err = b.Rollback(req, entry.Kind, entry.Data)
		if err != nil {
			err = fmt.Errorf(
				"Error rolling back '%s' entry: %s", entry.Kind, err)
		}
		if err == nil {
			err = DeleteWAL(req.Storage, k)
		}
		if err != nil {
			merr = multierror.Append(merr, err)
		}
	}

	if merr == nil {
		return nil, nil
	}

	return logical.ErrorResponse(merr.Error()), nil
}

// FieldSchema is a basic schema to describe the format of a path field.
type FieldSchema struct {
	Type        FieldType
	Default     interface{}
	Description string
}

// DefaultOrZero returns the default value if it is set, or otherwise
// the zero value of the type.
func (s *FieldSchema) DefaultOrZero() interface{} {
	if s.Default != nil {
		return s.Default
	}

	return s.Type.Zero()
}

func (t FieldType) Zero() interface{} {
	switch t {
	case TypeString:
		return ""
	case TypeInt:
		return 0
	case TypeBool:
		return false
	default:
		panic("unknown type: " + t.String())
	}
}
<commit_msg>logical/framework: Added missing case for TypeMap<commit_after>package framework

import (
	"fmt"
	"regexp"
	"sync"
	"time"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/vault/logical"
)

// Backend is an implementation of logical.Backend that allows
// the implementer to code a backend using a much more programmer-friendly
// framework that handles a lot of the routing and validation for you.
//
// This is recommended over implementing logical.Backend directly.
type Backend struct {
	// Paths are the various routes that the backend responds to.
	// This cannot be modified after construction (i.e. dynamically changing
	// paths, including adding or removing, is not allowed once the
	// backend is in use).
	//
	// PathsSpecial is the list of path patterns that denote the
	// paths above that require special privileges. These can't be
	// regular expressions, it is either exact match or prefix match.
	// For prefix match, append '*' as a suffix.
	Paths        []*Path
	PathsSpecial *logical.Paths

	// Secrets is the list of secret types that this backend can
	// return. It is used to automatically generate proper responses,
	// and ease specifying callbacks for revocation, renewal, etc.
	Secrets []*Secret

	// Rollback is called when a WAL entry (see wal.go) has to be rolled
	// back. It is called with the data from the entry.
	//
	// RollbackMinAge is the minimum age of a WAL entry before it is attempted
	// to be rolled back. This should be longer than the maximum time it takes
	// to successfully create a secret.
	Rollback       RollbackFunc
	RollbackMinAge time.Duration

	once    sync.Once
	pathsRe []*regexp.Regexp
}

// OperationFunc is the callback called for an operation on a path.
type OperationFunc func(*logical.Request, *FieldData) (*logical.Response, error)

// RollbackFunc is the callback for rollbacks.
type RollbackFunc func(*logical.Request, string, interface{}) error

// logical.Backend impl.
func (b *Backend) HandleRequest(req *logical.Request) (*logical.Response, error) {
	// Check for special cased global operations. These don't route
	// to a specific Path.
	switch req.Operation {
	case logical.RenewOperation:
		fallthrough
	case logical.RevokeOperation:
		return b.handleRevokeRenew(req)
	case logical.RollbackOperation:
		return b.handleRollback(req)
	}

	// Find the matching route
	path, captures := b.route(req.Path)
	if path == nil {
		return nil, logical.ErrUnsupportedPath
	}

	// Build up the data for the route, with the URL taking priority
	// for the fields over the PUT data.
	raw := make(map[string]interface{}, len(path.Fields))
	for k, v := range req.Data {
		raw[k] = v
	}
	for k, v := range captures {
		raw[k] = v
	}

	// Look up the callback for this operation
	var callback OperationFunc
	var ok bool
	if path.Callbacks != nil {
		callback, ok = path.Callbacks[req.Operation]
	}
	if !ok {
		if req.Operation == logical.HelpOperation && path.HelpSynopsis != "" {
			callback = path.helpCallback
			ok = true
		}
	}
	if !ok {
		return nil, logical.ErrUnsupportedOperation
	}

	// Call the callback with the request and the data
	return callback(req, &FieldData{
		Raw:    raw,
		Schema: path.Fields,
	})
}

// logical.Backend impl.
func (b *Backend) SpecialPaths() *logical.Paths {
	return b.PathsSpecial
}

// Route looks up the path that would be used for a given path string.
func (b *Backend) Route(path string) *Path {
	result, _ := b.route(path)
	return result
}

// Secret is used to look up the secret with the given type.
func (b *Backend) Secret(k string) *Secret {
	for _, s := range b.Secrets {
		if s.Type == k {
			return s
		}
	}

	return nil
}

func (b *Backend) init() {
	b.pathsRe = make([]*regexp.Regexp, len(b.Paths))
	for i, p := range b.Paths {
		b.pathsRe[i] = regexp.MustCompile(p.Pattern)
	}
}

func (b *Backend) route(path string) (*Path, map[string]string) {
	b.once.Do(b.init)

	for i, re := range b.pathsRe {
		matches := re.FindStringSubmatch(path)
		if matches == nil {
			continue
		}

		// We have a match, determine the mapping of the captures and
		// store that for returning.
		var captures map[string]string
		path := b.Paths[i]
		if captureNames := re.SubexpNames(); len(captureNames) > 1 {
			captures = make(map[string]string, len(captureNames))
			for i, name := range captureNames {
				if name != "" {
					captures[name] = matches[i]
				}
			}
		}

		return path, captures
	}

	return nil, nil
}

func (b *Backend) handleRevokeRenew(
	req *logical.Request) (*logical.Response, error) {
	if req.Secret == nil {
		return nil, fmt.Errorf("request has no secret")
	}

	rawSecretType, ok := req.Secret.InternalData["secret_type"]
	if !ok {
		return nil, fmt.Errorf("secret is unsupported by this backend")
	}
	secretType, ok := rawSecretType.(string)
	if !ok {
		return nil, fmt.Errorf("secret is unsupported by this backend")
	}

	secret := b.Secret(secretType)
	if secret == nil {
		return nil, fmt.Errorf("secret is unsupported by this backend")
	}

	switch req.Operation {
	case logical.RenewOperation:
		return secret.HandleRenew(req)
	case logical.RevokeOperation:
		return secret.HandleRevoke(req)
	default:
		return nil, fmt.Errorf(
			"invalid operation for revoke/renew: %s", req.Operation)
	}
}

func (b *Backend) handleRollback(
	req *logical.Request) (*logical.Response, error) {
	if b.Rollback == nil {
		return nil, logical.ErrUnsupportedOperation
	}

	var merr error
	keys, err := ListWAL(req.Storage)
	if err != nil {
		return logical.ErrorResponse(err.Error()), nil
	}
	if len(keys) == 0 {
		return nil, nil
	}

	// Calculate the minimum time that the WAL entries could be
	// created in order to be rolled back.
	age := b.RollbackMinAge
	if age == 0 {
		age = 10 * time.Minute
	}
	minAge := time.Now().UTC().Add(-1 * age)
	if _, ok := req.Data["immediate"]; ok {
		minAge = time.Now().UTC().Add(1000 * time.Hour)
	}

	for _, k := range keys {
		entry, err := GetWAL(req.Storage, k)
		if err != nil {
			merr = multierror.Append(merr, err)
			continue
		}
		if entry == nil {
			continue
		}

		// If the entry isn't old enough, then don't roll it back
		if !time.Unix(entry.CreatedAt, 0).Before(minAge) {
			continue
		}

		// Attempt a rollback
		err = b.Rollback(req, entry.Kind, entry.Data)
		if err != nil {
			err = fmt.Errorf(
				"Error rolling back '%s' entry: %s", entry.Kind, err)
		}
		if err == nil {
			err = DeleteWAL(req.Storage, k)
		}
		if err != nil {
			merr = multierror.Append(merr, err)
		}
	}

	if merr == nil {
		return nil, nil
	}

	return logical.ErrorResponse(merr.Error()), nil
}

// FieldSchema is a basic schema to describe the format of a path field.
type FieldSchema struct {
	Type        FieldType
	Default     interface{}
	Description string
}

// DefaultOrZero returns the default value if it is set, or otherwise
// the zero value of the type.
func (s *FieldSchema) DefaultOrZero() interface{} {
	if s.Default != nil {
		return s.Default
	}

	return s.Type.Zero()
}

func (t FieldType) Zero() interface{} {
	switch t {
	case TypeString:
		return ""
	case TypeInt:
		return 0
	case TypeBool:
		return false
	case TypeMap:
		return map[string]interface{}{}
	default:
		panic("unknown type: " + t.String())
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Chris McGee <sirnewton_01@yahoo.ca>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package gdblib

import (
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	sendSignalPath string
)

func init() {
	gopath := build.Default.GOPATH
	gopaths := strings.Split(gopath, filepath.ListSeparator)
	for _,path := range(gopaths) {
		p := path + "\\src\\github.com\\sirnewton01\\gdblib\\SendSignal.exe"
		_,err := os.Stat(p)
		if err == nil {
			sendSignalPath = p
			break
		}
	}
}

func fixCmd(cmd *exec.Cmd) {
	// No process group separation is required on Windows.
	// Processes do not share signals like they can on Unix.
}

func interruptInferior(process *os.Process, pid string) {
	// Invoke the included "sendsignal" program to send the
	// Ctrl-break to the inferior process to interrupt it

	initCommand := exec.Command("cmd", "/c", "start", sendSignalPath, pid)
	initCommand.Run()
}
<commit_msg>Convert ListSeparator into a string to use in Split.<commit_after>// Copyright 2013 Chris McGee <sirnewton_01@yahoo.ca>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package gdblib

import (
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	sendSignalPath string
)

func init() {
	gopath := build.Default.GOPATH
	gopaths := strings.Split(gopath, string(filepath.ListSeparator))
	for _,path := range(gopaths) {
		p := path + "\\src\\github.com\\sirnewton01\\gdblib\\SendSignal.exe"
		_,err := os.Stat(p)
		if err == nil {
			sendSignalPath = p
			break
		}
	}
}

func fixCmd(cmd *exec.Cmd) {
	// No process group separation is required on Windows.
	// Processes do not share signals like they can on Unix.
}

func interruptInferior(process *os.Process, pid string) {
	// Invoke the included "sendsignal" program to send the
	// Ctrl-break to the inferior process to interrupt it

	initCommand := exec.Command("cmd", "/c", "start", sendSignalPath, pid)
	initCommand.Run()
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2017 Cavium
//
// SPDX-License-Identifier: Apache-2.0
//

// +build zeromq

package distro

import (
	"encoding/json"

	"github.com/drasko/edgex-export"
	zmq "github.com/pebbe/zmq4"
	"go.uber.org/zap"
)

func ZeroMQReceiver(eventCh chan *export.Event) {
	go initZmq(eventCh)
}

func initZmq(eventCh chan *export.Event) {
	q, _ := zmq.NewSocket(zmq.SUB)
	defer q.Close()

	logger.Info("Connecting to zmq...")
	q.Connect("tcp://localhost:5563")
	logger.Info("Connected to zmq")
	q.SetSubscribe("")

	for {
		msg, err := q.RecvMessage(0)
		if err != nil {
			id, _ := q.GetIdentity()
			logger.Error("Error getting mesage", zap.String("id", id))
		} else {
			for _, str := range msg {
				event := parseEvent(str)
				logger.Info("Event received", zap.Any("event", event))
				eventCh <- event
			}
		}
	}
}

func parseEvent(str string) *export.Event {
	event := export.Event{}

	if err := json.Unmarshal([]byte(str), &event); err == nil {
		return &event
	}

	// Why the offset of 7?? zmq v3 vs v4 ?
	if err := json.Unmarshal([]byte(str[7:]), &event); err != nil {
		logger.Error("Failed to parse event", zap.Error(err))
		return nil
	}

	return &event
}
<commit_msg>Remove hack to parse the zeromq message. See https://github.com/edgexfoundry/core-data/pull/27 for the bug in core-data<commit_after>//
// Copyright (c) 2017 Cavium
//
// SPDX-License-Identifier: Apache-2.0
//

// +build zeromq

package distro

import (
	"encoding/json"

	"github.com/drasko/edgex-export"
	zmq "github.com/pebbe/zmq4"
	"go.uber.org/zap"
)

func ZeroMQReceiver(eventCh chan *export.Event) {
	go initZmq(eventCh)
}

func initZmq(eventCh chan *export.Event) {
	q, _ := zmq.NewSocket(zmq.SUB)
	defer q.Close()

	logger.Info("Connecting to zmq...")
	q.Connect("tcp://127.0.0.1:5563")
	logger.Info("Connected to zmq")
	q.SetSubscribe("")

	for {
		msg, err := q.RecvMessage(0)
		if err != nil {
			id, _ := q.GetIdentity()
			logger.Error("Error getting mesage", zap.String("id", id))
		} else {
			for _, str := range msg {
				event := parseEvent(str)
				logger.Info("Event received", zap.Any("event", event))
				eventCh <- event
			}
		}
	}
}

func parseEvent(str string) *export.Event {
	event := export.Event{}

	if err := json.Unmarshal([]byte(str), &event); err != nil {
		logger.Error("Failed to parse event", zap.Error(err))
		return nil
	}
	return &event
}
<|endoftext|>


<commit_before>package rados_test

import "testing"
//import "bytes"
import "github.com/noahdesu/rados"
import "github.com/stretchr/testify/assert"
import "os"
import "os/exec"
import "io"
import "io/ioutil"
import "time"
import "net"
import "fmt"

func GetUUID() string {
    out, _ := exec.Command("uuidgen").Output()
    return string(out[:36])
}

func TestVersion(t *testing.T) {
    var major, minor, patch = rados.Version()
    assert.False(t, major < 0 || major > 1000, "invalid major")
    assert.False(t, minor < 0 || minor > 1000, "invalid minor")
    assert.False(t, patch < 0 || patch > 1000, "invalid patch")
}

func TestGetSetConfigOption(t *testing.T) {
    conn, _ := rados.NewConn()

    // rejects invalid options
    err := conn.SetConfigOption("wefoijweojfiw", "welfkwjelkfj")
    assert.Error(t, err, "Invalid option")

    // verify SetConfigOption changes a values
    log_file_val, err := conn.GetConfigOption("log_file")
    assert.NotEqual(t, log_file_val, "/dev/null")

    err = conn.SetConfigOption("log_file", "/dev/null")
    assert.NoError(t, err, "Invalid option")

    log_file_val, err = conn.GetConfigOption("log_file")
    assert.Equal(t, log_file_val, "/dev/null")
}

func TestParseDefaultConfigEnv(t *testing.T) {
    conn, _ := rados.NewConn()

    log_file_val, _ := conn.GetConfigOption("log_file")
    assert.NotEqual(t, log_file_val, "/dev/null")

    err := os.Setenv("CEPH_ARGS", "--log-file /dev/null")
    assert.NoError(t, err)

    err = conn.ParseDefaultConfigEnv()
    assert.NoError(t, err)

    log_file_val, _ = conn.GetConfigOption("log_file")
    assert.Equal(t, log_file_val, "/dev/null")
}

func TestParseCmdLineArgs(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()

    mon_host_val, _ := conn.GetConfigOption("mon_host")
    assert.NotEqual(t, mon_host_val, "1.1.1.1")

    args := []string{ "--mon-host", "1.1.1.1" }
    err := conn.ParseCmdLineArgs(args)
    assert.NoError(t, err)

    mon_host_val, _ = conn.GetConfigOption("mon_host")
    assert.Equal(t, mon_host_val, "1.1.1.1")
}

func TestGetClusterStats(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    poolname := GetUUID()
    err := conn.MakePool(poolname)
    assert.NoError(t, err)

    pool, err := conn.OpenPool(poolname)
    assert.NoError(t, err)

    buf := make([]byte, 1<<22)
    pool.Write("obj", buf, 0)

    for i := 0; i < 30; i++ {
        stat, err := conn.GetClusterStats()
        assert.NoError(t, err)

        // wait a second if stats are zero
        if stat.Kb == 0 || stat.Kb_used == 0 ||
            stat.Kb_avail == 0 || stat.Num_objects == 0 {
            fmt.Println("waiting for cluster stats to refresh")
            time.Sleep(time.Second)
        } else {
            // success
            conn.Shutdown()
            return
        }
    }

    t.Error("Cluster stats are zero")

    conn.Shutdown()
}

func TestGetFSID(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    fsid, err := conn.GetFSID()
    assert.NoError(t, err)
    assert.NotEqual(t, fsid, "")

    conn.Shutdown()
}

func TestGetInstanceID(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    id := conn.GetInstanceID()
    assert.NotEqual(t, id, 0)

    conn.Shutdown()
}

func TestMakeDeletePool(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    // get current list of pool
    pools, err := conn.ListPools()
    assert.NoError(t, err)

    // check that new pool name is unique
    new_name := GetUUID()
    for _, poolname := range pools {
        if new_name == poolname {
            t.Error("Random pool name exists!")
            return
        }
    }

    // create pool
    err = conn.MakePool(new_name)
    assert.NoError(t, err)

    // get updated list of pools
    pools, err = conn.ListPools()
    assert.NoError(t, err)

    // verify that the new pool name exists
    found := false
    for _, poolname := range pools {
        if new_name == poolname {
            found = true
        }
    }

    if !found {
        t.Error("Cannot find newly created pool")
    }

    // delete the pool
    err = conn.DeletePool(new_name)
    assert.NoError(t, err)

    // verify that it is gone

    // get updated list of pools
    pools, err = conn.ListPools()
    assert.NoError(t, err)

    // verify that the new pool name exists
    found = false
    for _, poolname := range pools {
        if new_name == poolname {
            found = true
        }
    }

    if found {
        t.Error("Deleted pool still exists")
    }

    conn.Shutdown()
}

func TestPingMonitor(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    // mon id that should work with vstart.sh
    reply, err := conn.PingMonitor("a")
    if err == nil {
        assert.NotEqual(t, reply, "")
        return
    }

    // try to use a hostname as the monitor id
    mon_addr, _ := conn.GetConfigOption("mon_host")
    hosts, _ := net.LookupAddr(mon_addr)
    for _, host := range hosts {
        reply, err := conn.PingMonitor(host)
        if err == nil {
            assert.NotEqual(t, reply, "")
            return
        }
    }

    t.Error("Could not find a valid monitor id")

    conn.Shutdown()
}

func TestReadConfigFile(t *testing.T) {
    conn, _ := rados.NewConn()

    // check current log_file value
    log_file_val, err := conn.GetConfigOption("log_file")
    assert.NoError(t, err)
    assert.NotEqual(t, log_file_val, "/dev/null")

    // create a temporary ceph.conf file that changes the log_file conf
    // option.
    file, err := ioutil.TempFile("/tmp", "go-rados")
    assert.NoError(t, err)

    _, err = io.WriteString(file, "[global]\nlog_file = /dev/null\n")
    assert.NoError(t, err)

    // parse the config file
    err = conn.ReadConfigFile(file.Name())
    assert.NoError(t, err)

    // check current log_file value
    log_file_val, err = conn.GetConfigOption("log_file")
    assert.NoError(t, err)
    assert.Equal(t, log_file_val, "/dev/null")

    // cleanup
    file.Close()
    os.Remove(file.Name())
}

func TestWaitForLatestOSDMap(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    err := conn.WaitForLatestOSDMap()
    assert.NoError(t, err)

    conn.Shutdown()
}
<commit_msg>test: fix import path<commit_after>package rados_test

import "testing"
//import "bytes"
import "github.com/noahdesu/go-rados"
import "github.com/stretchr/testify/assert"
import "os"
import "os/exec"
import "io"
import "io/ioutil"
import "time"
import "net"
import "fmt"

func GetUUID() string {
    out, _ := exec.Command("uuidgen").Output()
    return string(out[:36])
}

func TestVersion(t *testing.T) {
    var major, minor, patch = rados.Version()
    assert.False(t, major < 0 || major > 1000, "invalid major")
    assert.False(t, minor < 0 || minor > 1000, "invalid minor")
    assert.False(t, patch < 0 || patch > 1000, "invalid patch")
}

func TestGetSetConfigOption(t *testing.T) {
    conn, _ := rados.NewConn()

    // rejects invalid options
    err := conn.SetConfigOption("wefoijweojfiw", "welfkwjelkfj")
    assert.Error(t, err, "Invalid option")

    // verify SetConfigOption changes a values
    log_file_val, err := conn.GetConfigOption("log_file")
    assert.NotEqual(t, log_file_val, "/dev/null")

    err = conn.SetConfigOption("log_file", "/dev/null")
    assert.NoError(t, err, "Invalid option")

    log_file_val, err = conn.GetConfigOption("log_file")
    assert.Equal(t, log_file_val, "/dev/null")
}

func TestParseDefaultConfigEnv(t *testing.T) {
    conn, _ := rados.NewConn()

    log_file_val, _ := conn.GetConfigOption("log_file")
    assert.NotEqual(t, log_file_val, "/dev/null")

    err := os.Setenv("CEPH_ARGS", "--log-file /dev/null")
    assert.NoError(t, err)

    err = conn.ParseDefaultConfigEnv()
    assert.NoError(t, err)

    log_file_val, _ = conn.GetConfigOption("log_file")
    assert.Equal(t, log_file_val, "/dev/null")
}

func TestParseCmdLineArgs(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()

    mon_host_val, _ := conn.GetConfigOption("mon_host")
    assert.NotEqual(t, mon_host_val, "1.1.1.1")

    args := []string{ "--mon-host", "1.1.1.1" }
    err := conn.ParseCmdLineArgs(args)
    assert.NoError(t, err)

    mon_host_val, _ = conn.GetConfigOption("mon_host")
    assert.Equal(t, mon_host_val, "1.1.1.1")
}

func TestGetClusterStats(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    poolname := GetUUID()
    err := conn.MakePool(poolname)
    assert.NoError(t, err)

    pool, err := conn.OpenPool(poolname)
    assert.NoError(t, err)

    buf := make([]byte, 1<<22)
    pool.Write("obj", buf, 0)

    for i := 0; i < 30; i++ {
        stat, err := conn.GetClusterStats()
        assert.NoError(t, err)

        // wait a second if stats are zero
        if stat.Kb == 0 || stat.Kb_used == 0 ||
            stat.Kb_avail == 0 || stat.Num_objects == 0 {
            fmt.Println("waiting for cluster stats to refresh")
            time.Sleep(time.Second)
        } else {
            // success
            conn.Shutdown()
            return
        }
    }

    t.Error("Cluster stats are zero")

    conn.Shutdown()
}

func TestGetFSID(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    fsid, err := conn.GetFSID()
    assert.NoError(t, err)
    assert.NotEqual(t, fsid, "")

    conn.Shutdown()
}

func TestGetInstanceID(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    id := conn.GetInstanceID()
    assert.NotEqual(t, id, 0)

    conn.Shutdown()
}

func TestMakeDeletePool(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    // get current list of pool
    pools, err := conn.ListPools()
    assert.NoError(t, err)

    // check that new pool name is unique
    new_name := GetUUID()
    for _, poolname := range pools {
        if new_name == poolname {
            t.Error("Random pool name exists!")
            return
        }
    }

    // create pool
    err = conn.MakePool(new_name)
    assert.NoError(t, err)

    // get updated list of pools
    pools, err = conn.ListPools()
    assert.NoError(t, err)

    // verify that the new pool name exists
    found := false
    for _, poolname := range pools {
        if new_name == poolname {
            found = true
        }
    }

    if !found {
        t.Error("Cannot find newly created pool")
    }

    // delete the pool
    err = conn.DeletePool(new_name)
    assert.NoError(t, err)

    // verify that it is gone

    // get updated list of pools
    pools, err = conn.ListPools()
    assert.NoError(t, err)

    // verify that the new pool name exists
    found = false
    for _, poolname := range pools {
        if new_name == poolname {
            found = true
        }
    }

    if found {
        t.Error("Deleted pool still exists")
    }

    conn.Shutdown()
}

func TestPingMonitor(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    // mon id that should work with vstart.sh
    reply, err := conn.PingMonitor("a")
    if err == nil {
        assert.NotEqual(t, reply, "")
        return
    }

    // try to use a hostname as the monitor id
    mon_addr, _ := conn.GetConfigOption("mon_host")
    hosts, _ := net.LookupAddr(mon_addr)
    for _, host := range hosts {
        reply, err := conn.PingMonitor(host)
        if err == nil {
            assert.NotEqual(t, reply, "")
            return
        }
    }

    t.Error("Could not find a valid monitor id")

    conn.Shutdown()
}

func TestReadConfigFile(t *testing.T) {
    conn, _ := rados.NewConn()

    // check current log_file value
    log_file_val, err := conn.GetConfigOption("log_file")
    assert.NoError(t, err)
    assert.NotEqual(t, log_file_val, "/dev/null")

    // create a temporary ceph.conf file that changes the log_file conf
    // option.
    file, err := ioutil.TempFile("/tmp", "go-rados")
    assert.NoError(t, err)

    _, err = io.WriteString(file, "[global]\nlog_file = /dev/null\n")
    assert.NoError(t, err)

    // parse the config file
    err = conn.ReadConfigFile(file.Name())
    assert.NoError(t, err)

    // check current log_file value
    log_file_val, err = conn.GetConfigOption("log_file")
    assert.NoError(t, err)
    assert.Equal(t, log_file_val, "/dev/null")

    // cleanup
    file.Close()
    os.Remove(file.Name())
}

func TestWaitForLatestOSDMap(t *testing.T) {
    conn, _ := rados.NewConn()
    conn.ReadDefaultConfigFile()
    conn.Connect()

    err := conn.WaitForLatestOSDMap()
    assert.NoError(t, err)

    conn.Shutdown()
}
<|endoftext|>


<commit_before>// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package p2p

import (
	"fmt"
)

const (
	errInvalidMsgCode = iota
	errInvalidMsg
)

var errorToString = map[int]string{
	errInvalidMsgCode: "invalid message code",
	errInvalidMsg:     "invalid message",
}

type peerError struct {
	code    int
	message string
}

func newPeerError(code int, format string, v ...interface{}) *peerError {
	desc, ok := errorToString[code]
	if !ok {
		panic("invalid error code")
	}
	err := &peerError{code, desc}
	if format != "" {
		err.message += ": " + fmt.Sprintf(format, v...)
	}
	return err
}

func (self *peerError) Error() string {
	return self.message
}

type DiscReason uint

const (
	DiscRequested DiscReason = iota
	DiscNetworkError
	DiscProtocolError
	DiscUselessPeer
	DiscTooManyPeers
	DiscAlreadyConnected
	DiscIncompatibleVersion
	DiscInvalidIdentity
	DiscQuitting
	DiscUnexpectedIdentity
	DiscSelf
	DiscReadTimeout
	DiscSubprotocolError
)

var discReasonToString = [...]string{
	DiscRequested:           "Disconnect requested",
	DiscNetworkError:        "Network error",
	DiscProtocolError:       "Breach of protocol",
	DiscUselessPeer:         "Useless peer",
	DiscTooManyPeers:        "Too many peers",
	DiscAlreadyConnected:    "Already connected",
	DiscIncompatibleVersion: "Incompatible P2P protocol version",
	DiscInvalidIdentity:     "Invalid node identity",
	DiscQuitting:            "Client quitting",
	DiscUnexpectedIdentity:  "Unexpected identity",
	DiscSelf:                "Connected to self",
	DiscReadTimeout:         "Read timeout",
	DiscSubprotocolError:    "Subprotocol error",
}

func (d DiscReason) String() string {
	if len(discReasonToString) < int(d) {
		return fmt.Sprintf("Unknown Reason(%d)", d)
	}
	return discReasonToString[d]
}

func (d DiscReason) Error() string {
	return d.String()
}

func discReasonForError(err error) DiscReason {
	if reason, ok := err.(DiscReason); ok {
		return reason
	}
	peerError, ok := err.(*peerError)
	if ok {
		switch peerError.code {
		case errInvalidMsgCode, errInvalidMsg:
			return DiscProtocolError
		default:
			return DiscSubprotocolError
		}
	}
	return DiscSubprotocolError
}
<commit_msg>p2p: fix value of DiscSubprotocolError<commit_after>// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package p2p

import (
	"fmt"
)

const (
	errInvalidMsgCode = iota
	errInvalidMsg
)

var errorToString = map[int]string{
	errInvalidMsgCode: "invalid message code",
	errInvalidMsg:     "invalid message",
}

type peerError struct {
	code    int
	message string
}

func newPeerError(code int, format string, v ...interface{}) *peerError {
	desc, ok := errorToString[code]
	if !ok {
		panic("invalid error code")
	}
	err := &peerError{code, desc}
	if format != "" {
		err.message += ": " + fmt.Sprintf(format, v...)
	}
	return err
}

func (self *peerError) Error() string {
	return self.message
}

type DiscReason uint

const (
	DiscRequested DiscReason = iota
	DiscNetworkError
	DiscProtocolError
	DiscUselessPeer
	DiscTooManyPeers
	DiscAlreadyConnected
	DiscIncompatibleVersion
	DiscInvalidIdentity
	DiscQuitting
	DiscUnexpectedIdentity
	DiscSelf
	DiscReadTimeout
	DiscSubprotocolError = 0x10
)

var discReasonToString = [...]string{
	DiscRequested:           "Disconnect requested",
	DiscNetworkError:        "Network error",
	DiscProtocolError:       "Breach of protocol",
	DiscUselessPeer:         "Useless peer",
	DiscTooManyPeers:        "Too many peers",
	DiscAlreadyConnected:    "Already connected",
	DiscIncompatibleVersion: "Incompatible P2P protocol version",
	DiscInvalidIdentity:     "Invalid node identity",
	DiscQuitting:            "Client quitting",
	DiscUnexpectedIdentity:  "Unexpected identity",
	DiscSelf:                "Connected to self",
	DiscReadTimeout:         "Read timeout",
	DiscSubprotocolError:    "Subprotocol error",
}

func (d DiscReason) String() string {
	if len(discReasonToString) < int(d) {
		return fmt.Sprintf("Unknown Reason(%d)", d)
	}
	return discReasonToString[d]
}

func (d DiscReason) Error() string {
	return d.String()
}

func discReasonForError(err error) DiscReason {
	if reason, ok := err.(DiscReason); ok {
		return reason
	}
	peerError, ok := err.(*peerError)
	if ok {
		switch peerError.code {
		case errInvalidMsgCode, errInvalidMsg:
			return DiscProtocolError
		default:
			return DiscSubprotocolError
		}
	}
	return DiscSubprotocolError
}
<|endoftext|>