<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"text/template"

	"launchpad.net/gnuflag"
	"launchpad.net/loggo"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/bootstrap"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/configstore"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/juju"
	_ "launchpad.net/juju-core/provider/all"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/utils"
)

func main() {
	Main(os.Args)
}

func Main(args []string) {
	if err := juju.InitJujuHome(); err != nil {
		fmt.Fprintf(os.Stderr, "error: %s\n", err)
		os.Exit(2)
	}
	os.Exit(cmd.Main(&restoreCommand{}, cmd.DefaultContext(), args[1:]))
}

var logger = loggo.GetLogger("juju.plugins.restore")

const restoreDoc = `
Restore restores a backup created with juju backup
by creating a new juju bootstrap instance and arranging
it so that the existing instances in the environment
talk to it.

It verifies that the existing bootstrap instance is
not running. The given constraints will be used
to choose the new instance.
`

type restoreCommand struct {
	cmd.EnvCommandBase
	Log         cmd.Log
	Constraints constraints.Value
	backupFile  string
}

func (c *restoreCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "juju-restore",
		Purpose: "Restore a backup made with juju backup",
		Args:    "<backupfile.tar.gz>",
		Doc:     restoreDoc,
	}
}

func (c *restoreCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "set environment constraints")
	c.Log.AddFlags(f)
}

func (c *restoreCommand) Init(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("no backup file specified")
	}
	c.backupFile = args[0]
	return nil
}

var updateBootstrapMachineTemplate = mustParseTemplate(`
	set -e -x
	tar xzf juju-backup.tgz
	test -d juju-backup

	initctl stop jujud-machine-0

	initctl stop juju-db
	rm -r /var/lib/juju /var/log/juju
	tar -C juju-backup/root -c -f - . | tar -C / -xp -f -
	ls -lR ~
	mongorestore --drop --dbpath /var/lib/juju/db juju-backup/dump
	initctl start juju-db

	mongoEval() {
		mongo --ssl -u admin -p {{.AdminSecret | shquote}} localhost:37017/admin --eval "$1"
	}
	# wait for mongo to come up after starting the juju-db upstart service.
	for i in 0 1 2 3 5 6 7
	do
		mongoEval ' ' && break
		sleep 1
	done
	mongoEval '
		db.machines.update({_id: 0}, {$set: {instanceid: {{.NewInstanceId}} } })
		db.instanceData.update({_id: 0}, {$set: {instanceid: {{.NewInstanceId}} } })
	'
	initctl start jujud-machine-0
`)

func updateBootstrapMachineScript(instanceId instance.Id, adminSecret string) string {
	return execTemplate(updateBootstrapMachineTemplate, struct {
		NewInstanceId instance.Id
		AdminSecret string
	}{instanceId, adminSecret})
}

func (c *restoreCommand) Run(ctx *cmd.Context) error {
	if err := c.Log.Start(ctx); err != nil {
		return err
	}
	store, err := configstore.Default()
	if err != nil {
		return err
	}
	cfg, _, err := environs.ConfigForName(c.EnvName, store)
	if err != nil {
		return err
	}
	env, err := rebootstrap(cfg, c.Constraints)
	if err != nil {
		return fmt.Errorf("cannot re-bootstrap environment: %v", err)
	}
	logger.Infof("connecting to newly bootstrapped instance")
	conn, err := juju.NewAPIConn(env, api.DefaultDialOpts())
	if err != nil {
		return fmt.Errorf("cannot connect to bootstrap instance: %v", err)
	}
	newInstId, machine0Addr, err := restoreBootstrapMachine(conn, c.backupFile)
	if err != nil {
		return fmt.Errorf("cannot restore bootstrap machine: %v", err)
	}
	// Update the environ state to point to the new instance.
	if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{
		StateInstances: []instance.Id{newInstId},
	}); err != nil {
		return fmt.Errorf("cannot update environ bootstrap state storage: %v", err)
	}

	// Construct our own state info rather than using juju.NewConn so
	// that we can avoid storage eventual consistency issues
	// (and it's faster too).
	caCert, ok := cfg.CACert()
	if !ok {
		return fmt.Errorf("configuration has no CA certificate")
	}
	st, err := state.Open(&state.Info{
		Addrs:  []string{fmt.Sprintf("%s:%d", machine0Addr, cfg.StatePort())},
		CACert: caCert,
	}, state.DefaultDialOpts())
	if err != nil {
		return fmt.Errorf("cannot open state: %v", err)
	}
	if err := updateAllMachines(st, machine0Addr); err != nil {
		return fmt.Errorf("cannot update machines: %v", err)
	}
	return nil
}

func rebootstrap(cfg *config.Config, cons constraints.Value) (environs.Environ, error) {
	// Turn on safe mode so that the newly bootstrapped instance
	// will not destroy all the instances it does not know about.
	cfg, err := cfg.Apply(map[string]interface{}{
		"provisioner-safe-mode": true,
	})
	if err != nil {
		return nil, fmt.Errorf("cannot enable provisioner-safe-mode: %v", err)
	}
	env, err := environs.New(cfg)
	if err != nil {
		return nil, err
	}
	state, err := bootstrap.LoadState(env.Storage())
	if err != nil {
		return nil, fmt.Errorf("cannot retrieve environment storage; perhaps the environment was not bootstrapped: %v", err)
	}
	if len(state.StateInstances) == 0 {
		return nil, fmt.Errorf("no instances found on bootstrap state; perhaps the environment was not bootstrapped", err)
	}
	if len(state.StateInstances) > 1 {
		return nil, fmt.Errorf("restore does not support HA juju configurations yet")
	}
	inst, err := env.Instances(state.StateInstances)
	if err == nil {
		return nil, fmt.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst)
	}
	if err != environs.ErrNoInstances {
		return nil, fmt.Errorf("cannot detect whether old instance is still running: %v", err)
	}
	// Remove the storage so that we can bootstrap without the provider complaining.
	if err := env.Storage().Remove(bootstrap.StateFile); err != nil {
		return nil, fmt.Errorf("cannot remove %q from storage: %v", bootstrap.StateFile, err)
	}

	// TODO If we fail beyond here, then we won't have a state file and
	// we won't be able to re-run this script because it fails without it.
	// We could either try to recreate the file if we fail (which is itself
	// error-prone) or we could provide a --no-check flag to make
	// it go ahead anyway without the check.

	if err := bootstrap.Bootstrap(env, cons); err != nil {
		return nil, fmt.Errorf("cannot bootstrap new instance: %v", err)
	}
	return env, nil
}

func restoreBootstrapMachine(conn *juju.APIConn, backupFile string) (newInstId instance.Id, addr string, err error) {
	addr, err = conn.State.Client().PublicAddress("0")
	if err != nil {
		return "", "", fmt.Errorf("cannot get public address of bootstrap machine: %v", err)
	}
	status, err := conn.State.Client().Status()
	if err != nil {
		return "", "", fmt.Errorf("cannot get environment status: %v", err)
	}
	info, ok := status.Machines["0"]
	if !ok {
		return "", "", fmt.Errorf("cannot find bootstrap machine in status")
	}
	newInstId = instance.Id(info.InstanceId)

	if err := scp(backupFile, addr, "~/juju-backup.tgz"); err != nil {
		return "", "", fmt.Errorf("cannot copy backup file to bootstrap instance: %v", err)
	}

	adminSecret := conn.Environ.Config().AdminSecret()
	if err := ssh(addr, updateBootstrapMachineScript(newInstId, adminSecret)); err != nil {
		return "", "", fmt.Errorf("update script failed: %v", err)
	}
	return newInstId, addr, nil
}

var agentAddressTemplate = mustParseTemplate(`
set -exu
cd /var/lib/juju/agents
for agent in *
do
	initctl stop jujud-$agent
	sed -i.old -r "/^(stateaddresses|apiaddresses):/{
		n
		s/- .*(:[0-9]+)/- {{.Address}}\1/
	}" $agent/agent.conf
	if [[ $agent = unit-* ]]
	then
 		sed -i -r 's/change-version: [0-9]+$/change-version: 0/' $agent/state/relations/*/* || true
	fi
	initctl start jujud-$agent
done
sed -i -r 's/^(:syslogtag, startswith, "juju-" @)(.*)(:[0-9]+.*)$/\1{{.Address}}\3/' /etc/rsyslog.d/*-juju*.conf
`)

// setAgentAddressScript generates an ssh script argument to update state addresses
func setAgentAddressScript(stateAddr string) string {
	return execTemplate(agentAddressTemplate, struct {
		Address string
	}{stateAddr})
}

// updateAllMachines finds all machines and resets the stored state address
// in each of them. The address does not include the port.
func updateAllMachines(st *state.State, stateAddr string) error {
	machines, err := st.AllMachines()
	if err != nil {
		return err
	}
	pendingMachineCount := 0
	done := make(chan error)
	for _, machine := range machines {
		// A newly resumed state server requires no updating, and more
		// than one state server is not yet support by this plugin.
		if machine.IsManager() || machine.Life() == state.Dead {
			continue
		}
		pendingMachineCount++
		machine := machine
		go func() {
			err := runMachineUpdate(machine, setAgentAddressScript(stateAddr))
			if err != nil {
				logger.Errorf("failed to update machine %s: %v", machine, err)
			} else {
				logger.Infof("updated machine %s", machine)
			}
			done <- err
		}()
	}
	err = nil
	for ; pendingMachineCount > 0; pendingMachineCount-- {
		if updateErr := <-done; updateErr != nil && err == nil {
			err = fmt.Errorf("machine update failed")
		}
	}
	return err
}

// runMachineUpdate connects via ssh to the machine and runs the update script
func runMachineUpdate(m *state.Machine, sshArg string) error {
	logger.Infof("updating machine: %v\n", m)
	addr := instance.SelectPublicAddress(m.Addresses())
	if addr == "" {
		return fmt.Errorf("no appropriate public address found")
	}
	args := []string{
		"-l", "ubuntu",
		"-T",
		"-o", "StrictHostKeyChecking no",
		"-o", "PasswordAuthentication no",
		addr,
		sshArg,
	}
	c := exec.Command("ssh", args...)
	if data, err := c.CombinedOutput(); err != nil {
		return fmt.Errorf("ssh command failed: %v (%q)", err, data)
	}
	return nil
}

func ssh(addr string, script string) error {
	args := []string{
		"-l", "ubuntu",
		"-T",
		"-o", "StrictHostKeyChecking no",
		"-o", "PasswordAuthentication no",
		addr,
		"sudo -n bash -c " + utils.ShQuote(script),
	}
	c := exec.Command("ssh", args...)
	if data, err := c.CombinedOutput(); err != nil {
		return fmt.Errorf("ssh command failed: %v (%q)", err, data)
	}
	return nil
}

func scp(file, host, destFile string) error {
	cmd := exec.Command("scp", "-B", "-q", file, "ubuntu@"+host+":"+destFile)
	logger.Infof("copying backup file to bootstrap host")
	logger.Debugf("scp command: %s %q", cmd.Path, cmd.Args)
	out, err := cmd.CombinedOutput()
	if err == nil {
		return nil
	}
	if _, ok := err.(*exec.ExitError); ok {
		return fmt.Errorf("scp failed: %s", out)
	}
	return err
}

func mustParseTemplate(templ string) *template.Template {
	t := template.New("").Funcs(template.FuncMap{
		"shquote": utils.ShQuote,
	})
	return template.Must(t.Parse(templ))
}

func execTemplate(tmpl *template.Template, data interface{}) string {
	var buf bytes.Buffer
	err := updateBootstrapMachineTemplate.Execute(&buf, data)
	if err != nil {
		panic(fmt.Errorf("template error: %v", err))
	}
	return buf.String()
}
<commit_msg>juju-resore: tweak<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"text/template"

	"launchpad.net/gnuflag"
	"launchpad.net/loggo"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/bootstrap"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/configstore"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/juju"
	_ "launchpad.net/juju-core/provider/all"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/utils"
)

func main() {
	Main(os.Args)
}

func Main(args []string) {
	if err := juju.InitJujuHome(); err != nil {
		fmt.Fprintf(os.Stderr, "error: %s\n", err)
		os.Exit(2)
	}
	os.Exit(cmd.Main(&restoreCommand{}, cmd.DefaultContext(), args[1:]))
}

var logger = loggo.GetLogger("juju.plugins.restore")

const restoreDoc = `
Restore restores a backup created with juju backup
by creating a new juju bootstrap instance and arranging
it so that the existing instances in the environment
talk to it.

It verifies that the existing bootstrap instance is
not running. The given constraints will be used
to choose the new instance.
`

type restoreCommand struct {
	cmd.EnvCommandBase
	Log         cmd.Log
	Constraints constraints.Value
	backupFile  string
}

func (c *restoreCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "juju-restore",
		Purpose: "Restore a backup made with juju backup",
		Args:    "<backupfile.tar.gz>",
		Doc:     restoreDoc,
	}
}

func (c *restoreCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "set environment constraints")
	c.Log.AddFlags(f)
}

func (c *restoreCommand) Init(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("no backup file specified")
	}
	c.backupFile = args[0]
	return nil
}

var updateBootstrapMachineTemplate = mustParseTemplate(`
	set -e -x
	tar xzf juju-backup.tgz
	test -d juju-backup

	initctl stop jujud-machine-0

	initctl stop juju-db
	rm -r /var/lib/juju /var/log/juju
	tar -C / -xvp -f juju-backup/root.tar
	mongorestore --drop --dbpath /var/lib/juju/db juju-backup/dump
	initctl start juju-db

	mongoEval() {
		mongo --ssl -u admin -p {{.AdminSecret | shquote}} localhost:37017/admin --eval "$1"
	}
	# wait for mongo to come up after starting the juju-db upstart service.
	for i in 0 1 2 3 5 6 7
	do
		mongoEval ' ' && break
		sleep 1
	done
	mongoEval '
		db.machines.update({_id: 0}, {$set: {instanceid: {{.NewInstanceId}} } })
		db.instanceData.update({_id: 0}, {$set: {instanceid: {{.NewInstanceId}} } })
	'
	initctl start jujud-machine-0
`)

func updateBootstrapMachineScript(instanceId instance.Id, adminSecret string) string {
	return execTemplate(updateBootstrapMachineTemplate, struct {
		NewInstanceId instance.Id
		AdminSecret string
	}{instanceId, adminSecret})
}

func (c *restoreCommand) Run(ctx *cmd.Context) error {
	if err := c.Log.Start(ctx); err != nil {
		return err
	}
	store, err := configstore.Default()
	if err != nil {
		return err
	}
	cfg, _, err := environs.ConfigForName(c.EnvName, store)
	if err != nil {
		return err
	}
	env, err := rebootstrap(cfg, c.Constraints)
	if err != nil {
		return fmt.Errorf("cannot re-bootstrap environment: %v", err)
	}
	logger.Infof("connecting to newly bootstrapped instance")
	conn, err := juju.NewAPIConn(env, api.DefaultDialOpts())
	if err != nil {
		return fmt.Errorf("cannot connect to bootstrap instance: %v", err)
	}
	newInstId, machine0Addr, err := restoreBootstrapMachine(conn, c.backupFile)
	if err != nil {
		return fmt.Errorf("cannot restore bootstrap machine: %v", err)
	}
	// Update the environ state to point to the new instance.
	if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{
		StateInstances: []instance.Id{newInstId},
	}); err != nil {
		return fmt.Errorf("cannot update environ bootstrap state storage: %v", err)
	}

	// Construct our own state info rather than using juju.NewConn so
	// that we can avoid storage eventual consistency issues
	// (and it's faster too).
	caCert, ok := cfg.CACert()
	if !ok {
		return fmt.Errorf("configuration has no CA certificate")
	}
	st, err := state.Open(&state.Info{
		Addrs:  []string{fmt.Sprintf("%s:%d", machine0Addr, cfg.StatePort())},
		CACert: caCert,
	}, state.DefaultDialOpts())
	if err != nil {
		return fmt.Errorf("cannot open state: %v", err)
	}
	if err := updateAllMachines(st, machine0Addr); err != nil {
		return fmt.Errorf("cannot update machines: %v", err)
	}
	return nil
}

func rebootstrap(cfg *config.Config, cons constraints.Value) (environs.Environ, error) {
	// Turn on safe mode so that the newly bootstrapped instance
	// will not destroy all the instances it does not know about.
	cfg, err := cfg.Apply(map[string]interface{}{
		"provisioner-safe-mode": true,
	})
	if err != nil {
		return nil, fmt.Errorf("cannot enable provisioner-safe-mode: %v", err)
	}
	env, err := environs.New(cfg)
	if err != nil {
		return nil, err
	}
	state, err := bootstrap.LoadState(env.Storage())
	if err != nil {
		return nil, fmt.Errorf("cannot retrieve environment storage; perhaps the environment was not bootstrapped: %v", err)
	}
	if len(state.StateInstances) == 0 {
		return nil, fmt.Errorf("no instances found on bootstrap state; perhaps the environment was not bootstrapped", err)
	}
	if len(state.StateInstances) > 1 {
		return nil, fmt.Errorf("restore does not support HA juju configurations yet")
	}
	inst, err := env.Instances(state.StateInstances)
	if err == nil {
		return nil, fmt.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst)
	}
	if err != environs.ErrNoInstances {
		return nil, fmt.Errorf("cannot detect whether old instance is still running: %v", err)
	}
	// Remove the storage so that we can bootstrap without the provider complaining.
	if err := env.Storage().Remove(bootstrap.StateFile); err != nil {
		return nil, fmt.Errorf("cannot remove %q from storage: %v", bootstrap.StateFile, err)
	}

	// TODO If we fail beyond here, then we won't have a state file and
	// we won't be able to re-run this script because it fails without it.
	// We could either try to recreate the file if we fail (which is itself
	// error-prone) or we could provide a --no-check flag to make
	// it go ahead anyway without the check.

	if err := bootstrap.Bootstrap(env, cons); err != nil {
		return nil, fmt.Errorf("cannot bootstrap new instance: %v", err)
	}
	return env, nil
}

func restoreBootstrapMachine(conn *juju.APIConn, backupFile string) (newInstId instance.Id, addr string, err error) {
	addr, err = conn.State.Client().PublicAddress("0")
	if err != nil {
		return "", "", fmt.Errorf("cannot get public address of bootstrap machine: %v", err)
	}
	status, err := conn.State.Client().Status()
	if err != nil {
		return "", "", fmt.Errorf("cannot get environment status: %v", err)
	}
	info, ok := status.Machines["0"]
	if !ok {
		return "", "", fmt.Errorf("cannot find bootstrap machine in status")
	}
	newInstId = instance.Id(info.InstanceId)

	if err := scp(backupFile, addr, "~/juju-backup.tgz"); err != nil {
		return "", "", fmt.Errorf("cannot copy backup file to bootstrap instance: %v", err)
	}

	adminSecret := conn.Environ.Config().AdminSecret()
	if err := ssh(addr, updateBootstrapMachineScript(newInstId, adminSecret)); err != nil {
		return "", "", fmt.Errorf("update script failed: %v", err)
	}
	return newInstId, addr, nil
}

var agentAddressTemplate = mustParseTemplate(`
set -exu
cd /var/lib/juju/agents
for agent in *
do
	initctl stop jujud-$agent
	sed -i.old -r "/^(stateaddresses|apiaddresses):/{
		n
		s/- .*(:[0-9]+)/- {{.Address}}\1/
	}" $agent/agent.conf
	if [[ $agent = unit-* ]]
	then
 		sed -i -r 's/change-version: [0-9]+$/change-version: 0/' $agent/state/relations/*/* || true
	fi
	initctl start jujud-$agent
done
sed -i -r 's/^(:syslogtag, startswith, "juju-" @)(.*)(:[0-9]+.*)$/\1{{.Address}}\3/' /etc/rsyslog.d/*-juju*.conf
`)

// setAgentAddressScript generates an ssh script argument to update state addresses
func setAgentAddressScript(stateAddr string) string {
	return execTemplate(agentAddressTemplate, struct {
		Address string
	}{stateAddr})
}

// updateAllMachines finds all machines and resets the stored state address
// in each of them. The address does not include the port.
func updateAllMachines(st *state.State, stateAddr string) error {
	machines, err := st.AllMachines()
	if err != nil {
		return err
	}
	pendingMachineCount := 0
	done := make(chan error)
	for _, machine := range machines {
		// A newly resumed state server requires no updating, and more
		// than one state server is not yet support by this plugin.
		if machine.IsManager() || machine.Life() == state.Dead {
			continue
		}
		pendingMachineCount++
		machine := machine
		go func() {
			err := runMachineUpdate(machine, setAgentAddressScript(stateAddr))
			if err != nil {
				logger.Errorf("failed to update machine %s: %v", machine, err)
			} else {
				logger.Infof("updated machine %s", machine)
			}
			done <- err
		}()
	}
	err = nil
	for ; pendingMachineCount > 0; pendingMachineCount-- {
		if updateErr := <-done; updateErr != nil && err == nil {
			err = fmt.Errorf("machine update failed")
		}
	}
	return err
}

// runMachineUpdate connects via ssh to the machine and runs the update script
func runMachineUpdate(m *state.Machine, sshArg string) error {
	logger.Infof("updating machine: %v\n", m)
	addr := instance.SelectPublicAddress(m.Addresses())
	if addr == "" {
		return fmt.Errorf("no appropriate public address found")
	}
	args := []string{
		"-l", "ubuntu",
		"-T",
		"-o", "StrictHostKeyChecking no",
		"-o", "PasswordAuthentication no",
		addr,
		sshArg,
	}
	c := exec.Command("ssh", args...)
	if data, err := c.CombinedOutput(); err != nil {
		return fmt.Errorf("ssh command failed: %v (%q)", err, data)
	}
	return nil
}

func ssh(addr string, script string) error {
	args := []string{
		"-l", "ubuntu",
		"-T",
		"-o", "StrictHostKeyChecking no",
		"-o", "PasswordAuthentication no",
		addr,
		"sudo -n bash -c " + utils.ShQuote(script),
	}
	c := exec.Command("ssh", args...)
	if data, err := c.CombinedOutput(); err != nil {
		return fmt.Errorf("ssh command failed: %v (%q)", err, data)
	}
	return nil
}

func scp(file, host, destFile string) error {
	cmd := exec.Command("scp", "-B", "-q", file, "ubuntu@"+host+":"+destFile)
	logger.Infof("copying backup file to bootstrap host")
	logger.Debugf("scp command: %s %q", cmd.Path, cmd.Args)
	out, err := cmd.CombinedOutput()
	if err == nil {
		return nil
	}
	if _, ok := err.(*exec.ExitError); ok {
		return fmt.Errorf("scp failed: %s", out)
	}
	return err
}

func mustParseTemplate(templ string) *template.Template {
	t := template.New("").Funcs(template.FuncMap{
		"shquote": utils.ShQuote,
	})
	return template.Must(t.Parse(templ))
}

func execTemplate(tmpl *template.Template, data interface{}) string {
	var buf bytes.Buffer
	err := updateBootstrapMachineTemplate.Execute(&buf, data)
	if err != nil {
		panic(fmt.Errorf("template error: %v", err))
	}
	return buf.String()
}
<|endoftext|>


<commit_before>package middleware

import (
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/newrelic/go-agent"
	"github.com/oinume/lekcije/server/bootstrap"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/context_data"
	"github.com/oinume/lekcije/server/controller"
	"github.com/oinume/lekcije/server/controller/flash_message"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/rs/cors"
	"go.uber.org/zap"
)

var _ = fmt.Print

const maxDBConnections = 5

func PanicHandler(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r := recover(); r != nil {
				var err error
				switch errorType := r.(type) {
				case string:
					err = fmt.Errorf(errorType)
				case error:
					err = errorType
				default:
					err = fmt.Errorf("Unknown error type: %v", errorType)
				}
				controller.InternalServerError(w, errors.InternalWrapf(err, "panic ocurred"))
				return
			}
		}()
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func AccessLogger(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		for name, values := range r.Header {
			logger.App.Info(fmt.Sprintf("%v: %v", name, values))
		}
		start := time.Now()
		writerProxy := controller.WrapWriter(w)
		h.ServeHTTP(writerProxy, r)
		func() {
			end := time.Now()
			status := writerProxy.Status()
			if status == 0 {
				status = http.StatusOK
			}
			trackingID := ""
			if v, err := context_data.GetTrackingID(r.Context()); err == nil {
				trackingID = v
			}

			// 180.76.15.26 - - [31/Jul/2016:13:18:07 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
			logger.Access.Info(
				"access",
				zap.String("date", start.Format(time.RFC3339)),
				zap.String("method", r.Method),
				zap.String("url", r.URL.String()),
				zap.Int("status", status),
				zap.Int("bytes", writerProxy.BytesWritten()),
				zap.String("remoteAddr", controller.GetRemoteAddress(r)),
				zap.String("userAgent", r.Header.Get("User-Agent")),
				zap.String("referer", r.Referer()),
				zap.Duration("elapsed", end.Sub(start)/time.Millisecond),
				zap.String("trackingID", trackingID),
			)
		}()
	}
	return http.HandlerFunc(fn)
}

func NewRelic(h http.Handler) http.Handler {
	key := os.Getenv("NEW_RELIC_LICENSE_KEY")
	if key == "" {
		return h
	}

	c := newrelic.NewConfig("lekcije", key)
	app, err := newrelic.NewApplication(c)
	if err != nil {
		logger.App.Error("Failed to newrelic.NewApplication()", zap.Error(err))
		return h
	}
	fn := func(w http.ResponseWriter, r *http.Request) {
		tx := app.StartTransaction(r.URL.Path, w, r)
		defer tx.End()
		h.ServeHTTP(tx, r)
	}
	return http.HandlerFunc(fn)
}

func SetDBAndRedis(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		if config.IsLocalEnv() {
			fmt.Printf("%s %s\n", r.Method, r.RequestURI)
		}

		db, err := model.OpenDB(
			bootstrap.ServerEnvVars.DBURL(),
			maxDBConnections,
			!config.IsProductionEnv(),
		)
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer db.Close()
		ctx = context_data.SetDB(ctx, db)

		redisClient, c, err := model.OpenRedisAndSetToContext(ctx, os.Getenv("REDIS_URL"))
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer redisClient.Close()
		_, c = flash_message.NewStoreRedisAndSetToContext(c, redisClient)

		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func SetLoggedInUser(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.APITokenCookieName)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}

		userService := model.NewUserService(context_data.MustDB(ctx))
		user, err := userService.FindLoggedInUser(cookie.Value)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}
		c := context_data.SetLoggedInUser(ctx, user)
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func SetTrackingID(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ignoreURLs := []string{
			"/api/status",
			"/robots.txt",
			"/sitemap.xml",
		}
		for _, u := range ignoreURLs {
			if r.RequestURI == u {
				h.ServeHTTP(w, r)
				return
			}
		}

		cookie, err := r.Cookie(controller.TrackingIDCookieName)
		var trackingID string
		if err == nil {
			trackingID = cookie.Value
		} else {
			trackingID = uuid.New().String()
			domain := strings.Replace(r.Host, "www.", "", 1)
			domain = strings.Replace(domain, ":4000", "", 1) // TODO: local only
			c := &http.Cookie{
				Name:     controller.TrackingIDCookieName,
				Value:    trackingID,
				Path:     "/",
				Domain:   domain,
				Expires:  time.Now().UTC().Add(time.Hour * 24 * 365 * 2),
				HttpOnly: true,
			}
			http.SetCookie(w, c)
		}
		c := context_data.SetTrackingID(r.Context(), trackingID)
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func LoginRequiredFilter(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if !strings.HasPrefix(r.RequestURI, "/me") {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.APITokenCookieName)
		if err != nil {
			logger.App.Debug("Not logged in")
			http.Redirect(w, r, config.WebURL(), http.StatusFound)
			return
		}

		// TODO: Use context_data.MustLoggedInUser(ctx)
		userService := model.NewUserService(context_data.MustDB(ctx))
		user, err := userService.FindLoggedInUser(cookie.Value)
		if err != nil {
			switch err.(type) {
			case *errors.NotFound:
				logger.App.Debug("not logged in")
				http.Redirect(w, r, config.WebURL(), http.StatusFound)
				return
			default:
				controller.InternalServerError(w, err)
				return
			}
		}
		logger.App.Debug("Logged in user", zap.String("name", user.Name))
		c := context_data.SetLoggedInUser(ctx, user)
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func CORS(h http.Handler) http.Handler {
	origins := []string{}
	if strings.HasPrefix(config.StaticURL(), "http") {
		origins = append(origins, strings.TrimSuffix(config.StaticURL(), "/static"))
	}
	c := cors.New(cors.Options{
		AllowedOrigins: origins,
		//Debug:          true,
	})
	fn := func(w http.ResponseWriter, r *http.Request) {
		c.HandlerFunc(w, r)
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func Redirecter(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if r.Host == "lekcije.herokuapp.com" {
			http.Redirect(w, r, config.WebURL()+r.RequestURI, http.StatusMovedPermanently)
			return
		}
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}
<commit_msg>Remove debug log<commit_after>package middleware

import (
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/newrelic/go-agent"
	"github.com/oinume/lekcije/server/bootstrap"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/context_data"
	"github.com/oinume/lekcije/server/controller"
	"github.com/oinume/lekcije/server/controller/flash_message"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/rs/cors"
	"go.uber.org/zap"
)

var _ = fmt.Print

const maxDBConnections = 5

func PanicHandler(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r := recover(); r != nil {
				var err error
				switch errorType := r.(type) {
				case string:
					err = fmt.Errorf(errorType)
				case error:
					err = errorType
				default:
					err = fmt.Errorf("Unknown error type: %v", errorType)
				}
				controller.InternalServerError(w, errors.InternalWrapf(err, "panic ocurred"))
				return
			}
		}()
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func AccessLogger(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		writerProxy := controller.WrapWriter(w)
		h.ServeHTTP(writerProxy, r)
		func() {
			end := time.Now()
			status := writerProxy.Status()
			if status == 0 {
				status = http.StatusOK
			}
			trackingID := ""
			if v, err := context_data.GetTrackingID(r.Context()); err == nil {
				trackingID = v
			}

			// 180.76.15.26 - - [31/Jul/2016:13:18:07 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
			logger.Access.Info(
				"access",
				zap.String("date", start.Format(time.RFC3339)),
				zap.String("method", r.Method),
				zap.String("url", r.URL.String()),
				zap.Int("status", status),
				zap.Int("bytes", writerProxy.BytesWritten()),
				zap.String("remoteAddr", controller.GetRemoteAddress(r)),
				zap.String("userAgent", r.Header.Get("User-Agent")),
				zap.String("referer", r.Referer()),
				zap.Duration("elapsed", end.Sub(start)/time.Millisecond),
				zap.String("trackingID", trackingID),
			)
		}()
	}
	return http.HandlerFunc(fn)
}

func NewRelic(h http.Handler) http.Handler {
	key := os.Getenv("NEW_RELIC_LICENSE_KEY")
	if key == "" {
		return h
	}

	c := newrelic.NewConfig("lekcije", key)
	app, err := newrelic.NewApplication(c)
	if err != nil {
		logger.App.Error("Failed to newrelic.NewApplication()", zap.Error(err))
		return h
	}
	fn := func(w http.ResponseWriter, r *http.Request) {
		tx := app.StartTransaction(r.URL.Path, w, r)
		defer tx.End()
		h.ServeHTTP(tx, r)
	}
	return http.HandlerFunc(fn)
}

func SetDBAndRedis(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		if config.IsLocalEnv() {
			fmt.Printf("%s %s\n", r.Method, r.RequestURI)
		}

		db, err := model.OpenDB(
			bootstrap.ServerEnvVars.DBURL(),
			maxDBConnections,
			!config.IsProductionEnv(),
		)
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer db.Close()
		ctx = context_data.SetDB(ctx, db)

		redisClient, c, err := model.OpenRedisAndSetToContext(ctx, os.Getenv("REDIS_URL"))
		if err != nil {
			controller.InternalServerError(w, err)
			return
		}
		defer redisClient.Close()
		_, c = flash_message.NewStoreRedisAndSetToContext(c, redisClient)

		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func SetLoggedInUser(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if r.RequestURI == "/api/status" {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.APITokenCookieName)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}

		userService := model.NewUserService(context_data.MustDB(ctx))
		user, err := userService.FindLoggedInUser(cookie.Value)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}
		c := context_data.SetLoggedInUser(ctx, user)
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func SetTrackingID(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ignoreURLs := []string{
			"/api/status",
			"/robots.txt",
			"/sitemap.xml",
		}
		for _, u := range ignoreURLs {
			if r.RequestURI == u {
				h.ServeHTTP(w, r)
				return
			}
		}

		cookie, err := r.Cookie(controller.TrackingIDCookieName)
		var trackingID string
		if err == nil {
			trackingID = cookie.Value
		} else {
			trackingID = uuid.New().String()
			domain := strings.Replace(r.Host, "www.", "", 1)
			domain = strings.Replace(domain, ":4000", "", 1) // TODO: local only
			c := &http.Cookie{
				Name:     controller.TrackingIDCookieName,
				Value:    trackingID,
				Path:     "/",
				Domain:   domain,
				Expires:  time.Now().UTC().Add(time.Hour * 24 * 365 * 2),
				HttpOnly: true,
			}
			http.SetCookie(w, c)
		}
		c := context_data.SetTrackingID(r.Context(), trackingID)
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func LoginRequiredFilter(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		if !strings.HasPrefix(r.RequestURI, "/me") {
			h.ServeHTTP(w, r)
			return
		}
		cookie, err := r.Cookie(controller.APITokenCookieName)
		if err != nil {
			logger.App.Debug("Not logged in")
			http.Redirect(w, r, config.WebURL(), http.StatusFound)
			return
		}

		// TODO: Use context_data.MustLoggedInUser(ctx)
		userService := model.NewUserService(context_data.MustDB(ctx))
		user, err := userService.FindLoggedInUser(cookie.Value)
		if err != nil {
			switch err.(type) {
			case *errors.NotFound:
				logger.App.Debug("not logged in")
				http.Redirect(w, r, config.WebURL(), http.StatusFound)
				return
			default:
				controller.InternalServerError(w, err)
				return
			}
		}
		logger.App.Debug("Logged in user", zap.String("name", user.Name))
		c := context_data.SetLoggedInUser(ctx, user)
		h.ServeHTTP(w, r.WithContext(c))
	}
	return http.HandlerFunc(fn)
}

func CORS(h http.Handler) http.Handler {
	origins := []string{}
	if strings.HasPrefix(config.StaticURL(), "http") {
		origins = append(origins, strings.TrimSuffix(config.StaticURL(), "/static"))
	}
	c := cors.New(cors.Options{
		AllowedOrigins: origins,
		//Debug:          true,
	})
	fn := func(w http.ResponseWriter, r *http.Request) {
		c.HandlerFunc(w, r)
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}

func Redirecter(h http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		if r.Host == "lekcije.herokuapp.com" {
			http.Redirect(w, r, config.WebURL()+r.RequestURI, http.StatusMovedPermanently)
			return
		}
		h.ServeHTTP(w, r)
	}
	return http.HandlerFunc(fn)
}
<|endoftext|>


<commit_before>// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package binding

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"net/http"
	"reflect"
	"strconv"
	"strings"
)

type (
	Binding interface {
		Bind(*http.Request, interface{}) error
	}

	// JSON binding
	jsonBinding struct{}

	// XML binding
	xmlBinding struct{}

	// // form binding
	formBinding struct{}
)

var (
	JSON = jsonBinding{}
	XML  = xmlBinding{}
	Form = formBinding{} // todo
)

func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := json.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := xml.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ formBinding) Bind(req *http.Request, obj interface{}) error {
	if err := req.ParseForm(); err != nil {
		return err
	}
	if err := mapForm(obj, req.Form); err != nil {
		return err
	}
	return Validate(obj)
}

func mapForm(ptr interface{}, form map[string][]string) error {
	typ := reflect.TypeOf(ptr).Elem()
	formStruct := reflect.ValueOf(ptr).Elem()
	for i := 0; i < typ.NumField(); i++ {
		typeField := typ.Field(i)
		if inputFieldName := typeField.Tag.Get("form"); inputFieldName != "" {
			structField := formStruct.Field(i)
			if !structField.CanSet() {
				continue
			}

			inputValue, exists := form[inputFieldName]
			if !exists {
				continue
			}
			numElems := len(inputValue)
			if structField.Kind() == reflect.Slice && numElems > 0 {
				sliceOf := structField.Type().Elem().Kind()
				slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
				for i := 0; i < numElems; i++ {
					if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
						return err
					}
				}
				formStruct.Elem().Field(i).Set(slice)
			} else {
				if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
	switch valueKind {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if val == "" {
			val = "0"
		}
		intVal, err := strconv.Atoi(val)
		if err != nil {
			return err
		} else {
			structField.SetInt(int64(intVal))
		}
	case reflect.Bool:
		if val == "" {
			val = "false"
		}
		boolVal, err := strconv.ParseBool(val)
		if err != nil {
			return err
		} else {
			structField.SetBool(boolVal)
		}
	case reflect.Float32:
		if val == "" {
			val = "0.0"
		}
		floatVal, err := strconv.ParseFloat(val, 32)
		if err != nil {
			return err
		} else {
			structField.SetFloat(floatVal)
		}
	case reflect.Float64:
		if val == "" {
			val = "0.0"
		}
		floatVal, err := strconv.ParseFloat(val, 64)
		if err != nil {
			return err
		} else {
			structField.SetFloat(floatVal)
		}
	case reflect.String:
		structField.SetString(val)
	}
	return nil
}

// Don't pass in pointers to bind to. Can lead to bugs. See:
// https://github.com/codegangsta/martini-contrib/issues/40
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
func ensureNotPointer(obj interface{}) {
	if reflect.TypeOf(obj).Kind() == reflect.Ptr {
		panic("Pointers are not accepted as binding models")
	}
}

func Validate(obj interface{}, parents ...string) error {
	typ := reflect.TypeOf(obj)
	val := reflect.ValueOf(obj)

	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		val = val.Elem()
	}

	switch typ.Kind() {
	case reflect.Struct:
		for i := 0; i < typ.NumField(); i++ {
			field := typ.Field(i)

			// Allow ignored fields in the struct
			if field.Tag.Get("form") == "-" {
				continue
			}

			fieldValue := val.Field(i).Interface()
			zero := reflect.Zero(field.Type).Interface()

			if strings.Index(field.Tag.Get("binding"), "required") > -1 {
				fieldType := field.Type.Kind()
				if fieldType == reflect.Struct {
					if reflect.DeepEqual(zero, fieldValue) {
						return errors.New("Required " + field.Name)
					}
					err := Validate(fieldValue, field.Name)
					if err != nil {
						return err
					}
				} else if reflect.DeepEqual(zero, fieldValue) {
					if len(parents) > 0 {
						return errors.New("Required " + field.Name + " on " + parents[0])
					} else {
						return errors.New("Required " + field.Name)
					}
				} else if fieldType == reflect.Slice && field.Type.Elem().Kind() == reflect.Struct {
					err := Validate(fieldValue)
					if err != nil {
						return err
					}
				}
			} else {
				fieldType := field.Type.Kind()
				if fieldType == reflect.Struct {
					if reflect.DeepEqual(zero, fieldValue) {
						continue
					}
					err := Validate(fieldValue, field.Name)
					if err != nil {
						return err
					}
				}
			}
		}
	case reflect.Slice:
		for i := 0; i < val.Len(); i++ {
			fieldValue := val.Index(i).Interface()
			err := Validate(fieldValue)
			if err != nil {
				return err
			}
		}
	default:
		return nil
	}
	return nil
}
<commit_msg>Add slice elements check for not required slice<commit_after>// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package binding

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"net/http"
	"reflect"
	"strconv"
	"strings"
)

type (
	Binding interface {
		Bind(*http.Request, interface{}) error
	}

	// JSON binding
	jsonBinding struct{}

	// XML binding
	xmlBinding struct{}

	// // form binding
	formBinding struct{}
)

var (
	JSON = jsonBinding{}
	XML  = xmlBinding{}
	Form = formBinding{} // todo
)

func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := json.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := xml.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ formBinding) Bind(req *http.Request, obj interface{}) error {
	if err := req.ParseForm(); err != nil {
		return err
	}
	if err := mapForm(obj, req.Form); err != nil {
		return err
	}
	return Validate(obj)
}

func mapForm(ptr interface{}, form map[string][]string) error {
	typ := reflect.TypeOf(ptr).Elem()
	formStruct := reflect.ValueOf(ptr).Elem()
	for i := 0; i < typ.NumField(); i++ {
		typeField := typ.Field(i)
		if inputFieldName := typeField.Tag.Get("form"); inputFieldName != "" {
			structField := formStruct.Field(i)
			if !structField.CanSet() {
				continue
			}

			inputValue, exists := form[inputFieldName]
			if !exists {
				continue
			}
			numElems := len(inputValue)
			if structField.Kind() == reflect.Slice && numElems > 0 {
				sliceOf := structField.Type().Elem().Kind()
				slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
				for i := 0; i < numElems; i++ {
					if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
						return err
					}
				}
				formStruct.Elem().Field(i).Set(slice)
			} else {
				if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
	switch valueKind {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if val == "" {
			val = "0"
		}
		intVal, err := strconv.Atoi(val)
		if err != nil {
			return err
		} else {
			structField.SetInt(int64(intVal))
		}
	case reflect.Bool:
		if val == "" {
			val = "false"
		}
		boolVal, err := strconv.ParseBool(val)
		if err != nil {
			return err
		} else {
			structField.SetBool(boolVal)
		}
	case reflect.Float32:
		if val == "" {
			val = "0.0"
		}
		floatVal, err := strconv.ParseFloat(val, 32)
		if err != nil {
			return err
		} else {
			structField.SetFloat(floatVal)
		}
	case reflect.Float64:
		if val == "" {
			val = "0.0"
		}
		floatVal, err := strconv.ParseFloat(val, 64)
		if err != nil {
			return err
		} else {
			structField.SetFloat(floatVal)
		}
	case reflect.String:
		structField.SetString(val)
	}
	return nil
}

// Don't pass in pointers to bind to. Can lead to bugs. See:
// https://github.com/codegangsta/martini-contrib/issues/40
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
func ensureNotPointer(obj interface{}) {
	if reflect.TypeOf(obj).Kind() == reflect.Ptr {
		panic("Pointers are not accepted as binding models")
	}
}

func Validate(obj interface{}, parents ...string) error {
	typ := reflect.TypeOf(obj)
	val := reflect.ValueOf(obj)

	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		val = val.Elem()
	}

	switch typ.Kind() {
	case reflect.Struct:
		for i := 0; i < typ.NumField(); i++ {
			field := typ.Field(i)

			// Allow ignored fields in the struct
			if field.Tag.Get("form") == "-" {
				continue
			}

			fieldValue := val.Field(i).Interface()
			zero := reflect.Zero(field.Type).Interface()

			if strings.Index(field.Tag.Get("binding"), "required") > -1 {
				fieldType := field.Type.Kind()
				if fieldType == reflect.Struct {
					if reflect.DeepEqual(zero, fieldValue) {
						return errors.New("Required " + field.Name)
					}
					err := Validate(fieldValue, field.Name)
					if err != nil {
						return err
					}
				} else if reflect.DeepEqual(zero, fieldValue) {
					if len(parents) > 0 {
						return errors.New("Required " + field.Name + " on " + parents[0])
					} else {
						return errors.New("Required " + field.Name)
					}
				} else if fieldType == reflect.Slice && field.Type.Elem().Kind() == reflect.Struct {
					err := Validate(fieldValue)
					if err != nil {
						return err
					}
				}
			} else {
				fieldType := field.Type.Kind()
				if fieldType == reflect.Struct {
					if reflect.DeepEqual(zero, fieldValue) {
						continue
					}
					err := Validate(fieldValue, field.Name)
					if err != nil {
						return err
					}
				} else if fieldType == reflect.Slice && field.Type.Elem().Kind() == reflect.Struct {
					err := Validate(fieldValue, field.Name)
					if err != nil {
						return err
					}
				}
			}
		}
	case reflect.Slice:
		for i := 0; i < val.Len(); i++ {
			fieldValue := val.Index(i).Interface()
			err := Validate(fieldValue)
			if err != nil {
				return err
			}
		}
	default:
		return nil
	}
	return nil
}
<|endoftext|>


<commit_before>package dhcpv4

// MessageType is the type for the various DHCP messages defined in RFC2132.
type MessageType byte

const (
	MessageTypeDhcpDiscover = MessageType(1)
	MessageTypeDhcpOffer    = MessageType(2)
	MessageTypeDhcpRequest  = MessageType(3)
	MessageTypeDhcpDecline  = MessageType(4)
	MessageTypeDhcpAck      = MessageType(5)
	MessageTypeDhcpNak      = MessageType(6)
	MessageTypeDhcpRelease  = MessageType(7)
	MessageTypeDhcpInform   = MessageType(8)
)

// Option is the type for DHCP option tags.
type Option byte

// OptionMap maps DHCP option tags to their values.
type OptionMap map[Option][]byte

// From RFC2132: DHCP Options and BOOTP Vendor Extensions
const (
	// RFC2132 Section 3: RFC 1497 Vendor Extensions
	OptionPad           = Option(0)
	OptionEnd           = Option(255)
	OptionSubnetMask    = Option(1)
	OptionTimeOffset    = Option(2)
	OptionRouter        = Option(3)
	OptionTimeServer    = Option(4)
	OptionNameServer    = Option(5)
	OptionDomainServer  = Option(6)
	OptionLogServer     = Option(7)
	OptionQuotesServer  = Option(8)
	OptionLPRServer     = Option(9)
	OptionImpressServer = Option(10)
	OptionRLPServer     = Option(11)
	OptionHostname      = Option(12)
	OptionBootFileSize  = Option(13)
	OptionMeritDumpFile = Option(14)
	OptionDomainName    = Option(15)
	OptionSwapServer    = Option(16)
	OptionRootPath      = Option(17)
	OptionExtensionFile = Option(18)

	// RFC2132 Section 4: IP Layer Parameters per Host
	OptionForwardOnOff  = Option(19)
	OptionSrcRteOnOff   = Option(20)
	OptionPolicyFilter  = Option(21)
	OptionMaxDGAssembly = Option(22)
	OptionDefaultIPTTL  = Option(23)
	OptionMTUTimeout    = Option(24)
	OptionMTUPlateau    = Option(25)

	// RFC2132 Section 5: IP Layer Parameters per Interface
	OptionMTUInterface     = Option(26)
	OptionMTUSubnet        = Option(27)
	OptionBroadcastAddress = Option(28)
	OptionMaskDiscovery    = Option(29)
	OptionMaskSupplier     = Option(30)
	OptionRouterDiscovery  = Option(31)
	OptionRouterRequest    = Option(32)
	OptionStaticRoute      = Option(33)

	// RFC2132 Section 6: Link Layer Parameters per Interface
	OptionTrailers   = Option(34)
	OptionARPTimeout = Option(35)
	OptionEthernet   = Option(36)

	// RFC2132 Section 7: TCP Parameters
	OptionDefaultTCPTTL = Option(37)
	OptionKeepaliveTime = Option(38)
	OptionKeepaliveData = Option(39)

	// RFC2132 Section 8: Application and Service Parameters
	OptionNISDomain        = Option(40)
	OptionNISServers       = Option(41)
	OptionNTPServers       = Option(42)
	OptionVendorSpecific   = Option(43)
	OptionNETBIOSNameSrv   = Option(44)
	OptionNETBIOSDistSrv   = Option(45)
	OptionNETBIOSNodeType  = Option(46)
	OptionNETBIOSScope     = Option(47)
	OptionXWindowFont      = Option(48)
	OptionXWindowManager   = Option(49)
	OptionNISDomainName    = Option(64)
	OptionNISServerAddr    = Option(65)
	OptionHomeAgentAddrs   = Option(68)
	OptionSMTPServer       = Option(69)
	OptionPOP3Server       = Option(70)
	OptionNNTPServer       = Option(71)
	OptionWWWServer        = Option(72)
	OptionFingerServer     = Option(73)
	OptionIRCServer        = Option(74)
	OptionStreetTalkServer = Option(75)
	OptionSTDAServer       = Option(76)

	// RFC2132 Section 9: DHCP Extensions
	OptionAddressRequest = Option(50)
	OptionAddressTime    = Option(51)
	OptionOverload       = Option(52)
	OptionServerName     = Option(66)
	OptionBootfileName   = Option(67)
	OptionDHCPMsgType    = Option(53)
	OptionDHCPServerID   = Option(54)
	OptionParameterList  = Option(55)
	OptionDHCPMessage    = Option(56)
	OptionDHCPMaxMsgSize = Option(57)
	OptionRenewalTime    = Option(58)
	OptionRebindingTime  = Option(59)
	OptionClassID        = Option(60)
	OptionClientID       = Option(61)
)

// From RFC2241: DHCP Options for Novell Directory Services
const (
	OptionNDSServers  = Option(85)
	OptionNDSTreeName = Option(86)
	OptionNDSContext  = Option(87)
)

// From RFC2242: NetWare/IP Domain Name and Information
const (
	OptionNetWareIPDomain = Option(62)
	OptionNetWareIPOption = Option(63)
)

// From RFC2485: DHCP Option for The Open Group\x27s User Authentication Protocol
const (
	OptionUserAuth = Option(98)
)

// From RFC2563: DHCP Option to Disable Stateless Auto-Configuration in IPv4 Clients
const (
	OptionAutoConfig = Option(116)
)

// From RFC2610: DHCP Options for Service Location Protocol
const (
	OptionDirectoryAgent = Option(78)
	OptionServiceScope   = Option(79)
)

// From RFC2937: The Name Service Search Option for DHCP
const (
	OptionNameServiceSearch = Option(117)
)

// From RFC3004: The User Class Option for DHCP
const (
	OptionUserClass = Option(77)
)

// From RFC3011: The IPv4 Subnet Selection Option for DHCP
const (
	OptionSubnetSelectionOption = Option(118)
)

// From RFC3046: DHCP Relay Agent Information Option
const (
	OptionRelayAgentInformation = Option(82)
)

// From RFC3118: Authentication for DHCP Messages
const (
	OptionAuthentication = Option(90)
)

// From RFC3361: Dynamic Host Configuration Protocol (DHCP-for-IPv4) Option for Session Initiation Protocol (SIP) Servers
const (
	OptionSIPServersDHCPOption = Option(120)
)

// From RFC3397: Dynamic Host Configuration Protocol (DHCP) Domain Search Option
const (
	OptionDomainSearch = Option(119)
)

// From RFC3442: The Classless Static Route Option for Dynamic Host Configuration Protocol (DHCP) version 4
const (
	OptionClasslessStaticRouteOption = Option(121)
)

// From RFC3495: Dynamic Host Configuration Protocol (DHCP) Option for CableLabs Client Configuration
const (
	OptionCCC = Option(122)
)

// From RFC3679: Unused Dynamic Host Configuration Protocol (DHCP) Option Codes
const (
	OptionLDAP           = Option(95)
	OptionNetinfoAddress = Option(112)
	OptionNetinfoTag     = Option(113)
	OptionURL            = Option(114)
)

// From RFC3925: Vendor-Identifying Vendor Options for Dynamic Host Configuration Protocol version 4 (DHCPv4)
const (
	OptionVIVendorClass               = Option(124)
	OptionVIVendorSpecificInformation = Option(125)
)

// From RFC4039: Rapid Commit Option for the Dynamic Host Configuration Protocol version 4 (DHCPv4)
const (
	OptionRapidCommit = Option(80)
)

// From RFC4174: The IPv4 Dynamic Host Configuration Protocol (DHCP) Option for the Internet Storage Name Service
const (
	OptioniSNS = Option(83)
)

// From RFC4280: Dynamic Host Configuration Protocol (DHCP) Options for Broadcast and Multicast Control Servers
const (
	OptionBCMCSControllerDomainNameList    = Option(88)
	OptionBCMCSControllerIPv4AddressOption = Option(89)
)

// From RFC4388: Dynamic Host Configuration Protocol (DHCP) Leasequery
const (
	OptionClientLastTransactionTimeOption = Option(91)
	OptionAssociatedIPOption              = Option(92)
)

// From RFC4578: Dynamic Host Configuration Protocol (DHCP) Options for the Intel Preboot eXecution Environment (PXE)
const (
	OptionClientSystem    = Option(93)
	OptionClientNDI       = Option(94)
	OptionUUIDGUID        = Option(97)
	OptionPXEUndefined128 = Option(128)
	OptionPXEUndefined129 = Option(129)
	OptionPXEUndefined130 = Option(130)
	OptionPXEUndefined131 = Option(131)
	OptionPXEUndefined132 = Option(132)
	OptionPXEUndefined133 = Option(133)
	OptionPXEUndefined134 = Option(134)
	OptionPXEUndefined135 = Option(135)
)

// From RFC4702: The Dynamic Host Configuration Protocol (DHCP) Client Fully Qualified Domain Name (FQDN) Option
const (
	OptionClientFQDN = Option(81)
)

// From RFC4776: Dynamic Host Configuration Protocol (DHCPv4 and DHCPv6) Option for Civic Addresses Configuration Information
const (
	OptionGeoConfCivic = Option(99)
)

// From RFC4833: Timezone Options for DHCP
const (
	OptionPCode = Option(100)
	OptionTCode = Option(101)
)

// From RFC6225: Dynamic Host Configuration Protocol Options for Coordinate-Based Location Configuration Information
const (
	OptionGeoConfOption = Option(123)
	OptionGeoLoc        = Option(144)
)
<commit_msg>Consistently use upper case DHCP in naming<commit_after>package dhcpv4

// MessageType is the type for the various DHCP messages defined in RFC2132.
type MessageType byte

const (
	MessageTypeDHCPDiscover = MessageType(1)
	MessageTypeDHCPOffer    = MessageType(2)
	MessageTypeDHCPRequest  = MessageType(3)
	MessageTypeDHCPDecline  = MessageType(4)
	MessageTypeDHCPAck      = MessageType(5)
	MessageTypeDHCPNak      = MessageType(6)
	MessageTypeDHCPRelease  = MessageType(7)
	MessageTypeDHCPInform   = MessageType(8)
)

// Option is the type for DHCP option tags.
type Option byte

// OptionMap maps DHCP option tags to their values.
type OptionMap map[Option][]byte

// From RFC2132: DHCP Options and BOOTP Vendor Extensions
const (
	// RFC2132 Section 3: RFC 1497 Vendor Extensions
	OptionPad           = Option(0)
	OptionEnd           = Option(255)
	OptionSubnetMask    = Option(1)
	OptionTimeOffset    = Option(2)
	OptionRouter        = Option(3)
	OptionTimeServer    = Option(4)
	OptionNameServer    = Option(5)
	OptionDomainServer  = Option(6)
	OptionLogServer     = Option(7)
	OptionQuotesServer  = Option(8)
	OptionLPRServer     = Option(9)
	OptionImpressServer = Option(10)
	OptionRLPServer     = Option(11)
	OptionHostname      = Option(12)
	OptionBootFileSize  = Option(13)
	OptionMeritDumpFile = Option(14)
	OptionDomainName    = Option(15)
	OptionSwapServer    = Option(16)
	OptionRootPath      = Option(17)
	OptionExtensionFile = Option(18)

	// RFC2132 Section 4: IP Layer Parameters per Host
	OptionForwardOnOff  = Option(19)
	OptionSrcRteOnOff   = Option(20)
	OptionPolicyFilter  = Option(21)
	OptionMaxDGAssembly = Option(22)
	OptionDefaultIPTTL  = Option(23)
	OptionMTUTimeout    = Option(24)
	OptionMTUPlateau    = Option(25)

	// RFC2132 Section 5: IP Layer Parameters per Interface
	OptionMTUInterface     = Option(26)
	OptionMTUSubnet        = Option(27)
	OptionBroadcastAddress = Option(28)
	OptionMaskDiscovery    = Option(29)
	OptionMaskSupplier     = Option(30)
	OptionRouterDiscovery  = Option(31)
	OptionRouterRequest    = Option(32)
	OptionStaticRoute      = Option(33)

	// RFC2132 Section 6: Link Layer Parameters per Interface
	OptionTrailers   = Option(34)
	OptionARPTimeout = Option(35)
	OptionEthernet   = Option(36)

	// RFC2132 Section 7: TCP Parameters
	OptionDefaultTCPTTL = Option(37)
	OptionKeepaliveTime = Option(38)
	OptionKeepaliveData = Option(39)

	// RFC2132 Section 8: Application and Service Parameters
	OptionNISDomain        = Option(40)
	OptionNISServers       = Option(41)
	OptionNTPServers       = Option(42)
	OptionVendorSpecific   = Option(43)
	OptionNETBIOSNameSrv   = Option(44)
	OptionNETBIOSDistSrv   = Option(45)
	OptionNETBIOSNodeType  = Option(46)
	OptionNETBIOSScope     = Option(47)
	OptionXWindowFont      = Option(48)
	OptionXWindowManager   = Option(49)
	OptionNISDomainName    = Option(64)
	OptionNISServerAddr    = Option(65)
	OptionHomeAgentAddrs   = Option(68)
	OptionSMTPServer       = Option(69)
	OptionPOP3Server       = Option(70)
	OptionNNTPServer       = Option(71)
	OptionWWWServer        = Option(72)
	OptionFingerServer     = Option(73)
	OptionIRCServer        = Option(74)
	OptionStreetTalkServer = Option(75)
	OptionSTDAServer       = Option(76)

	// RFC2132 Section 9: DHCP Extensions
	OptionAddressRequest = Option(50)
	OptionAddressTime    = Option(51)
	OptionOverload       = Option(52)
	OptionServerName     = Option(66)
	OptionBootfileName   = Option(67)
	OptionDHCPMsgType    = Option(53)
	OptionDHCPServerID   = Option(54)
	OptionParameterList  = Option(55)
	OptionDHCPMessage    = Option(56)
	OptionDHCPMaxMsgSize = Option(57)
	OptionRenewalTime    = Option(58)
	OptionRebindingTime  = Option(59)
	OptionClassID        = Option(60)
	OptionClientID       = Option(61)
)

// From RFC2241: DHCP Options for Novell Directory Services
const (
	OptionNDSServers  = Option(85)
	OptionNDSTreeName = Option(86)
	OptionNDSContext  = Option(87)
)

// From RFC2242: NetWare/IP Domain Name and Information
const (
	OptionNetWareIPDomain = Option(62)
	OptionNetWareIPOption = Option(63)
)

// From RFC2485: DHCP Option for The Open Group\x27s User Authentication Protocol
const (
	OptionUserAuth = Option(98)
)

// From RFC2563: DHCP Option to Disable Stateless Auto-Configuration in IPv4 Clients
const (
	OptionAutoConfig = Option(116)
)

// From RFC2610: DHCP Options for Service Location Protocol
const (
	OptionDirectoryAgent = Option(78)
	OptionServiceScope   = Option(79)
)

// From RFC2937: The Name Service Search Option for DHCP
const (
	OptionNameServiceSearch = Option(117)
)

// From RFC3004: The User Class Option for DHCP
const (
	OptionUserClass = Option(77)
)

// From RFC3011: The IPv4 Subnet Selection Option for DHCP
const (
	OptionSubnetSelectionOption = Option(118)
)

// From RFC3046: DHCP Relay Agent Information Option
const (
	OptionRelayAgentInformation = Option(82)
)

// From RFC3118: Authentication for DHCP Messages
const (
	OptionAuthentication = Option(90)
)

// From RFC3361: Dynamic Host Configuration Protocol (DHCP-for-IPv4) Option for Session Initiation Protocol (SIP) Servers
const (
	OptionSIPServersDHCPOption = Option(120)
)

// From RFC3397: Dynamic Host Configuration Protocol (DHCP) Domain Search Option
const (
	OptionDomainSearch = Option(119)
)

// From RFC3442: The Classless Static Route Option for Dynamic Host Configuration Protocol (DHCP) version 4
const (
	OptionClasslessStaticRouteOption = Option(121)
)

// From RFC3495: Dynamic Host Configuration Protocol (DHCP) Option for CableLabs Client Configuration
const (
	OptionCCC = Option(122)
)

// From RFC3679: Unused Dynamic Host Configuration Protocol (DHCP) Option Codes
const (
	OptionLDAP           = Option(95)
	OptionNetinfoAddress = Option(112)
	OptionNetinfoTag     = Option(113)
	OptionURL            = Option(114)
)

// From RFC3925: Vendor-Identifying Vendor Options for Dynamic Host Configuration Protocol version 4 (DHCPv4)
const (
	OptionVIVendorClass               = Option(124)
	OptionVIVendorSpecificInformation = Option(125)
)

// From RFC4039: Rapid Commit Option for the Dynamic Host Configuration Protocol version 4 (DHCPv4)
const (
	OptionRapidCommit = Option(80)
)

// From RFC4174: The IPv4 Dynamic Host Configuration Protocol (DHCP) Option for the Internet Storage Name Service
const (
	OptioniSNS = Option(83)
)

// From RFC4280: Dynamic Host Configuration Protocol (DHCP) Options for Broadcast and Multicast Control Servers
const (
	OptionBCMCSControllerDomainNameList    = Option(88)
	OptionBCMCSControllerIPv4AddressOption = Option(89)
)

// From RFC4388: Dynamic Host Configuration Protocol (DHCP) Leasequery
const (
	OptionClientLastTransactionTimeOption = Option(91)
	OptionAssociatedIPOption              = Option(92)
)

// From RFC4578: Dynamic Host Configuration Protocol (DHCP) Options for the Intel Preboot eXecution Environment (PXE)
const (
	OptionClientSystem    = Option(93)
	OptionClientNDI       = Option(94)
	OptionUUIDGUID        = Option(97)
	OptionPXEUndefined128 = Option(128)
	OptionPXEUndefined129 = Option(129)
	OptionPXEUndefined130 = Option(130)
	OptionPXEUndefined131 = Option(131)
	OptionPXEUndefined132 = Option(132)
	OptionPXEUndefined133 = Option(133)
	OptionPXEUndefined134 = Option(134)
	OptionPXEUndefined135 = Option(135)
)

// From RFC4702: The Dynamic Host Configuration Protocol (DHCP) Client Fully Qualified Domain Name (FQDN) Option
const (
	OptionClientFQDN = Option(81)
)

// From RFC4776: Dynamic Host Configuration Protocol (DHCPv4 and DHCPv6) Option for Civic Addresses Configuration Information
const (
	OptionGeoConfCivic = Option(99)
)

// From RFC4833: Timezone Options for DHCP
const (
	OptionPCode = Option(100)
	OptionTCode = Option(101)
)

// From RFC6225: Dynamic Host Configuration Protocol Options for Coordinate-Based Location Configuration Information
const (
	OptionGeoConfOption = Option(123)
	OptionGeoLoc        = Option(144)
)
<|endoftext|>


<commit_before>package orm

import (
	"encoding"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"encoding/base64"
	"reflect"
)

func indirect(a interface{}) interface{} {
	if a == nil {
		return nil
	}
	if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
		// Avoid creating a reflect.Value if it's not a pointer.
		return a
	}
	v := reflect.ValueOf(a)
	for v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	}
	return v.Interface()
}

func toTimeE(i interface{}) (time.Time, error) {
	i = indirect(i)
	switch s := i.(type) {
	case time.Time:
		return s, nil
	default:
		return time.Time{}, fmt.Errorf("Unable to Cast %#v to Time\n", i)
	}
}

func MsSQLTimeParse(s string) time.Time {
	t, err := time.Parse(time.RFC3339Nano, s)
	if err != nil {
		fmt.Println("MsSQLTimeParse failed:", err)
	}
	return t.Local()
}

func MsSQLTimeFormat(t interface{}) string {
	tm, err := toTimeE(t)
	if err != nil {
		panic(err)
	}
	return tm.UTC().Format("2006-01-02 15:04:05")
}

func TimeToLocalTime(c time.Time) string {
	return c.Local().Format("2006-01-02 15:04:05")
}

func TimeParse(s string) time.Time {
	var err error
	var ret time.Time
	// 可能遇到多种情况
	if strings.HasSuffix(s, "Z") {
		if s != "0000-00-00T00:00:00Z" {
			ret, err = time.ParseInLocation("2006-01-02T15:04:05Z", s, time.Local)
		}
	} else {
		if s != "0000-00-00 00:00:00" {
			ret, err = time.ParseInLocation("2006-01-02 15:04:05", s, time.Local)
		}
	}
	if s != "" && err != nil {
		println("db.TimeParse error:", err.Error(), s)
	}
	return ret
}

func TimeFormat(t time.Time) string {
	return t.Format("2006-01-02 15:04:05")
}

func TimeParseLocalTime(s string) time.Time {
	t, err := time.Parse("2006-01-02 15:04:05", s)
	if err != nil {
		return t
	}
	localTime := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(),
		t.Second(), t.Nanosecond(), time.Local)
	return localTime
}

func NewStringSlice(len int, val string) []string {
	s := make([]string, len)
	for i := 0; i < len; i++ {
		s[i] = val
	}
	return s
}

func SliceJoin(objs []interface{}, sep string) string {
	s := make([]string, 0, len(objs))
	for _, obj := range objs {
		s = append(s, fmt.Sprint(obj))
	}
	return strings.Join(s, sep)
}

func ToFloat64(value interface{}) (float64, error) {
	switch value.(type) {
	case string:
		v, _ := value.(string)
		return strconv.ParseFloat(v, 64)
	case int:
		v, _ := value.(int)
		return float64(v), nil
	case int32:
		v, _ := value.(int32)
		return float64(v), nil
	case int64:
		v, _ := value.(int64)
		return float64(v), nil
	case float32:
		v, _ := value.(float32)
		return float64(v), nil
	case float64:
		v, _ := value.(float64)
		return v, nil
	}
	return float64(0), errors.New("unsupport type to float64")
}

func SQLWhere(conditions []string) string {
	if len(conditions) > 0 {
		return fmt.Sprintf("WHERE %s", strings.Join(conditions, " AND "))
	}
	return ""
}

func SQLOrderBy(field string, revert bool) string {
	if field != "" {
		if revert {
			return fmt.Sprintf("ORDER BY %s DESC", field)
		}
		return fmt.Sprintf("ORDER BY %s ASC", field)
	}
	return ""
}

func SQLOffsetLimit(offset, limit int) string {
	if limit <= 0 {
		return ""
	}
	if offset <= 0 {
		return fmt.Sprintf("LIMIT %d", limit)
	}
	return fmt.Sprintf("LIMIT %d, %d", offset, limit)
}

func MsSQLOffsetLimit(offset, limit int) string {
	if limit <= 0 {
		return ""
	}
	if offset < 0 {
		offset = 0
	}
	return fmt.Sprintf("OFFSET %d ROWS FETCH NEXT %d ROWS ONLY", offset, limit)
}

func atoi(b []byte) (int, error) {
	return strconv.Atoi(string(b))
}

func parseInt(b []byte, base int, bitSize int) (int64, error) {
	return strconv.ParseInt(string(b), base, bitSize)
}

func parseUint(b []byte, base int, bitSize int) (uint64, error) {
	return strconv.ParseUint(string(b), base, bitSize)
}

func parseFloat(b []byte, bitSize int) (float64, error) {
	return strconv.ParseFloat(string(b), bitSize)
}

func StringScan(str string, v interface{}) error {
	b := []byte(str)
	switch v := v.(type) {
	case nil:
		return fmt.Errorf("StringScan(nil)")
	case *string:
		*v = str
		return nil
	case *[]byte:
		*v = b
		return nil
	case *int:
		var err error
		*v, err = atoi(b)
		return err
	case *int8:
		n, err := parseInt(b, 10, 8)
		if err != nil {
			return err
		}
		*v = int8(n)
		return nil
	case *int16:
		n, err := parseInt(b, 10, 16)
		if err != nil {
			return err
		}
		*v = int16(n)
		return nil
	case *int32:
		n, err := parseInt(b, 10, 32)
		if err != nil {
			return err
		}
		*v = int32(n)
		return nil
	case *int64:
		n, err := parseInt(b, 10, 64)
		if err != nil {
			return err
		}
		*v = n
		return nil
	case *uint:
		n, err := parseUint(b, 10, 64)
		if err != nil {
			return err
		}
		*v = uint(n)
		return nil
	case *uint8:
		n, err := parseUint(b, 10, 8)
		if err != nil {
			return err
		}
		*v = uint8(n)
		return nil
	case *uint16:
		n, err := parseUint(b, 10, 16)
		if err != nil {
			return err
		}
		*v = uint16(n)
		return nil
	case *uint32:
		n, err := parseUint(b, 10, 32)
		if err != nil {
			return err
		}
		*v = uint32(n)
		return nil
	case *uint64:
		n, err := parseUint(b, 10, 64)
		if err != nil {
			return err
		}
		*v = n
		return nil
	case *float32:
		n, err := parseFloat(b, 32)
		if err != nil {
			return err
		}
		*v = float32(n)
		return err
	case *float64:
		var err error
		*v, err = parseFloat(b, 64)
		return err
	case *bool:
		*v = len(b) == 1 && b[0] == '1'
		return nil
	case encoding.BinaryUnmarshaler:
		return v.UnmarshalBinary(b)
	default:
		return fmt.Errorf(
			"can't unmarshal %T (consider implementing BinaryUnmarshaler)", v)
	}

}

func Encode(src string) string {
	return base64.StdEncoding.EncodeToString([]byte(src))
}

func Decode(src string) string {
	decoded, err := base64.StdEncoding.DecodeString(src)
	if err != nil {
		panic(err)
	}
	return string(decoded)
}
<commit_msg>fix bool conv<commit_after>package orm

import (
	"encoding"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"encoding/base64"
	"reflect"
)

func indirect(a interface{}) interface{} {
	if a == nil {
		return nil
	}
	if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
		// Avoid creating a reflect.Value if it's not a pointer.
		return a
	}
	v := reflect.ValueOf(a)
	for v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	}
	return v.Interface()
}

func toTimeE(i interface{}) (time.Time, error) {
	i = indirect(i)
	switch s := i.(type) {
	case time.Time:
		return s, nil
	default:
		return time.Time{}, fmt.Errorf("Unable to Cast %#v to Time\n", i)
	}
}

func MsSQLTimeParse(s string) time.Time {
	t, err := time.Parse(time.RFC3339Nano, s)
	if err != nil {
		fmt.Println("MsSQLTimeParse failed:", err)
	}
	return t.Local()
}

func MsSQLTimeFormat(t interface{}) string {
	tm, err := toTimeE(t)
	if err != nil {
		panic(err)
	}
	return tm.UTC().Format("2006-01-02 15:04:05")
}

func TimeToLocalTime(c time.Time) string {
	return c.Local().Format("2006-01-02 15:04:05")
}

func TimeParse(s string) time.Time {
	var err error
	var ret time.Time
	// 可能遇到多种情况
	if strings.HasSuffix(s, "Z") {
		if s != "0000-00-00T00:00:00Z" {
			ret, err = time.ParseInLocation("2006-01-02T15:04:05Z", s, time.Local)
		}
	} else {
		if s != "0000-00-00 00:00:00" {
			ret, err = time.ParseInLocation("2006-01-02 15:04:05", s, time.Local)
		}
	}
	if s != "" && err != nil {
		println("db.TimeParse error:", err.Error(), s)
	}
	return ret
}

func TimeFormat(t time.Time) string {
	return t.Format("2006-01-02 15:04:05")
}

func TimeParseLocalTime(s string) time.Time {
	t, err := time.Parse("2006-01-02 15:04:05", s)
	if err != nil {
		return t
	}
	localTime := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(),
		t.Second(), t.Nanosecond(), time.Local)
	return localTime
}

func NewStringSlice(len int, val string) []string {
	s := make([]string, len)
	for i := 0; i < len; i++ {
		s[i] = val
	}
	return s
}

func SliceJoin(objs []interface{}, sep string) string {
	s := make([]string, 0, len(objs))
	for _, obj := range objs {
		s = append(s, fmt.Sprint(obj))
	}
	return strings.Join(s, sep)
}

func ToFloat64(value interface{}) (float64, error) {
	switch value.(type) {
	case string:
		v, _ := value.(string)
		return strconv.ParseFloat(v, 64)
	case int:
		v, _ := value.(int)
		return float64(v), nil
	case int32:
		v, _ := value.(int32)
		return float64(v), nil
	case int64:
		v, _ := value.(int64)
		return float64(v), nil
	case float32:
		v, _ := value.(float32)
		return float64(v), nil
	case float64:
		v, _ := value.(float64)
		return v, nil
	}
	return float64(0), errors.New("unsupport type to float64")
}

func SQLWhere(conditions []string) string {
	if len(conditions) > 0 {
		return fmt.Sprintf("WHERE %s", strings.Join(conditions, " AND "))
	}
	return ""
}

func SQLOrderBy(field string, revert bool) string {
	if field != "" {
		if revert {
			return fmt.Sprintf("ORDER BY %s DESC", field)
		}
		return fmt.Sprintf("ORDER BY %s ASC", field)
	}
	return ""
}

func SQLOffsetLimit(offset, limit int) string {
	if limit <= 0 {
		return ""
	}
	if offset <= 0 {
		return fmt.Sprintf("LIMIT %d", limit)
	}
	return fmt.Sprintf("LIMIT %d, %d", offset, limit)
}

func MsSQLOffsetLimit(offset, limit int) string {
	if limit <= 0 {
		return ""
	}
	if offset < 0 {
		offset = 0
	}
	return fmt.Sprintf("OFFSET %d ROWS FETCH NEXT %d ROWS ONLY", offset, limit)
}

func atoi(b []byte) (int, error) {
	return strconv.Atoi(string(b))
}

func parseInt(b []byte, base int, bitSize int) (int64, error) {
	return strconv.ParseInt(string(b), base, bitSize)
}

func parseUint(b []byte, base int, bitSize int) (uint64, error) {
	return strconv.ParseUint(string(b), base, bitSize)
}

func parseFloat(b []byte, bitSize int) (float64, error) {
	return strconv.ParseFloat(string(b), bitSize)
}

func parseBool(b []byte) (bool, error) {
	return strconv.ParseBool(string(b))
}

func StringScan(str string, v interface{}) error {
	b := []byte(str)
	switch v := v.(type) {
	case nil:
		return fmt.Errorf("StringScan(nil)")
	case *string:
		*v = str
		return nil
	case *[]byte:
		*v = b
		return nil
	case *int:
		var err error
		*v, err = atoi(b)
		return err
	case *int8:
		n, err := parseInt(b, 10, 8)
		if err != nil {
			return err
		}
		*v = int8(n)
		return nil
	case *int16:
		n, err := parseInt(b, 10, 16)
		if err != nil {
			return err
		}
		*v = int16(n)
		return nil
	case *int32:
		n, err := parseInt(b, 10, 32)
		if err != nil {
			return err
		}
		*v = int32(n)
		return nil
	case *int64:
		n, err := parseInt(b, 10, 64)
		if err != nil {
			return err
		}
		*v = n
		return nil
	case *uint:
		n, err := parseUint(b, 10, 64)
		if err != nil {
			return err
		}
		*v = uint(n)
		return nil
	case *uint8:
		n, err := parseUint(b, 10, 8)
		if err != nil {
			return err
		}
		*v = uint8(n)
		return nil
	case *uint16:
		n, err := parseUint(b, 10, 16)
		if err != nil {
			return err
		}
		*v = uint16(n)
		return nil
	case *uint32:
		n, err := parseUint(b, 10, 32)
		if err != nil {
			return err
		}
		*v = uint32(n)
		return nil
	case *uint64:
		n, err := parseUint(b, 10, 64)
		if err != nil {
			return err
		}
		*v = n
		return nil
	case *float32:
		n, err := parseFloat(b, 32)
		if err != nil {
			return err
		}
		*v = float32(n)
		return err
	case *float64:
		var err error
		*v, err = parseFloat(b, 64)
		return err
	case *bool:
		*v = len(b) == 1 && b[0] == '1'
		if !*v {
			var err error
			*v, err = parseBool(b)
			if err != nil {
				return err
			}
		}
		return nil
	case encoding.BinaryUnmarshaler:
		return v.UnmarshalBinary(b)
	default:
		return fmt.Errorf(
			"can't unmarshal %T (consider implementing BinaryUnmarshaler)", v)
	}

}

func Encode(src string) string {
	return base64.StdEncoding.EncodeToString([]byte(src))
}

func Decode(src string) string {
	decoded, err := base64.StdEncoding.DecodeString(src)
	if err != nil {
		panic(err)
	}
	return string(decoded)
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {

	evenOdd()

	operators()

}

func operators() {

	pf := func(s string, vals ...int) {
		const (
			row = "%04b = %d\n"
		)

		fmt.Println(s)
		for _, i := range vals {
			fmt.Printf(row, i, i)
		}
		fmt.Println()
	}

	// Use bitwise AND & to get the bits
	// that are in 3 AND 6
	pf("and", 3, 6, 3&6)

	// Use bitwise OR | to get the bits
	// that are in 3 OR 6
	pf("or", 3, 6, 3|6)

	// Use bitwise XOR ^ to get the bits
	// that are in 3 OR 6 BUT NOT BOTH
	pf("xor", 3, 6, 3^6)

	// Use bit clear AND NOT &^ to get the bits
	// that are in 3 AND NOT 6 (order matters)
	pf("and not", 3, 6, 3&^6)
}

func evenOdd() {
	fmt.Println("even or odd")
	fmt.Printf("isEvenBitwise(%d) = %v\n", 2, isEvenBitwise(2))
	fmt.Printf("isEvenBitwise(%d) = %v\n", 5, isEvenBitwise(5))
	fmt.Println()
}

func isEvenBitwise(i int) bool {
	v := (i & 1) == 0
	return v
}

func isEvenRemainder(i int) bool {
	v := (i % 2) == 0
	return v
}
<commit_msg>bitwise: fix evenOdd<commit_after>package main

import (
	"fmt"
	"os"
)

func main() {

	evenOdd()

	operators()

}

func operators() {

	pf := func(s string, vals ...int) {
		const (
			row = "%04b = %d\n"
		)

		fmt.Println(s)
		for _, i := range vals {
			fmt.Printf(row, i, i)
		}
		fmt.Println()
	}

	// Use bitwise AND & to get the bits
	// that are in 3 AND 6
	pf("and", 3, 6, 3&6)

	// Use bitwise OR | to get the bits
	// that are in 3 OR 6
	pf("or", 3, 6, 3|6)

	// Use bitwise XOR ^ to get the bits
	// that are in 3 OR 6 BUT NOT BOTH
	pf("xor", 3, 6, 3^6)

	// Use bit clear AND NOT &^ to get the bits
	// that are in 3 AND NOT 6 (order matters)
	pf("and not", 3, 6, 3&^6)
}

func evenOdd() {

	vals := [...]struct {
		n    int
		want bool
	}{
		{n: 2, want: true},
		{n: 5, want: false},
	}
	for _, v := range vals {
		bit := isEvenBitwise(v.n)
		rem := isEvenRemainder(v.n)
		if bit != rem {
			fmt.Println("bitwise != remainder")
			os.Exit(1)
		}
		if got, want := bit, v.want; got != want {
			fmt.Printf("isEvenBitwise(%d) = %v; want %v\n",
				v.n, got, want)
		}
		if got, want := rem, v.want; got != want {
			fmt.Printf("isEvenRemainder(%d) = %v; want %v\n",
				v.n, got, want)
		}
	}

}

func isEvenBitwise(i int) bool {
	v := (i & 1) == 0
	return v
}

func isEvenRemainder(i int) bool {
	v := (i % 2) == 0
	return v
}
<|endoftext|>


<commit_before>package pcap

import (
	"encoding/binary"
	"fmt"
	"reflect"
	"strings"
	"time"
)

type PacketTime struct {
	Sec  int32
	Usec int32
}

// Packet is a single packet parsed from a pcap file.
type Packet struct {
	// porting from 'pcap_pkthdr' struct
	Time   time.Time // packet send/receive time
	Caplen uint32    // bytes stored in the file (caplen <= len)
	Len    uint32    // bytes sent/received

	Data []byte // packet data

	Type    int // protocol type, see LINKTYPE_*
	DestMac uint64
	SrcMac  uint64

	Headers []interface{} // decoded headers, in order
	Payload []byte        // remaining non-header bytes
}

// Decode decodes the headers of a Packet.
func (p *Packet) Decode() {

	p.Type = int(binary.BigEndian.Uint16(p.Data[12:14]))
	p.DestMac = decodemac(p.Data[0:6])
	p.SrcMac = decodemac(p.Data[6:12])
	p.Payload = p.Data[14:]

	switch p.Type {
	case TYPE_IP:
		p.decodeIp()
	case TYPE_IP6:
		p.decodeIp6()
	case TYPE_ARP:
		p.decodeArp()
	}
}

func (p *Packet) headerString(headers []interface{}) string {
	// If there's just one header, return that.
	if len(headers) == 1 {
		if hdr, ok := headers[0].(fmt.Stringer); ok {
			return hdr.String()
		}
	}
	// If there are two headers (IPv4/IPv6 -> TCP/UDP/IP..)
	if len(headers) == 2 {
		// Commonly the first header is an address.
		if addr, ok := p.Headers[0].(addrHdr); ok {
			if hdr, ok := p.Headers[1].(addrStringer); ok {
				return fmt.Sprintf("%s %s", p.Time, hdr.String(addr))
			}
		}
	}
	// For IP in IP, we do a recursive call.
	if len(headers) >= 2 {
		if addr, ok := headers[0].(addrHdr); ok {
			if _, ok := headers[1].(addrHdr); ok {
				return fmt.Sprintf("%s > %s IP in IP: ",
					addr.SrcAddr(), addr.DestAddr(), p.headerString(headers[1:]))
			}
		}
	}

	var typeNames []string
	for _, hdr := range headers {
		typeNames = append(typeNames, reflect.TypeOf(hdr).String())
	}

	return fmt.Sprintf("unknown [%s]", strings.Join(typeNames, ","))
}

// String prints a one-line representation of the packet header.
// The output is suitable for use in a tcpdump program.
func (p *Packet) String() string {
	// If there are no headers, print "unsupported protocol".
	if len(p.Headers) == 0 {
		return fmt.Sprintf("%s unsupported protocol %d", p.Time, int(p.Type))
	}
	return fmt.Sprintf("%s %s", p.Time, p.headerString(p.Headers))
}

func (p *Packet) decodeArp() {
	pkt := p.Payload
	arp := new(Arphdr)
	arp.Addrtype = binary.BigEndian.Uint16(pkt[0:2])
	arp.Protocol = binary.BigEndian.Uint16(pkt[2:4])
	arp.HwAddressSize = pkt[4]
	arp.ProtAddressSize = pkt[5]
	arp.Operation = binary.BigEndian.Uint16(pkt[6:8])
	arp.SourceHwAddress = pkt[8 : 8+arp.HwAddressSize]
	arp.SourceProtAddress = pkt[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
	arp.DestHwAddress = pkt[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
	arp.DestProtAddress = pkt[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]

	p.Headers = append(p.Headers, arp)
	p.Payload = p.Payload[8+2*arp.HwAddressSize+2*arp.ProtAddressSize:]
}

func (p *Packet) decodeIp() {
	if len(p.Payload) < 20 {
		return
	}
	pkt := p.Payload
	ip := new(Iphdr)

	ip.Version = uint8(pkt[0]) >> 4
	ip.Ihl = uint8(pkt[0]) & 0x0F
	ip.Tos = pkt[1]
	ip.Length = binary.BigEndian.Uint16(pkt[2:4])
	ip.Id = binary.BigEndian.Uint16(pkt[4:6])
	flagsfrags := binary.BigEndian.Uint16(pkt[6:8])
	ip.Flags = uint8(flagsfrags >> 13)
	ip.FragOffset = flagsfrags & 0x1FFF
	ip.Ttl = pkt[8]
	ip.Protocol = pkt[9]
	ip.Checksum = binary.BigEndian.Uint16(pkt[10:12])
	ip.SrcIp = pkt[12:16]
	ip.DestIp = pkt[16:20]
	pEnd := int(ip.Length)
	if pEnd > len(pkt) {
		pEnd = len(pkt)
	}
	pIhl := int(ip.Ihl) * 4
	if pIhl > pEnd {
		pIhl = pEnd
	}
	p.Payload = pkt[pIhl:pEnd]
	p.Headers = append(p.Headers, ip)

	switch ip.Protocol {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp()
	}
}

func (p *Packet) decodeTcp() {
	pLenPayload := len(p.Payload)
	if pLenPayload < 20 {
		return
	}
	pkt := p.Payload
	tcp := new(Tcphdr)
	tcp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	tcp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	tcp.Seq = binary.BigEndian.Uint32(pkt[4:8])
	tcp.Ack = binary.BigEndian.Uint32(pkt[8:12])
	tcp.DataOffset = (pkt[12] & 0xF0) >> 4
	tcp.Flags = binary.BigEndian.Uint16(pkt[12:14]) & 0x1FF
	tcp.Window = binary.BigEndian.Uint16(pkt[14:16])
	tcp.Checksum = binary.BigEndian.Uint16(pkt[16:18])
	tcp.Urgent = binary.BigEndian.Uint16(pkt[18:20])
	pDataOffset := int(tcp.DataOffset * 4)
	if pDataOffset > pLenPayload {
		pDataOffset = pLenPayload
	}
	p.Payload = pkt[pDataOffset:]
	p.Headers = append(p.Headers, tcp)
}

func (p *Packet) decodeUdp() {
	if len(p.Payload) < 8 {
		return
	}
	pkt := p.Payload
	udp := new(Udphdr)
	udp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	udp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	udp.Length = binary.BigEndian.Uint16(pkt[4:6])
	udp.Checksum = binary.BigEndian.Uint16(pkt[6:8])
	p.Headers = append(p.Headers, udp)
	p.Payload = pkt[8:]
}

func (p *Packet) decodeIcmp() *Icmphdr {
	if len(p.Payload) < 8 {
		return nil
	}
	pkt := p.Payload
	icmp := new(Icmphdr)
	icmp.Type = pkt[0]
	icmp.Code = pkt[1]
	icmp.Checksum = binary.BigEndian.Uint16(pkt[2:4])
	icmp.Id = binary.BigEndian.Uint16(pkt[4:6])
	icmp.Seq = binary.BigEndian.Uint16(pkt[6:8])
	p.Payload = pkt[8:]
	p.Headers = append(p.Headers, icmp)
	return icmp
}

func (p *Packet) decodeIp6() {
	if len(p.Payload) < 40 {
		return
	}
	pkt := p.Payload
	ip6 := new(Ip6hdr)
	ip6.Version = uint8(pkt[0]) >> 4
	ip6.TrafficClass = uint8((binary.BigEndian.Uint16(pkt[0:2]) >> 4) & 0x00FF)
	ip6.FlowLabel = binary.BigEndian.Uint32(pkt[0:4]) & 0x000FFFFF
	ip6.Length = binary.BigEndian.Uint16(pkt[4:6])
	ip6.NextHeader = pkt[6]
	ip6.HopLimit = pkt[7]
	ip6.SrcIp = pkt[8:24]
	ip6.DestIp = pkt[24:40]
	p.Payload = pkt[40:]
	p.Headers = append(p.Headers, ip6)

	switch ip6.NextHeader {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp()
	}
}
<commit_msg>Tcphdr Data member wasn't being set in decodeTcp. Needed for access to tcp header options<commit_after>package pcap

import (
	"encoding/binary"
	"fmt"
	"reflect"
	"strings"
	"time"
)

type PacketTime struct {
	Sec  int32
	Usec int32
}

// Packet is a single packet parsed from a pcap file.
type Packet struct {
	// porting from 'pcap_pkthdr' struct
	Time   time.Time // packet send/receive time
	Caplen uint32    // bytes stored in the file (caplen <= len)
	Len    uint32    // bytes sent/received

	Data []byte // packet data

	Type    int // protocol type, see LINKTYPE_*
	DestMac uint64
	SrcMac  uint64

	Headers []interface{} // decoded headers, in order
	Payload []byte        // remaining non-header bytes
}

// Decode decodes the headers of a Packet.
func (p *Packet) Decode() {

	p.Type = int(binary.BigEndian.Uint16(p.Data[12:14]))
	p.DestMac = decodemac(p.Data[0:6])
	p.SrcMac = decodemac(p.Data[6:12])
	p.Payload = p.Data[14:]

	switch p.Type {
	case TYPE_IP:
		p.decodeIp()
	case TYPE_IP6:
		p.decodeIp6()
	case TYPE_ARP:
		p.decodeArp()
	}
}

func (p *Packet) headerString(headers []interface{}) string {
	// If there's just one header, return that.
	if len(headers) == 1 {
		if hdr, ok := headers[0].(fmt.Stringer); ok {
			return hdr.String()
		}
	}
	// If there are two headers (IPv4/IPv6 -> TCP/UDP/IP..)
	if len(headers) == 2 {
		// Commonly the first header is an address.
		if addr, ok := p.Headers[0].(addrHdr); ok {
			if hdr, ok := p.Headers[1].(addrStringer); ok {
				return fmt.Sprintf("%s %s", p.Time, hdr.String(addr))
			}
		}
	}
	// For IP in IP, we do a recursive call.
	if len(headers) >= 2 {
		if addr, ok := headers[0].(addrHdr); ok {
			if _, ok := headers[1].(addrHdr); ok {
				return fmt.Sprintf("%s > %s IP in IP: ",
					addr.SrcAddr(), addr.DestAddr(), p.headerString(headers[1:]))
			}
		}
	}

	var typeNames []string
	for _, hdr := range headers {
		typeNames = append(typeNames, reflect.TypeOf(hdr).String())
	}

	return fmt.Sprintf("unknown [%s]", strings.Join(typeNames, ","))
}

// String prints a one-line representation of the packet header.
// The output is suitable for use in a tcpdump program.
func (p *Packet) String() string {
	// If there are no headers, print "unsupported protocol".
	if len(p.Headers) == 0 {
		return fmt.Sprintf("%s unsupported protocol %d", p.Time, int(p.Type))
	}
	return fmt.Sprintf("%s %s", p.Time, p.headerString(p.Headers))
}

func (p *Packet) decodeArp() {
	pkt := p.Payload
	arp := new(Arphdr)
	arp.Addrtype = binary.BigEndian.Uint16(pkt[0:2])
	arp.Protocol = binary.BigEndian.Uint16(pkt[2:4])
	arp.HwAddressSize = pkt[4]
	arp.ProtAddressSize = pkt[5]
	arp.Operation = binary.BigEndian.Uint16(pkt[6:8])
	arp.SourceHwAddress = pkt[8 : 8+arp.HwAddressSize]
	arp.SourceProtAddress = pkt[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
	arp.DestHwAddress = pkt[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
	arp.DestProtAddress = pkt[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]

	p.Headers = append(p.Headers, arp)
	p.Payload = p.Payload[8+2*arp.HwAddressSize+2*arp.ProtAddressSize:]
}

func (p *Packet) decodeIp() {
	if len(p.Payload) < 20 {
		return
	}
	pkt := p.Payload
	ip := new(Iphdr)

	ip.Version = uint8(pkt[0]) >> 4
	ip.Ihl = uint8(pkt[0]) & 0x0F
	ip.Tos = pkt[1]
	ip.Length = binary.BigEndian.Uint16(pkt[2:4])
	ip.Id = binary.BigEndian.Uint16(pkt[4:6])
	flagsfrags := binary.BigEndian.Uint16(pkt[6:8])
	ip.Flags = uint8(flagsfrags >> 13)
	ip.FragOffset = flagsfrags & 0x1FFF
	ip.Ttl = pkt[8]
	ip.Protocol = pkt[9]
	ip.Checksum = binary.BigEndian.Uint16(pkt[10:12])
	ip.SrcIp = pkt[12:16]
	ip.DestIp = pkt[16:20]
	pEnd := int(ip.Length)
	if pEnd > len(pkt) {
		pEnd = len(pkt)
	}
	pIhl := int(ip.Ihl) * 4
	if pIhl > pEnd {
		pIhl = pEnd
	}
	p.Payload = pkt[pIhl:pEnd]
	p.Headers = append(p.Headers, ip)

	switch ip.Protocol {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp()
	}
}

func (p *Packet) decodeTcp() {
	pLenPayload := len(p.Payload)
	if pLenPayload < 20 {
		return
	}
	pkt := p.Payload
	tcp := new(Tcphdr)
	tcp.Data = pkt
	tcp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	tcp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	tcp.Seq = binary.BigEndian.Uint32(pkt[4:8])
	tcp.Ack = binary.BigEndian.Uint32(pkt[8:12])
	tcp.DataOffset = (pkt[12] & 0xF0) >> 4
	tcp.Flags = binary.BigEndian.Uint16(pkt[12:14]) & 0x1FF
	tcp.Window = binary.BigEndian.Uint16(pkt[14:16])
	tcp.Checksum = binary.BigEndian.Uint16(pkt[16:18])
	tcp.Urgent = binary.BigEndian.Uint16(pkt[18:20])
	pDataOffset := int(tcp.DataOffset * 4)
	if pDataOffset > pLenPayload {
		pDataOffset = pLenPayload
	}
	p.Payload = pkt[pDataOffset:]
	p.Headers = append(p.Headers, tcp)
}

func (p *Packet) decodeUdp() {
	if len(p.Payload) < 8 {
		return
	}
	pkt := p.Payload
	udp := new(Udphdr)
	udp.SrcPort = binary.BigEndian.Uint16(pkt[0:2])
	udp.DestPort = binary.BigEndian.Uint16(pkt[2:4])
	udp.Length = binary.BigEndian.Uint16(pkt[4:6])
	udp.Checksum = binary.BigEndian.Uint16(pkt[6:8])
	p.Headers = append(p.Headers, udp)
	p.Payload = pkt[8:]
}

func (p *Packet) decodeIcmp() *Icmphdr {
	if len(p.Payload) < 8 {
		return nil
	}
	pkt := p.Payload
	icmp := new(Icmphdr)
	icmp.Type = pkt[0]
	icmp.Code = pkt[1]
	icmp.Checksum = binary.BigEndian.Uint16(pkt[2:4])
	icmp.Id = binary.BigEndian.Uint16(pkt[4:6])
	icmp.Seq = binary.BigEndian.Uint16(pkt[6:8])
	p.Payload = pkt[8:]
	p.Headers = append(p.Headers, icmp)
	return icmp
}

func (p *Packet) decodeIp6() {
	if len(p.Payload) < 40 {
		return
	}
	pkt := p.Payload
	ip6 := new(Ip6hdr)
	ip6.Version = uint8(pkt[0]) >> 4
	ip6.TrafficClass = uint8((binary.BigEndian.Uint16(pkt[0:2]) >> 4) & 0x00FF)
	ip6.FlowLabel = binary.BigEndian.Uint32(pkt[0:4]) & 0x000FFFFF
	ip6.Length = binary.BigEndian.Uint16(pkt[4:6])
	ip6.NextHeader = pkt[6]
	ip6.HopLimit = pkt[7]
	ip6.SrcIp = pkt[8:24]
	ip6.DestIp = pkt[24:40]
	p.Payload = pkt[40:]
	p.Headers = append(p.Headers, ip6)

	switch ip6.NextHeader {
	case IP_TCP:
		p.decodeTcp()
	case IP_UDP:
		p.decodeUdp()
	case IP_ICMP:
		p.decodeIcmp()
	case IP_INIP:
		p.decodeIp()
	}
}
<|endoftext|>