<commit_before>package agent

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/hashicorp/consul/consul"
	"github.com/hashicorp/consul/consul/structs"
)

const (
	preparedQueryEndpoint      = "PreparedQuery"
	preparedQueryExecuteSuffix = "/execute"
)

// preparedQueryCreateResponse is used to wrap the query ID.
type preparedQueryCreateResponse struct {
	ID string
}

// PreparedQueryGeneral handles all the general prepared query requests.
func (s *HTTPServer) PreparedQueryGeneral(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	endpoint := s.agent.getEndpoint(preparedQueryEndpoint)
	switch req.Method {
	case "POST": // Create a new prepared query.
		args := structs.PreparedQueryRequest{
			Op: structs.PreparedQueryCreate,
		}
		s.parseDC(req, &args.Datacenter)
		s.parseToken(req, &args.Token)
		if req.ContentLength > 0 {
			if err := decodeBody(req, &args.Query, nil); err != nil {
				resp.WriteHeader(400)
				resp.Write([]byte(fmt.Sprintf("Request decode failed: %v", err)))
				return nil, nil
			}
		}

		var reply string
		if err := s.agent.RPC(endpoint+".Apply", &args, &reply); err != nil {
			return nil, err
		}
		return preparedQueryCreateResponse{reply}, nil

	case "GET": // List all the prepared queries.
		var args structs.DCSpecificRequest
		if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
			return nil, nil
		}

		var reply structs.IndexedPreparedQueries
		if err := s.agent.RPC(endpoint+".List", &args, &reply); err != nil {
			return nil, err
		}

		// Use empty list instead of nil.
		if reply.Queries == nil {
			reply.Queries = make(structs.PreparedQueries, 0)
		}
		return reply.Queries, nil

	default:
		resp.WriteHeader(405)
		return nil, nil
	}
}

// parseLimit parses the optional limit parameter for a prepared query execution.
func parseLimit(req *http.Request, limit *int) error {
	*limit = 0
	if arg := req.URL.Query().Get("limit"); arg != "" {
		if i, err := strconv.Atoi(arg); err != nil {
			return err
		} else {
			*limit = i
		}
	}
	return nil
}

// PreparedQuerySpecifc handles all the prepared query requests specific to a
// particular query.
func (s *HTTPServer) PreparedQuerySpecific(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	id := strings.TrimPrefix(req.URL.Path, "/v1/query/")
	execute := false
	if strings.HasSuffix(id, preparedQueryExecuteSuffix) {
		execute = true
		id = strings.TrimSuffix(id, preparedQueryExecuteSuffix)
	}

	endpoint := s.agent.getEndpoint(preparedQueryEndpoint)
	switch req.Method {
	case "GET": // Execute or retrieve a prepared query.
		if execute {
			args := structs.PreparedQueryExecuteRequest{
				QueryIDOrName: id,
			}
			s.parseSource(req, &args.Source)
			if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
				return nil, nil
			}
			if err := parseLimit(req, &args.Limit); err != nil {
				return nil, fmt.Errorf("Bad limit: %s", err)
			}

			var reply structs.PreparedQueryExecuteResponse
			if err := s.agent.RPC(endpoint+".Execute", &args, &reply); err != nil {
				// We have to check the string since the RPC sheds
				// the specific error type.
				if err.Error() == consul.ErrQueryNotFound.Error() {
					resp.WriteHeader(404)
					resp.Write([]byte(err.Error()))
					return nil, nil
				}
				return nil, err
			}

			// Use empty list instead of nil.
			if reply.Nodes == nil {
				reply.Nodes = make(structs.CheckServiceNodes, 0)
			}
			return reply, nil
		} else {
			args := structs.PreparedQuerySpecificRequest{
				QueryID: id,
			}
			if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
				return nil, nil
			}

			var reply structs.IndexedPreparedQueries
			if err := s.agent.RPC(endpoint+".Get", &args, &reply); err != nil {
				// We have to check the string since the RPC sheds
				// the specific error type.
				if err.Error() == consul.ErrQueryNotFound.Error() {
					resp.WriteHeader(404)
					resp.Write([]byte(err.Error()))
					return nil, nil
				}
				return nil, err
			}
			return reply.Queries, nil
		}

	case "PUT": // Update an existing prepared query.
		args := structs.PreparedQueryRequest{
			Op: structs.PreparedQueryUpdate,
		}
		s.parseDC(req, &args.Datacenter)
		s.parseToken(req, &args.Token)
		if req.ContentLength > 0 {
			if err := decodeBody(req, &args.Query, nil); err != nil {
				resp.WriteHeader(400)
				resp.Write([]byte(fmt.Sprintf("Request decode failed: %v", err)))
				return nil, nil
			}
		}

		// Take the ID from the URL, not the embedded one.
		args.Query.ID = id

		var reply string
		if err := s.agent.RPC(endpoint+".Apply", &args, &reply); err != nil {
			return nil, err
		}
		return nil, nil

	case "DELETE": // Delete a prepared query.
		args := structs.PreparedQueryRequest{
			Op: structs.PreparedQueryDelete,
			Query: &structs.PreparedQuery{
				ID: id,
			},
		}
		s.parseDC(req, &args.Datacenter)
		s.parseToken(req, &args.Token)

		var reply string
		if err := s.agent.RPC(endpoint+".Apply", &args, &reply); err != nil {
			return nil, err
		}
		return nil, nil

	default:
		resp.WriteHeader(405)
		return nil, nil
	}
}
<commit_msg>Breaks up huge HTTP endpoint functions.<commit_after>package agent

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/hashicorp/consul/consul"
	"github.com/hashicorp/consul/consul/structs"
)

const (
	preparedQueryEndpoint      = "PreparedQuery"
	preparedQueryExecuteSuffix = "/execute"
)

// preparedQueryCreateResponse is used to wrap the query ID.
type preparedQueryCreateResponse struct {
	ID string
}

// preparedQueryCreate makes a new prepared query.
func (s *HTTPServer) preparedQueryCreate(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	args := structs.PreparedQueryRequest{
		Op: structs.PreparedQueryCreate,
	}
	s.parseDC(req, &args.Datacenter)
	s.parseToken(req, &args.Token)
	if req.ContentLength > 0 {
		if err := decodeBody(req, &args.Query, nil); err != nil {
			resp.WriteHeader(400)
			resp.Write([]byte(fmt.Sprintf("Request decode failed: %v", err)))
			return nil, nil
		}
	}

	var reply string
	endpoint := s.agent.getEndpoint(preparedQueryEndpoint)
	if err := s.agent.RPC(endpoint+".Apply", &args, &reply); err != nil {
		return nil, err
	}
	return preparedQueryCreateResponse{reply}, nil
}

// preparedQueryList returns all the prepared queries.
func (s *HTTPServer) preparedQueryList(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	var args structs.DCSpecificRequest
	if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
		return nil, nil
	}

	var reply structs.IndexedPreparedQueries
	endpoint := s.agent.getEndpoint(preparedQueryEndpoint)
	if err := s.agent.RPC(endpoint+".List", &args, &reply); err != nil {
		return nil, err
	}

	// Use empty list instead of nil.
	if reply.Queries == nil {
		reply.Queries = make(structs.PreparedQueries, 0)
	}
	return reply.Queries, nil
}

// PreparedQueryGeneral handles all the general prepared query requests.
func (s *HTTPServer) PreparedQueryGeneral(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	switch req.Method {
	case "POST":
		return s.preparedQueryCreate(resp, req)

	case "GET":
		return s.preparedQueryList(resp, req)

	default:
		resp.WriteHeader(405)
		return nil, nil
	}
}

// parseLimit parses the optional limit parameter for a prepared query execution.
func parseLimit(req *http.Request, limit *int) error {
	*limit = 0
	if arg := req.URL.Query().Get("limit"); arg != "" {
		if i, err := strconv.Atoi(arg); err != nil {
			return err
		} else {
			*limit = i
		}
	}
	return nil
}

// preparedQueryExecute executes a prepared query.
func (s *HTTPServer) preparedQueryExecute(id string, resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	args := structs.PreparedQueryExecuteRequest{
		QueryIDOrName: id,
	}
	s.parseSource(req, &args.Source)
	if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
		return nil, nil
	}
	if err := parseLimit(req, &args.Limit); err != nil {
		return nil, fmt.Errorf("Bad limit: %s", err)
	}

	var reply structs.PreparedQueryExecuteResponse
	endpoint := s.agent.getEndpoint(preparedQueryEndpoint)
	if err := s.agent.RPC(endpoint+".Execute", &args, &reply); err != nil {
		// We have to check the string since the RPC sheds
		// the specific error type.
		if err.Error() == consul.ErrQueryNotFound.Error() {
			resp.WriteHeader(404)
			resp.Write([]byte(err.Error()))
			return nil, nil
		}
		return nil, err
	}

	// Use empty list instead of nil.
	if reply.Nodes == nil {
		reply.Nodes = make(structs.CheckServiceNodes, 0)
	}
	return reply, nil
}

// preparedQueryGet returns a single prepared query.
func (s *HTTPServer) preparedQueryGet(id string, resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	args := structs.PreparedQuerySpecificRequest{
		QueryID: id,
	}
	if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
		return nil, nil
	}

	var reply structs.IndexedPreparedQueries
	endpoint := s.agent.getEndpoint(preparedQueryEndpoint)
	if err := s.agent.RPC(endpoint+".Get", &args, &reply); err != nil {
		// We have to check the string since the RPC sheds
		// the specific error type.
		if err.Error() == consul.ErrQueryNotFound.Error() {
			resp.WriteHeader(404)
			resp.Write([]byte(err.Error()))
			return nil, nil
		}
		return nil, err
	}
	return reply.Queries, nil
}

// preparedQueryUpdate updates a prepared query.
func (s *HTTPServer) preparedQueryUpdate(id string, resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	args := structs.PreparedQueryRequest{
		Op: structs.PreparedQueryUpdate,
	}
	s.parseDC(req, &args.Datacenter)
	s.parseToken(req, &args.Token)
	if req.ContentLength > 0 {
		if err := decodeBody(req, &args.Query, nil); err != nil {
			resp.WriteHeader(400)
			resp.Write([]byte(fmt.Sprintf("Request decode failed: %v", err)))
			return nil, nil
		}
	}

	// Take the ID from the URL, not the embedded one.
	args.Query.ID = id

	var reply string
	endpoint := s.agent.getEndpoint(preparedQueryEndpoint)
	if err := s.agent.RPC(endpoint+".Apply", &args, &reply); err != nil {
		return nil, err
	}
	return nil, nil
}

// preparedQueryDelete deletes prepared query.
func (s *HTTPServer) preparedQueryDelete(id string, resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	args := structs.PreparedQueryRequest{
		Op: structs.PreparedQueryDelete,
		Query: &structs.PreparedQuery{
			ID: id,
		},
	}
	s.parseDC(req, &args.Datacenter)
	s.parseToken(req, &args.Token)

	var reply string
	endpoint := s.agent.getEndpoint(preparedQueryEndpoint)
	if err := s.agent.RPC(endpoint+".Apply", &args, &reply); err != nil {
		return nil, err
	}
	return nil, nil
}

// PreparedQuerySpecific handles all the prepared query requests specific to a
// particular query.
func (s *HTTPServer) PreparedQuerySpecific(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
	id := strings.TrimPrefix(req.URL.Path, "/v1/query/")
	execute := false
	if strings.HasSuffix(id, preparedQueryExecuteSuffix) {
		execute = true
		id = strings.TrimSuffix(id, preparedQueryExecuteSuffix)
	}

	switch req.Method {
	case "GET":
		if execute {
			return s.preparedQueryExecute(id, resp, req)
		} else {
			return s.preparedQueryGet(id, resp, req)
		}

	case "PUT":
		return s.preparedQueryUpdate(id, resp, req)

	case "DELETE":
		return s.preparedQueryDelete(id, resp, req)

	default:
		resp.WriteHeader(405)
		return nil, nil
	}
}
<|endoftext|>


<commit_before>// +build windows

package main

import (
	"fmt"

	_ "github.com/ying32/govcl/pkgs/winappres"
	"github.com/ying32/govcl/vcl/win"
)

func main() {

	// 利用互斥来演示exe单一运行，当然不止这一种方法了
	// GetLastError 无法获取错误，待研究原因所在。
	/*
		Mutex := win.CreateMutex(nil, true, "SingleRunTest")
		defer win.ReleaseMutex(Mutex)
		fmt.Println("Mutex:", Mutex)
		fmt.Println("win.GetLastError:", win.GetLastError(), syscall.GetLastError())
		if win.GetLastError() == win.ERROR_ALREADY_EXISTS {
			win.MessageBox(0, "我已经在运行中啦！", "运行提示", win.MB_OK+win.MB_ICONINFORMATION)
			os.Exit(1)
		}

		Mutex3 := win.CreateMutex(nil, true, "SingleRunTest")

		fmt.Println("win.GetLastError:", win.GetLastError())
		defer win.ReleaseMutex(Mutex3)
	*/

	// Exe运行在Administrator权限下检测
	fmt.Println("IsAdministrator:", win.IsAdministrator())

	// 注：只有当exe为Win32并在64位系统上运行时才返回true, 否则都会返回false
	fmt.Println("IsWow64:", win.IsWow64())

	// 在资源管理器中定位指定文件名
	// win.OpenInExplorer("F:\\Golang\\bin\\libvclx64.dll")
	var s string
	fmt.Scan(&s)

}
<commit_msg>Update WindowsTest example.<commit_after>// +build windows

package main

import (
	"fmt"
	"os"

	_ "github.com/ying32/govcl/pkgs/winappres"
	"github.com/ying32/govcl/vcl/win"
)

func main() {

	// 利用互斥来演示exe单一运行，当然不止这一种方法了
	// GetLastError 无法获取错误，待研究原因所在。
	/*
		Mutex := win.CreateMutex(nil, true, "SingleRunTest")
		defer win.ReleaseMutex(Mutex)
		fmt.Println("Mutex:", Mutex)
		fmt.Println("win.GetLastError:", win.GetLastError(), syscall.GetLastError())
		if win.GetLastError() == win.ERROR_ALREADY_EXISTS {
			win.MessageBox(0, "我已经在运行中啦！", "运行提示", win.MB_OK+win.MB_ICONINFORMATION)
			os.Exit(1)
		}

		Mutex3 := win.CreateMutex(nil, true, "SingleRunTest")

		fmt.Println("win.GetLastError:", win.GetLastError())
		defer win.ReleaseMutex(Mutex3)
	*/

	// Exe运行在Administrator权限下检测
	fmt.Println("IsAdministrator:", win.IsAdministrator())

	// 注：只有当exe为Win32并在64位系统上运行时才返回true, 否则都会返回false
	fmt.Println("IsWow64:", win.IsWow64())

	// 在资源管理器中定位指定文件名
	// win.OpenInExplorer("F:\\Golang\\bin\\libvclx64.dll")
	var s string
	fmt.Scan(&s)

	// 以管理员权限运行一个程序
	if cmdFileName, ok := os.LookupEnv("ComSpec"); ok {
		fmt.Println(win.RunAsAdministrator(cmdFileName, "/c ping 192.168.1.1", ""))
	}

}
<|endoftext|>


<commit_before>package servercommands

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

	"github.com/codegangsta/cli"
	"github.com/fatih/structs"
	"github.com/jrperritt/rackcli/auth"
	"github.com/jrperritt/rackcli/output"
	"github.com/jrperritt/rackcli/util"
	"github.com/olekukonko/tablewriter"
	osServers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
)

var create = cli.Command{
	Name:        "create",
	Usage:       fmt.Sprintf("%s %s create [flags]", util.Name, commandPrefix),
	Description: "Creates a new server",
	Action:      commandCreate,
	Flags:       flagsCreate(),
}

func flagsCreate() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			Name:  "imageRef",
			Usage: "[optional; required if imageName and bootFromVolume flags are not provided] The image ID from which to create the server.",
		},
		cli.StringFlag{
			Name:  "imageName",
			Usage: "[optional; required if imageRef and bootFromVolume flags are not provided] The name of the image from which to create the server.",
		},
		cli.StringFlag{
			Name:  "flavorRef",
			Usage: "[optional; required if flavorName is not provided] The flavor ID that the server should have.",
		},
		cli.StringFlag{
			Name:  "flavorName",
			Usage: "[optional; required if flavorRef is not provided] The name of the flavor that the server should have.",
		},
		cli.StringFlag{
			Name:  "securityGroups",
			Usage: "[optional] A comma-separated string of names of the security groups to which this server should belong.",
		},
		cli.StringFlag{
			Name:  "userData",
			Usage: "[optional] Configuration information or scripts to use after the server boots.",
		},
		cli.StringFlag{
			Name:  "networks",
			Usage: "[optional] A comma-separated string of IDs of the networks to attach to this server. If not provided, a public and private network will be attached.",
		},
		cli.StringFlag{
			Name:  "metadata",
			Usage: "[optional] A comma-separated string a key=value pairs.",
		},
		cli.StringFlag{
			Name:  "adminPass",
			Usage: "[optional] The root password for the server. If not provided, one will be randomly generated and returned in the output.",
		},
		cli.StringFlag{
			Name:  "keypair",
			Usage: "[optional] the name of the SSH KeyPair to be injected into this server.",
		},
	}
}

func commandCreate(c *cli.Context) {
	util.CheckArgNum(c, 1)
	serverName := c.Args()[0]
	opts := &servers.CreateOpts{
		Name:           serverName,
		ImageRef:       c.String("imageRef"),
		ImageName:      c.String("imageName"),
		FlavorRef:      c.String("flavorRef"),
		FlavorName:     c.String("flavorName"),
		SecurityGroups: strings.Split(c.String("securityGroups"), ","),
		AdminPass:      c.String("adminPass"),
		KeyPair:        c.String("keypair"),
	}

	if c.IsSet("userData") {
		s := c.String("userData")
		userData, err := ioutil.ReadFile(s)
		if err != nil {
			opts.UserData = userData
		} else {
			opts.UserData = []byte(s)
		}
	}

	if c.IsSet("networks") {
		netIDs := strings.Split(c.String("networks"), ",")
		networks := make([]osServers.Network, len(netIDs))
		for i, netID := range netIDs {
			networks[i] = osServers.Network{
				UUID: netID,
			}
		}
		opts.Networks = networks
	}

	if c.IsSet("metadata") {
		metadata := make(map[string]string)
		metaStrings := strings.Split(c.String("metadata"), ",")
		for _, metaString := range metaStrings {
			temp := strings.Split(metaString, "=")
			if len(temp) != 2 {
				fmt.Printf("Error parsing metadata: Expected key=value format but got %s\n", metaString)
				os.Exit(1)
			}
			metadata[temp[0]] = temp[1]
		}
		opts.Metadata = metadata
	}

	client := auth.NewClient("compute")
	o, err := servers.Create(client, opts).Extract()
	if err != nil {
		fmt.Printf("Error creating server: %s\n", err)
		os.Exit(1)
	}
	output.Print(c, o, tableCreate)
}

func tableCreate(c *cli.Context, i interface{}) {
	m := structs.Map(i)
	t := tablewriter.NewWriter(c.App.Writer)
	t.SetHeader([]string{"property", "value"})
	for k, v := range m {
		t.Append([]string{k, fmt.Sprint(v)})
	}
	t.Render()
}
<commit_msg>servers tableCreate<commit_after>package servercommands

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

	"github.com/codegangsta/cli"
	"github.com/fatih/structs"
	"github.com/jrperritt/rackcli/auth"
	"github.com/jrperritt/rackcli/output"
	"github.com/jrperritt/rackcli/util"
	"github.com/olekukonko/tablewriter"
	osServers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
)

var create = cli.Command{
	Name:        "create",
	Usage:       fmt.Sprintf("%s %s [global flags] create [command flags]", util.Name, commandPrefix),
	Description: "Creates a new server",
	Action:      commandCreate,
	Flags:       flagsCreate(),
}

func flagsCreate() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			Name:  "imageRef",
			Usage: "[optional; required if imageName and bootFromVolume flags are not provided] The image ID from which to create the server.",
		},
		cli.StringFlag{
			Name:  "imageName",
			Usage: "[optional; required if imageRef and bootFromVolume flags are not provided] The name of the image from which to create the server.",
		},
		cli.StringFlag{
			Name:  "flavorRef",
			Usage: "[optional; required if flavorName is not provided] The flavor ID that the server should have.",
		},
		cli.StringFlag{
			Name:  "flavorName",
			Usage: "[optional; required if flavorRef is not provided] The name of the flavor that the server should have.",
		},
		cli.StringFlag{
			Name:  "securityGroups",
			Usage: "[optional] A comma-separated string of names of the security groups to which this server should belong.",
		},
		cli.StringFlag{
			Name:  "userData",
			Usage: "[optional] Configuration information or scripts to use after the server boots.",
		},
		cli.StringFlag{
			Name:  "networks",
			Usage: "[optional] A comma-separated string of IDs of the networks to attach to this server. If not provided, a public and private network will be attached.",
		},
		cli.StringFlag{
			Name:  "metadata",
			Usage: "[optional] A comma-separated string a key=value pairs.",
		},
		cli.StringFlag{
			Name:  "adminPass",
			Usage: "[optional] The root password for the server. If not provided, one will be randomly generated and returned in the output.",
		},
		cli.StringFlag{
			Name:  "keypair",
			Usage: "[optional] the name of the SSH KeyPair to be injected into this server.",
		},
	}
}

func commandCreate(c *cli.Context) {
	util.CheckArgNum(c, 1)
	serverName := c.Args()[0]
	opts := &servers.CreateOpts{
		Name:           serverName,
		ImageRef:       c.String("imageRef"),
		ImageName:      c.String("imageName"),
		FlavorRef:      c.String("flavorRef"),
		FlavorName:     c.String("flavorName"),
		SecurityGroups: strings.Split(c.String("securityGroups"), ","),
		AdminPass:      c.String("adminPass"),
		KeyPair:        c.String("keypair"),
	}

	if c.IsSet("userData") {
		s := c.String("userData")
		userData, err := ioutil.ReadFile(s)
		if err != nil {
			opts.UserData = userData
		} else {
			opts.UserData = []byte(s)
		}
	}

	if c.IsSet("networks") {
		netIDs := strings.Split(c.String("networks"), ",")
		networks := make([]osServers.Network, len(netIDs))
		for i, netID := range netIDs {
			networks[i] = osServers.Network{
				UUID: netID,
			}
		}
		opts.Networks = networks
	}

	if c.IsSet("metadata") {
		metadata := make(map[string]string)
		metaStrings := strings.Split(c.String("metadata"), ",")
		for _, metaString := range metaStrings {
			temp := strings.Split(metaString, "=")
			if len(temp) != 2 {
				fmt.Printf("Error parsing metadata: Expected key=value format but got %s\n", metaString)
				os.Exit(1)
			}
			metadata[temp[0]] = temp[1]
		}
		opts.Metadata = metadata
	}

	client := auth.NewClient("compute")
	o, err := servers.Create(client, opts).Extract()
	if err != nil {
		fmt.Printf("Error creating server: %s\n", err)
		os.Exit(1)
	}
	output.Print(c, o, tableCreate)
}

func tableCreate(c *cli.Context, i interface{}) {
	m := structs.Map(i)
	t := tablewriter.NewWriter(c.App.Writer)
	t.SetAlignment(tablewriter.ALIGN_LEFT)
	t.SetHeader([]string{"property", "value"})
	keys := []string{"ID", "AdminPass"}
	for _, key := range keys {
		t.Append([]string{key, fmt.Sprint(m[key])})
	}
	t.Render()
}
<|endoftext|>


<commit_before>package tracking

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

const (
	host       = "http://api.mixpanel.com"
	trackPath  = "track"
	engagePath = "engage"
)

// engage constants
const (
	EngageSet = "set"
	EngageSetOnce = "set_once"
	EngageAdd = "add"
	EngageAppend = "append"
	EngageUnion = "union"
	EngageUnset = "unset"
	EngageDelete = "delete"
)

type client struct {
	token string
}

type eventData struct {
	Event string                 `json:"event"`
	Props map[string]interface{} `json:"properties"`
}

type engageData struct {
	Token   string      `json:"$token"`
	Time    int64       `json:"$time"`
	Id      int64       `json:"$distinct_id"`
	Ip      string      `json:"$ip,omitempty"`
	Set     interface{} `json:"$set,omitempty"`
	SetOnce interface{} `json:"$set_once,omitempty"`
	Add     interface{} `json:"$add,omitempty"`
	Append  interface{} `json:"$append,omitempty"`
	Union   interface{} `json:"$union,omitempty"`
	Unset   interface{} `json:"$unset,omitempty"`
	Delete  interface{} `json:"$delete,omitempty"`
}

func New(token string) *client {
	return &client{
		token: token,
	}
}

func (mp *client) Track(uid int64, e string, p map[string]interface{}, params ...map[string]interface{}) bool {
	data := &eventData{
		Event: e,
		Props: map[string]interface{}{
			"time":  time.Now().Unix(),
			"token": mp.token,
		},
	}
	if uid != 0 {
		data.Props["distinct_id"] = strconv.Itoa(int(uid))
	}
	for k, v := range p {
		data.Props[k] = v
	}

	marshaledData, err := json.Marshal(data)
	if err != nil {
		return false
	}

	u := fmt.Sprintf("%s/%s/?data=%s", host, trackPath,
		base64.StdEncoding.EncodeToString(marshaledData))

	parameters := url.Values{}
	// iterate over any query parameters
	for _, val := range params {
		for k, v := range val {
			if str, ok := v.(string); ok {
				/* act on str */
				parameters.Add(k, str)
			} else {
				/* not string - int? */
				if in, ok := v.(int); ok {
					parameters.Add(k, strconv.Itoa(in))
				} else {
					continue
				}
			}

		}
	}
	// append encoded params to url if any
	if qs := parameters.Encode(); qs != "" {
		u += "&" + qs
	}
	// send request
	resp, err := http.Get(u)
	if err != nil {
		return false
	}
	resp.Body.Close()
	return true
}

func (mp *client) Engage(uid int64, p map[string]interface{}, ip string) error {
	profileData := &engageData{
		Token: mp.token,
		Time:  time.Now().Unix(),
	}
	if uid != 0 {
		profileData.Id = uid
	}
	if ip != "" {
		profileData.Ip = ip
	}
	// should probably just add separate methods for each of these
	for k, v := range p {
		switch k {
		case EngageSet:
			profileData.Set = v
			break
		case EngageSetOnce:
			profileData.SetOnce = v
			break
		case EngageAdd:
			profileData.Add = v
			break
		case EngageAppend:
			profileData.Append = v
			break
		case EngageUnion:
			profileData.Union = v
			break
		case EngageUnset:
			profileData.Unset = v
			break
		case EngageDelete:
			profileData.Delete = v
			break
		}
	}

	marshalledData, err := json.Marshal(profileData)
	if err != nil {
		return err
	}

	url := fmt.Sprintf("%s/%s/?data=%s", host, engagePath, base64.StdEncoding.EncodeToString(marshalledData))

	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}
<commit_msg>return error<commit_after>package tracking

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

const (
	host       = "http://api.mixpanel.com"
	trackPath  = "track"
	engagePath = "engage"
)

// engage constants
const (
	EngageSet = "set"
	EngageSetOnce = "set_once"
	EngageAdd = "add"
	EngageAppend = "append"
	EngageUnion = "union"
	EngageUnset = "unset"
	EngageDelete = "delete"
)

type client struct {
	token string
}

type eventData struct {
	Event string                 `json:"event"`
	Props map[string]interface{} `json:"properties"`
}

type engageData struct {
	Token   string      `json:"$token"`
	Time    int64       `json:"$time"`
	Id      int64       `json:"$distinct_id"`
	Ip      string      `json:"$ip,omitempty"`
	Set     interface{} `json:"$set,omitempty"`
	SetOnce interface{} `json:"$set_once,omitempty"`
	Add     interface{} `json:"$add,omitempty"`
	Append  interface{} `json:"$append,omitempty"`
	Union   interface{} `json:"$union,omitempty"`
	Unset   interface{} `json:"$unset,omitempty"`
	Delete  interface{} `json:"$delete,omitempty"`
}

func New(token string) *client {
	return &client{
		token: token,
	}
}

func (mp *client) Track(uid int64, e string, p map[string]interface{}, params ...map[string]interface{}) error {
	data := &eventData{
		Event: e,
		Props: map[string]interface{}{
			"time":  time.Now().Unix(),
			"token": mp.token,
		},
	}
	if uid != 0 {
		data.Props["distinct_id"] = strconv.Itoa(int(uid))
	}
	for k, v := range p {
		data.Props[k] = v
	}

	marshaledData, err := json.Marshal(data)
	if err != nil {
		return err
	}

	u := fmt.Sprintf("%s/%s/?data=%s", host, trackPath,
		base64.StdEncoding.EncodeToString(marshaledData))

	parameters := url.Values{}
	// iterate over any query parameters
	for _, val := range params {
		for k, v := range val {
			if str, ok := v.(string); ok {
				/* act on str */
				parameters.Add(k, str)
			} else {
				/* not string - int? */
				if in, ok := v.(int); ok {
					parameters.Add(k, strconv.Itoa(in))
				} else {
					continue
				}
			}

		}
	}
	// append encoded params to url if any
	if qs := parameters.Encode(); qs != "" {
		u += "&" + qs
	}
	// send request
	resp, err := http.Get(u)
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}

func (mp *client) Engage(uid int64, p map[string]interface{}, ip string) error {
	profileData := &engageData{
		Token: mp.token,
		Time:  time.Now().Unix(),
	}
	if uid != 0 {
		profileData.Id = uid
	}
	if ip != "" {
		profileData.Ip = ip
	}
	// should probably just add separate methods for each of these
	for k, v := range p {
		switch k {
		case EngageSet:
			profileData.Set = v
			break
		case EngageSetOnce:
			profileData.SetOnce = v
			break
		case EngageAdd:
			profileData.Add = v
			break
		case EngageAppend:
			profileData.Append = v
			break
		case EngageUnion:
			profileData.Union = v
			break
		case EngageUnset:
			profileData.Unset = v
			break
		case EngageDelete:
			profileData.Delete = v
			break
		}
	}

	marshalledData, err := json.Marshal(profileData)
	if err != nil {
		return err
	}

	url := fmt.Sprintf("%s/%s/?data=%s", host, engagePath, base64.StdEncoding.EncodeToString(marshalledData))

	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}
<|endoftext|>


<commit_before>package manta

import (
	"strconv"
)

func decodeHandle(r *Reader, f *dt_field) interface{} {
	// So far these seem to occupy 32 bits but the value is made up only
	// out of what's present in the first 21 bits. In source 1, these only
	// occupied 21 bits of space.
	value := r.readBits(21) // a uint32
	r.seekBits(11)          // skip the rest of the 32 bits
	return value
}

func decodeByte(r *Reader, f *dt_field) interface{} {
	return r.readBits(8)
}

func decodeShort(r *Reader, f *dt_field) interface{} {
	return r.readBits(16)
}

func decodeUnsigned(r *Reader, f *dt_field) interface{} {
	return r.readVarUint64()
}

func decodeSigned(r *Reader, f *dt_field) interface{} {
	return r.readVarInt32()
}

func decodeBoolean(r *Reader, f *dt_field) interface{} {
	return r.readBoolean()
}

func decodeFloat(r *Reader, f *dt_field) interface{} {
	_debugf(
		"Bitcount: %v, Low: %v, High: %v, Flags: %v, Encoder: %v",
		saveReturnInt32(f.BitCount),
		saveReturnFloat32(f.LowValue, "nil"),
		saveReturnFloat32(f.HighValue, "nil"),
		strconv.FormatInt(int64(saveReturnInt32(f.Flags)), 2),
		f.Encoder,
	)

	// Parse specific encoders
	switch f.Encoder {
	case "coord":
		return r.readCoord()
	}

	var BitCount int
	var Low float32
	var High float32

	if f.BitCount != nil {
		BitCount = int(*f.BitCount)
	} else {
		// Maybe treated as no scale or something?
		return r.readVarUint32()
	}

	if f.LowValue != nil {
		Low = *f.LowValue
	} else {
		Low = 0.0
	}

	if f.HighValue != nil {
		High = *f.HighValue
	} else {
		High = 1.0
	}

	if f.Flags != nil {
		// Read raw float
		if *f.Flags&0x100 != 0 {
			return r.readBits(BitCount)
		}

		// read low value if empty
		if *f.Flags&0x10 != 0 && r.readBoolean() {
			return f.LowValue
		}

		// read high value if empty
		if *f.Flags&0x20 != 0 && r.readBoolean() {
			return f.HighValue
		}
	}

	dividend := r.readBits(BitCount)
	divisor := (1 << uint32(BitCount)) - 1
	return Low + (float32(dividend)/float32(divisor))*(High-Low)
}

func decodeString(r *Reader, f *dt_field) interface{} {
	return r.readString()
}

func decodeVector(r *Reader, f *dt_field) interface{} {
	size := r.readVarUint32()

	if size > 0 {
		_panicf("Ive been called")
	}

	return 0
}

func decodeClass(r *Reader, f *dt_field) interface{} {
	return r.readVarUint32()
}

func decodeQuantized(r *Reader, f *dt_field) interface{} {
	// Lets do this for now
	return decodeFloat(r, f)
}

func decodeFVector(r *Reader, f *dt_field) interface{} {
	// Parse specific encoders
	switch f.Encoder {
	case "normal":
		return r.read3BitNormal()
	}

	return []float32{decodeFloat(r, f).(float32), decodeFloat(r, f).(float32), decodeFloat(r, f).(float32)}
}

func decodeNop(r *Reader, f *dt_field) interface{} {
	return 0
}

func decodePointer(r *Reader, f *dt_field) interface{} {
	// Seems to be encoded as a single bit, not sure what to make of it
	if !r.readBoolean() {
		_panicf("Figure out how this works")
	}

	return 0
}

func decodeQAngle(r *Reader, f *dt_field) interface{} {
	if f.Flags != nil {
		// There is a flag check against 0x20 in the disasembly
		_debugf("Angle flags: %v", *f.Flags)
	}

	ret := [3]float32{0.0, 0.0, 0.0}

	rX := r.readBoolean()
	rY := r.readBoolean()
	rZ := r.readBoolean()

	if rX {
		ret[0] = r.readCoord()
	}

	if rY {
		ret[1] = r.readCoord()
	}

	if rZ {
		ret[2] = r.readCoord()
	}

	return ret
}

func decodeComponent(r *Reader, f *dt_field) interface{} {
	_debugf(
		"Bitcount: %v, Low: %v, High: %v, Flags: %v",
		saveReturnInt32(f.BitCount),
		saveReturnFloat32(f.LowValue, "nil"),
		saveReturnFloat32(f.HighValue, "nil"),
		strconv.FormatInt(int64(saveReturnInt32(f.Flags)), 2),
	)

	return r.readBits(1)
}

func decodeHSequence(r *Reader, f *dt_field) interface{} {
	// wrong, just testing
	return r.readBits(1)
}
<commit_msg>Added decodeSigned64<commit_after>package manta

import (
	"strconv"
)

func decodeHandle(r *Reader, f *dt_field) interface{} {
	// So far these seem to occupy 32 bits but the value is made up only
	// out of what's present in the first 21 bits. In source 1, these only
	// occupied 21 bits of space.
	value := r.readBits(21) // a uint32
	r.seekBits(11)          // skip the rest of the 32 bits
	return value
}

func decodeByte(r *Reader, f *dt_field) interface{} {
	return r.readBits(8)
}

func decodeShort(r *Reader, f *dt_field) interface{} {
	return r.readBits(16)
}

func decodeUnsigned(r *Reader, f *dt_field) interface{} {
	return r.readVarUint64()
}

func decodeSigned(r *Reader, f *dt_field) interface{} {
	return r.readVarInt32()
}

func decodeSigned64(r *Reader, f *dt_field) interface{} {
	return r.readVarInt64()
}

func decodeBoolean(r *Reader, f *dt_field) interface{} {
	return r.readBoolean()
}

func decodeFloat(r *Reader, f *dt_field) interface{} {
	_debugf(
		"Bitcount: %v, Low: %v, High: %v, Flags: %v, Encoder: %v",
		saveReturnInt32(f.BitCount),
		saveReturnFloat32(f.LowValue, "nil"),
		saveReturnFloat32(f.HighValue, "nil"),
		strconv.FormatInt(int64(saveReturnInt32(f.Flags)), 2),
		f.Encoder,
	)

	// Parse specific encoders
	switch f.Encoder {
	case "coord":
		return r.readCoord()
	}

	var BitCount int
	var Low float32
	var High float32

	if f.BitCount != nil {
		BitCount = int(*f.BitCount)
	} else {
		// Maybe treated as no scale or something?
		return r.readVarUint32()
	}

	if f.LowValue != nil {
		Low = *f.LowValue
	} else {
		Low = 0.0
	}

	if f.HighValue != nil {
		High = *f.HighValue
	} else {
		High = 1.0
	}

	if f.Flags != nil {
		// Read raw float
		if *f.Flags&0x100 != 0 {
			return r.readBits(BitCount)
		}

		// read low value if empty
		if *f.Flags&0x10 != 0 && r.readBoolean() {
			return f.LowValue
		}

		// read high value if empty
		if *f.Flags&0x20 != 0 && r.readBoolean() {
			return f.HighValue
		}
	}

	dividend := r.readBits(BitCount)
	divisor := (1 << uint32(BitCount)) - 1
	return Low + (float32(dividend)/float32(divisor))*(High-Low)
}

func decodeString(r *Reader, f *dt_field) interface{} {
	return r.readString()
}

func decodeVector(r *Reader, f *dt_field) interface{} {
	size := r.readVarUint32()

	if size > 0 {
		_panicf("Ive been called")
	}

	return 0
}

func decodeClass(r *Reader, f *dt_field) interface{} {
	return r.readVarUint32()
}

func decodeQuantized(r *Reader, f *dt_field) interface{} {
	// Lets do this for now
	return decodeFloat(r, f)
}

func decodeFVector(r *Reader, f *dt_field) interface{} {
	// Parse specific encoders
	switch f.Encoder {
	case "normal":
		return r.read3BitNormal()
	}

	return []float32{decodeFloat(r, f).(float32), decodeFloat(r, f).(float32), decodeFloat(r, f).(float32)}
}

func decodeNop(r *Reader, f *dt_field) interface{} {
	return 0
}

func decodePointer(r *Reader, f *dt_field) interface{} {
	// Seems to be encoded as a single bit, not sure what to make of it
	if !r.readBoolean() {
		_panicf("Figure out how this works")
	}

	return 0
}

func decodeQAngle(r *Reader, f *dt_field) interface{} {
	if f.Flags != nil {
		// There is a flag check against 0x20 in the disasembly
		_debugf("Angle flags: %v", *f.Flags)
	}

	ret := [3]float32{0.0, 0.0, 0.0}

	rX := r.readBoolean()
	rY := r.readBoolean()
	rZ := r.readBoolean()

	if rX {
		ret[0] = r.readCoord()
	}

	if rY {
		ret[1] = r.readCoord()
	}

	if rZ {
		ret[2] = r.readCoord()
	}

	return ret
}

func decodeComponent(r *Reader, f *dt_field) interface{} {
	_debugf(
		"Bitcount: %v, Low: %v, High: %v, Flags: %v",
		saveReturnInt32(f.BitCount),
		saveReturnFloat32(f.LowValue, "nil"),
		saveReturnFloat32(f.HighValue, "nil"),
		strconv.FormatInt(int64(saveReturnInt32(f.Flags)), 2),
	)

	return r.readBits(1)
}

func decodeHSequence(r *Reader, f *dt_field) interface{} {
	// wrong, just testing
	return r.readBits(1)
}
<|endoftext|>


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

package proto

import (
	"testing"
	"fmt"
	"bytes"
	"io"
	"crypto/rand"
	"crypto/sha256"
	"crypto/hmac"
)

type opBetweenWriteAndRead interface {
	Op()
}

func testSendingCommands(t *testing.T, op opBetweenWriteAndRead, from, to *commandIO, cmds ...*command) {
	errCh := make(chan error)
	startRead := make(chan bool)
	go func() {
		defer close(errCh)
		if op != nil {
			<-startRead
		}
		for i, cmd := range cmds {
			recved, err := to.ReadCommand()
			if err != nil {
				errCh <- err
				return
			}
			if !cmd.eq(recved) {
				errCh <- fmt.Errorf("%vth command does not equal", i)
			}
		}
	}()

	for _, cmd := range cmds {
		err := from.WriteCommand(cmd)
		if err != nil {
			t.Errorf("Error on write: %v", err)
		}
	}
	if op != nil {
		op.Op()
		startRead<-true
	}

	for err := range errCh {
		if err != nil {
			t.Errorf("Error on read: %v", err)
		}
	}
}

func getBufferCommandIOs(t *testing.T, compress, encrypt bool) (io1, io2 *commandIO, buffer *bytes.Buffer, ks *keySet) {
	keybuf := make([]byte, 2 * (authKeyLen + encrKeyLen))
	io.ReadFull(rand.Reader, keybuf)
	sen := keybuf[:encrKeyLen]
	keybuf = keybuf[encrKeyLen:]
	sau := keybuf[:authKeyLen]
	keybuf = keybuf[authKeyLen:]
	cen := keybuf[:encrKeyLen]
	keybuf = keybuf[encrKeyLen:]
	cau := keybuf[:authKeyLen]
	keybuf = keybuf[authKeyLen:]

	buffer = new(bytes.Buffer)
	ks = newKeySet(sen, sau, cen, cau)
	scmdio := ks.getServerCommandIO(buffer)
	scmdio = confCommandIO(scmdio, compress, encrypt)
	ccmdio := ks.getClientCommandIO(buffer)
	ccmdio = confCommandIO(ccmdio, compress, encrypt)
	io1 = scmdio
	io2 = ccmdio
	return
}

func confCommandIO(cmdio *commandIO, compress, encrypt bool) *commandIO {
	if !compress {
		cmdio.ReadCompressOff()
		cmdio.WriteCompressOff()
	}
	if !encrypt {
		cmdio.ReadEncryptOff()
		cmdio.WriteEncryptOff()
	}
	return cmdio
}

func getNetworkCommandIOs(t *testing.T, compress, encrypt bool) (io1, io2 *commandIO) {
	sks, cks, s2c, c2s := exchangeKeysOrReport(t)
	if sks == nil || cks == nil || s2c == nil || c2s == nil {
		return
	}

	scmdio := sks.getServerCommandIO(s2c)
	scmdio = confCommandIO(scmdio, compress, encrypt)
	ccmdio := cks.getClientCommandIO(c2s)
	ccmdio = confCommandIO(ccmdio, compress, encrypt)
	io1 = scmdio
	io2 = ccmdio
	return
}

func TestExchangingFullCommandNoCompressNoEncrypt(t *testing.T) {
	cmd := new(command)
	cmd.Body = []byte{1,2,3}
	cmd.Type = 1
	cmd.Params = make([][]byte, 2)
	cmd.Params[0] = []byte{1,2,3}
	cmd.Params[1] = []byte{2,2,3}
	cmd.Header = make(map[string]string, 2)
	cmd.Header["a"] = "hello"
	cmd.Header["b"] = "hell"
	io1, io2 := getNetworkCommandIOs(t, false, false)
	testSendingCommands(t, nil, io1, io2, cmd)
	testSendingCommands(t, nil, io2, io1, cmd)
}

func TestExchangingFullCommandNoEncrypt(t *testing.T) {
	cmd := new(command)
	cmd.Body = []byte{1,2,3}
	cmd.Type = 1
	cmd.Params = make([][]byte, 2)
	cmd.Params[0] = []byte{1,2,3}
	cmd.Params[1] = []byte{2,2,3}
	cmd.Header = make(map[string]string, 2)
	cmd.Header["a"] = "hello"
	cmd.Header["b"] = "hell"
	io1, io2 := getNetworkCommandIOs(t, true, false)
	testSendingCommands(t, nil, io1, io2, cmd)
	testSendingCommands(t, nil, io2, io1, cmd)
}

type bufPrinter struct {
	buf *bytes.Buffer
	authKey []byte
}

func (self *bufPrinter) Op() {
	fmt.Printf("--------------\n")
	fmt.Printf("Data in buffer: %v\n", self.buf.Bytes())

	data := self.buf.Bytes()
	data = data[16:]

	hash := hmac.New(sha256.New, self.authKey)
	hash.Write(data)

	fmt.Printf("HMAC: %v\n", hash.Sum(nil))
	fmt.Printf("--------------\n")
}

func TestExchangingFullCommandOverNetwork(t *testing.T) {
	cmd := new(command)
	cmd.Body = []byte{1,2,3}
	cmd.Type = 1
	cmd.Params = make([][]byte, 2)
	cmd.Params[0] = []byte{1,2,3}
	cmd.Params[1] = []byte{2,2,3}
	cmd.Header = make(map[string]string, 2)
	cmd.Header["a"] = "hello"
	cmd.Header["b"] = "hell"
	io1, io2 := getNetworkCommandIOs(t, true, true)
	testSendingCommands(t, nil, io1, io2, cmd)
	testSendingCommands(t, nil, io2, io1, cmd)
}

func TestExchangingFullCommandInBuffer(t *testing.T) {
	cmd := new(command)
	cmd.Body = []byte{1,2,3}
	cmd.Type = 1
	cmd.Params = make([][]byte, 2)
	cmd.Params[0] = []byte{1,2,3}
	cmd.Params[1] = []byte{2,2,3}
	cmd.Header = make(map[string]string, 2)
	cmd.Header["a"] = "hello"
	cmd.Header["b"] = "hell"
	io1, io2, buffer, ks := getBufferCommandIOs(t, true, true)
	op := &bufPrinter{buffer, ks.serverAuthKey}
	testSendingCommands(t, op, io1, io2, cmd)

	op = &bufPrinter{buffer, ks.clientAuthKey}
	testSendingCommands(t, op, io2, io1, cmd)
}

func randomCommand() *command {
	cmd := new(command)
	cmd.Body = make([]byte, 10)
	io.ReadFull(rand.Reader, cmd.Body)
	cmd.Params = make([][]byte, 2)
	cmd.Params[0] = []byte{1,2,3}
	cmd.Params[1] = []byte{2,2,3}
	cmd.Header = make(map[string]string, 2)
	cmd.Header["d"] = "hello"
	cmd.Header["c"] = "hell"
	return cmd
}

func TestExchangingMultiFullCommandOverNetwork(t *testing.T) {
	cmds := make([]*command, 10)
	for i, _ := range cmds {
		cmd := randomCommand()
		cmds[i] = cmd
	}
	io1, io2 := getNetworkCommandIOs(t, true, true)
	testSendingCommands(t, nil, io1, io2, cmds...)
	testSendingCommands(t, nil, io2, io1, cmds...)
}

func BenchmarkExchangingMultiFullCommandOverNetwork(b *testing.B) {
	b.StopTimer()
	cmds := make([]*command, b.N)
	for i, _ := range cmds {
		cmd := randomCommand()
		cmds[i] = cmd
	}
	io1, io2 := getNetworkCommandIOs(nil, true, true)
	done := make(chan bool)
	go func() {
		defer close(done)
		for i := 0; i < b.N; i++ {
			io2.ReadCommand()
		}
	}()

	b.StartTimer()
	for _, cmd := range cmds {
		io1.WriteCommand(cmd)
	}
	<-done
}
<commit_msg>Added benchmarks.<commit_after>/*
 * Copyright 2012 Nan Deng
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package proto

import (
	"testing"
	"fmt"
	"bytes"
	"io"
	"crypto/rand"
	"crypto/sha256"
	"crypto/hmac"
)

type opBetweenWriteAndRead interface {
	Op()
}

func testSendingCommands(t *testing.T, op opBetweenWriteAndRead, from, to *commandIO, cmds ...*command) {
	errCh := make(chan error)
	startRead := make(chan bool)
	go func() {
		defer close(errCh)
		if op != nil {
			<-startRead
		}
		for i, cmd := range cmds {
			recved, err := to.ReadCommand()
			if err != nil {
				errCh <- err
				return
			}
			if !cmd.eq(recved) {
				errCh <- fmt.Errorf("%vth command does not equal", i)
			}
		}
	}()

	for _, cmd := range cmds {
		err := from.WriteCommand(cmd)
		if err != nil {
			t.Errorf("Error on write: %v", err)
		}
	}
	if op != nil {
		op.Op()
		startRead<-true
	}

	for err := range errCh {
		if err != nil {
			t.Errorf("Error on read: %v", err)
		}
	}
}

func getBufferCommandIOs(t *testing.T, compress, encrypt bool) (io1, io2 *commandIO, buffer *bytes.Buffer, ks *keySet) {
	keybuf := make([]byte, 2 * (authKeyLen + encrKeyLen))
	io.ReadFull(rand.Reader, keybuf)
	sen := keybuf[:encrKeyLen]
	keybuf = keybuf[encrKeyLen:]
	sau := keybuf[:authKeyLen]
	keybuf = keybuf[authKeyLen:]
	cen := keybuf[:encrKeyLen]
	keybuf = keybuf[encrKeyLen:]
	cau := keybuf[:authKeyLen]
	keybuf = keybuf[authKeyLen:]

	buffer = new(bytes.Buffer)
	ks = newKeySet(sen, sau, cen, cau)
	scmdio := ks.getServerCommandIO(buffer)
	scmdio = confCommandIO(scmdio, compress, encrypt)
	ccmdio := ks.getClientCommandIO(buffer)
	ccmdio = confCommandIO(ccmdio, compress, encrypt)
	io1 = scmdio
	io2 = ccmdio
	return
}

func confCommandIO(cmdio *commandIO, compress, encrypt bool) *commandIO {
	if !compress {
		cmdio.ReadCompressOff()
		cmdio.WriteCompressOff()
	}
	if !encrypt {
		cmdio.ReadEncryptOff()
		cmdio.WriteEncryptOff()
	}
	return cmdio
}

func getNetworkCommandIOs(t *testing.T, compress, encrypt bool) (io1, io2 *commandIO) {
	sks, cks, s2c, c2s := exchangeKeysOrReport(t)
	if sks == nil || cks == nil || s2c == nil || c2s == nil {
		return
	}

	scmdio := sks.getServerCommandIO(s2c)
	scmdio = confCommandIO(scmdio, compress, encrypt)
	ccmdio := cks.getClientCommandIO(c2s)
	ccmdio = confCommandIO(ccmdio, compress, encrypt)
	io1 = scmdio
	io2 = ccmdio
	return
}

func TestExchangingFullCommandNoCompressNoEncrypt(t *testing.T) {
	cmd := new(command)
	cmd.Body = []byte{1,2,3}
	cmd.Type = 1
	cmd.Params = make([][]byte, 2)
	cmd.Params[0] = []byte{1,2,3}
	cmd.Params[1] = []byte{2,2,3}
	cmd.Header = make(map[string]string, 2)
	cmd.Header["a"] = "hello"
	cmd.Header["b"] = "hell"
	io1, io2 := getNetworkCommandIOs(t, false, false)
	testSendingCommands(t, nil, io1, io2, cmd)
	testSendingCommands(t, nil, io2, io1, cmd)
}

func TestExchangingFullCommandNoEncrypt(t *testing.T) {
	cmd := new(command)
	cmd.Body = []byte{1,2,3}
	cmd.Type = 1
	cmd.Params = make([][]byte, 2)
	cmd.Params[0] = []byte{1,2,3}
	cmd.Params[1] = []byte{2,2,3}
	cmd.Header = make(map[string]string, 2)
	cmd.Header["a"] = "hello"
	cmd.Header["b"] = "hell"
	io1, io2 := getNetworkCommandIOs(t, true, false)
	testSendingCommands(t, nil, io1, io2, cmd)
	testSendingCommands(t, nil, io2, io1, cmd)
}

type bufPrinter struct {
	buf *bytes.Buffer
	authKey []byte
}

func (self *bufPrinter) Op() {
	fmt.Printf("--------------\n")
	fmt.Printf("Data in buffer: %v\n", self.buf.Bytes())

	data := self.buf.Bytes()
	data = data[16:]

	hash := hmac.New(sha256.New, self.authKey)
	hash.Write(data)

	fmt.Printf("HMAC: %v\n", hash.Sum(nil))
	fmt.Printf("--------------\n")
}

func TestExchangingFullCommandOverNetwork(t *testing.T) {
	cmd := new(command)
	cmd.Body = []byte{1,2,3}
	cmd.Type = 1
	cmd.Params = make([][]byte, 2)
	cmd.Params[0] = []byte{1,2,3}
	cmd.Params[1] = []byte{2,2,3}
	cmd.Header = make(map[string]string, 2)
	cmd.Header["a"] = "hello"
	cmd.Header["b"] = "hell"
	io1, io2 := getNetworkCommandIOs(t, true, true)
	testSendingCommands(t, nil, io1, io2, cmd)
	testSendingCommands(t, nil, io2, io1, cmd)
}

func TestExchangingFullCommandInBuffer(t *testing.T) {
	cmd := new(command)
	cmd.Body = []byte{1,2,3}
	cmd.Type = 1
	cmd.Params = make([][]byte, 2)
	cmd.Params[0] = []byte{1,2,3}
	cmd.Params[1] = []byte{2,2,3}
	cmd.Header = make(map[string]string, 2)
	cmd.Header["a"] = "hello"
	cmd.Header["b"] = "hell"
	io1, io2, buffer, ks := getBufferCommandIOs(t, true, true)
	op := &bufPrinter{buffer, ks.serverAuthKey}
	testSendingCommands(t, op, io1, io2, cmd)

	op = &bufPrinter{buffer, ks.clientAuthKey}
	testSendingCommands(t, op, io2, io1, cmd)
}

func randomCommand() *command {
	cmd := new(command)
	cmd.Body = make([]byte, 10)
	io.ReadFull(rand.Reader, cmd.Body)
	cmd.Params = make([][]byte, 2)
	cmd.Params[0] = []byte{1,2,3}
	cmd.Params[1] = []byte{2,2,3}
	cmd.Header = make(map[string]string, 2)
	cmd.Header["d"] = "hello"
	cmd.Header["c"] = "hell"
	return cmd
}

func TestExchangingMultiFullCommandOverNetwork(t *testing.T) {
	cmds := make([]*command, 100)
	for i, _ := range cmds {
		cmd := randomCommand()
		cmds[i] = cmd
	}
	io1, io2 := getNetworkCommandIOs(t, true, true)
	testSendingCommands(t, nil, io1, io2, cmds...)
	testSendingCommands(t, nil, io2, io1, cmds...)
}

func BenchmarkExchangingMultiFullCommandOverNetwork(b *testing.B) {
	b.StopTimer()
	cmds := make([]*command, b.N)
	for i, _ := range cmds {
		cmd := randomCommand()
		cmds[i] = cmd
	}
	io1, io2 := getNetworkCommandIOs(nil, true, true)
	done := make(chan bool)
	go func() {
		defer close(done)
		for i := 0; i < b.N; i++ {
			io2.ReadCommand()
		}
	}()

	b.StartTimer()
	for _, cmd := range cmds {
		io1.WriteCommand(cmd)
	}
	<-done
}

func BenchmarkExchangingMultiFullCommandNoEncrypt(b *testing.B) {
	b.StopTimer()
	cmds := make([]*command, b.N)
	for i, _ := range cmds {
		cmd := randomCommand()
		cmds[i] = cmd
	}
	io1, io2 := getNetworkCommandIOs(nil, true, false)
	done := make(chan bool)
	go func() {
		defer close(done)
		for i := 0; i < b.N; i++ {
			io2.ReadCommand()
		}
	}()

	b.StartTimer()
	for _, cmd := range cmds {
		io1.WriteCommand(cmd)
	}
	<-done
}

func BenchmarkExchangingMultiFullCommandNoEncryptNoCompress(b *testing.B) {
	b.StopTimer()
	cmds := make([]*command, b.N)
	for i, _ := range cmds {
		cmd := randomCommand()
		cmds[i] = cmd
	}
	io1, io2 := getNetworkCommandIOs(nil, false, false)
	done := make(chan bool)
	go func() {
		defer close(done)
		for i := 0; i < b.N; i++ {
			io2.ReadCommand()
		}
	}()

	b.StartTimer()
	for _, cmd := range cmds {
		io1.WriteCommand(cmd)
	}
	<-done
}
<|endoftext|>


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

// Package release 发布版本号管理
package release

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"os/exec"
	"path/filepath"

	"github.com/issue9/cmdopt"
	"github.com/issue9/version"

	"github.com/issue9/web/internal/versioninfo"
)

var flagset *flag.FlagSet

// Init 初始化函数
func Init(opt *cmdopt.CmdOpt) {
	flagset = opt.New("release", do, usage)
}

func do(output io.Writer) error {
	ver := flagset.Arg(0)

	if ver == "" {
		return errors.New("必须指定一个版本号")
	}

	if !version.SemVerValid(ver) {
		_, err := fmt.Fprintf(output, "无效的版本号格式：%s", ver)
		return err
	}

	v, err := versioninfo.New("./")
	if err != nil {
		return err
	}

	if err := v.DumpFile(ver); err != nil {
		return err
	}

	// 添加到 git 缓存中
	cmd := exec.Command("git", "add", filepath.Join(v.Path(versioninfo.Path)))
	cmd.Stderr = output
	cmd.Stdout = output
	if err := cmd.Run(); err != nil {
		return err
	}

	cmd = exec.Command("git", "commit", "-m", "v"+ver)
	cmd.Stderr = output
	cmd.Stdout = output
	if err := cmd.Run(); err != nil {
		return err
	}

	// TODO 检测是否已经存在相同的 git tag

	// 输出 git 标签
	cmd = exec.Command("git", "tag", "v"+ver)
	cmd.Stderr = output
	cmd.Stdout = output

	return cmd.Run()
}

func usage(output io.Writer) error {
	_, err := fmt.Fprintf(output, `为当前程序发布一个新版本

该操作会在项目的根目录下添加 %s 文件，
并在其中写入版本信息。同时会通过 git tag 命令添加一条 tag 信息。
之后通过 web build 编译，会更新 %s 中的
buildDate 信息，但不会写入文件。

版本号的固定格式为 major.minjor.patch，比如 1.0.1，
git tag 标签中会自动加上 v 前缀，变成 v1.0.1。

一般用法：
git release 0.1.1 [commit message]
`, versioninfo.Path, versioninfo.Path)

	return err
}
<commit_msg>[cmd/web] web release 可以添加提交信息<commit_after>// Copyright 2019 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package release 发布版本号管理
package release

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/issue9/cmdopt"
	"github.com/issue9/version"

	"github.com/issue9/web/internal/versioninfo"
)

var flagset *flag.FlagSet

// Init 初始化函数
func Init(opt *cmdopt.CmdOpt) {
	flagset = opt.New("release", do, usage)
}

func do(output io.Writer) error {
	ver := flagset.Arg(0)

	if ver == "" {
		return errors.New("必须指定一个版本号")
	}

	if !version.SemVerValid(ver) {
		_, err := fmt.Fprintf(output, "无效的版本号格式：%s", ver)
		return err
	}

	v, err := versioninfo.New("./")
	if err != nil {
		return err
	}

	if err := v.DumpFile(ver); err != nil {
		return err
	}

	// 添加到 git 缓存中
	cmd := exec.Command("git", "add", filepath.Join(v.Path(versioninfo.Path)))
	cmd.Stderr = output
	cmd.Stdout = output
	if err := cmd.Run(); err != nil {
		return err
	}

	var message string
	if len(flagset.Args()) > 1 {
		message = strings.Join(flagset.Args()[:1], " ")
	}

	cmd = exec.Command("git", "commit", "-m", message)
	cmd.Stderr = output
	cmd.Stdout = output
	if err := cmd.Run(); err != nil {
		return err
	}

	// TODO 检测是否已经存在相同的 git tag

	// 输出 git 标签
	cmd = exec.Command("git", "tag", "v"+ver)
	cmd.Stderr = output
	cmd.Stdout = output

	return cmd.Run()
}

func usage(output io.Writer) error {
	_, err := fmt.Fprintf(output, `为当前程序发布一个新版本

该操作会在项目的根目录下添加 %s 文件，
并在其中写入版本信息。同时会通过 git tag 命令添加一条 tag 信息。
之后通过 web build 编译，会更新 %s 中的
buildDate 信息，但不会写入文件。

版本号的固定格式为 major.minjor.patch，比如 1.0.1，
git tag 标签中会自动加上 v 前缀，变成 v1.0.1。

一般用法：
git release 0.1.1 [commit message]
`, versioninfo.Path, versioninfo.Path)

	return err
}
<|endoftext|>


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

// Package release 发布版本号管理
package release

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"os/exec"

	"github.com/issue9/cmdopt"
	"github.com/issue9/version"

	"github.com/issue9/web/internal/versioninfo"
)

var flagset *flag.FlagSet

// Init 初始化函数
func Init(opt *cmdopt.CmdOpt) {
	flagset = opt.New("release", do, usage)
}

func do(output io.Writer) error {
	ver := flagset.Arg(0)

	if ver == "" {
		return errors.New("必须指定一个版本号")
	}

	if !version.SemVerValid(ver) {
		_, err := fmt.Fprintf(output, "无效的版本号格式：%s", ver)
		return err
	}

	if err := versioninfo.DumpFile("./", ver); err != nil {
		return err
	}

	// 输出 git 标签
	cmd := exec.Command("git", "tag", "v"+ver)
	cmd.Stderr = output
	cmd.Stdout = output

	return cmd.Run()
}

func usage(output io.Writer) error {
	_, err := fmt.Fprintf(output, `为当前程序发布一个新版本

该操作会在当前目录下添加 %s 文件，
并在其中写入版本信息。同时会通过 git tag 命令添加一条 tag 信息。
之后的 web build 会更新 %s 中的
buildDate 信息，但不会写入文件。

版本号的固定格式为 major.minjor.patch，比如 1.0.1，
git tag 标签中会自动加上 v 前缀，变成 v1.0.1。
`, versioninfo.Path, versioninfo.Path)

	return err
}
<commit_msg>[cmd/web] 更新文档<commit_after>// Copyright 2019 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package release 发布版本号管理
package release

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"os/exec"

	"github.com/issue9/cmdopt"
	"github.com/issue9/version"

	"github.com/issue9/web/internal/versioninfo"
)

var flagset *flag.FlagSet

// Init 初始化函数
func Init(opt *cmdopt.CmdOpt) {
	flagset = opt.New("release", do, usage)
}

func do(output io.Writer) error {
	ver := flagset.Arg(0)

	if ver == "" {
		return errors.New("必须指定一个版本号")
	}

	if !version.SemVerValid(ver) {
		_, err := fmt.Fprintf(output, "无效的版本号格式：%s", ver)
		return err
	}

	if err := versioninfo.DumpFile("./", ver); err != nil {
		return err
	}

	// 输出 git 标签
	cmd := exec.Command("git", "tag", "v"+ver)
	cmd.Stderr = output
	cmd.Stdout = output

	return cmd.Run()
}

func usage(output io.Writer) error {
	_, err := fmt.Fprintf(output, `为当前程序发布一个新版本

该操作会在项目的根目录下添加 %s 文件，
并在其中写入版本信息。同时会通过 git tag 命令添加一条 tag 信息。
之后通过 web build 编译，会更新 %s 中的
buildDate 信息，但不会写入文件。

版本号的固定格式为 major.minjor.patch，比如 1.0.1，
git tag 标签中会自动加上 v 前缀，变成 v1.0.1。
`, versioninfo.Path, versioninfo.Path)

	return err
}
<|endoftext|>


<commit_before>package fusefrontend

// Mkdir and Rmdir

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/hanwen/go-fuse/fuse"

	"github.com/rfjakob/gocryptfs/internal/configfile"
	"github.com/rfjakob/gocryptfs/internal/cryptocore"
	"github.com/rfjakob/gocryptfs/internal/nametransform"
	"github.com/rfjakob/gocryptfs/internal/toggledlog"
)

func (fs *FS) mkdirWithIv(cPath string, mode uint32) error {
	// Between the creation of the directory and the creation of gocryptfs.diriv
	// the directory is inconsistent. Take the lock to prevent other readers.
	fs.dirIVLock.Lock()
	// The new directory may take the place of an older one that is still in the cache
	fs.nameTransform.DirIVCache.Clear()
	defer fs.dirIVLock.Unlock()
	err := os.Mkdir(cPath, os.FileMode(mode))
	if err != nil {
		return err
	}
	// Create gocryptfs.diriv
	err = nametransform.WriteDirIV(cPath)
	if err != nil {
		err2 := syscall.Rmdir(cPath)
		if err2 != nil {
			toggledlog.Warn.Printf("mkdirWithIv: rollback failed: %v", err2)
		}
	}
	return err
}

func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fuse.Status) {
	if fs.isFiltered(newPath) {
		return fuse.EPERM
	}
	cPath, err := fs.getBackingPath(newPath)
	if err != nil {
		return fuse.ToStatus(err)
	}
	if !fs.args.DirIV {
		return fuse.ToStatus(os.Mkdir(cPath, os.FileMode(mode)))
	}
	// We need write and execute permissions to create gocryptfs.diriv
	origMode := mode
	mode = mode | 0300

	// Handle long file name
	cName := filepath.Base(cPath)
	if nametransform.IsLongContent(cName) {
		var dirfd *os.File
		dirfd, err = os.Open(filepath.Dir(cPath))
		if err != nil {
			return fuse.ToStatus(err)
		}
		defer dirfd.Close()

		// Create ".name"
		err = fs.nameTransform.WriteLongName(dirfd, cName, newPath)
		if err != nil {
			return fuse.ToStatus(err)
		}

		// Create directory
		err = fs.mkdirWithIv(cPath, mode)
		if err != nil {
			nametransform.DeleteLongName(dirfd, cName)
			return fuse.ToStatus(err)
		}
	} else {
		err = fs.mkdirWithIv(cPath, mode)
		if err != nil {
			return fuse.ToStatus(err)
		}
	}

	// Set permissions back to what the user wanted
	if origMode != mode {
		err = os.Chmod(cPath, os.FileMode(origMode))
		if err != nil {
			toggledlog.Warn.Printf("Mkdir: Chmod failed: %v", err)
		}
	}

	return fuse.OK
}

func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
	cPath, err := fs.getBackingPath(path)
	if err != nil {
		return fuse.ToStatus(err)
	}
	if !fs.args.DirIV {
		return fuse.ToStatus(syscall.Rmdir(cPath))
	}

	parentDir := filepath.Dir(cPath)
	parentDirFd, err := os.Open(parentDir)
	if err != nil {
		return fuse.ToStatus(err)
	}
	defer parentDirFd.Close()

	cName := filepath.Base(cPath)
	dirfdRaw, err := syscall.Openat(int(parentDirFd.Fd()), cName,
		syscall.O_RDONLY, 0)
	if err == syscall.EACCES {
		// We need permission to read and modify the directory
		toggledlog.Debug.Printf("Rmdir: handling EACCESS")
		// TODO use syscall.Fstatat once it is available in Go
		var fi os.FileInfo
		fi, err = os.Lstat(cPath)
		if err != nil {
			toggledlog.Debug.Printf("Rmdir: Stat: %v", err)
			return fuse.ToStatus(err)
		}
		origMode := fi.Mode()
		// TODO use syscall.Chmodat once it is available in Go
		err = os.Chmod(cPath, origMode|0700)
		if err != nil {
			toggledlog.Debug.Printf("Rmdir: Chmod failed: %v", err)
			return fuse.ToStatus(err)
		}
		// Retry open
		var st syscall.Stat_t
		syscall.Lstat(cPath, &st)
		dirfdRaw, err = syscall.Openat(int(parentDirFd.Fd()), cName,
			syscall.O_RDONLY, 0)
		// Undo the chmod if removing the directory failed
		defer func() {
			if code != fuse.OK {
				err := os.Chmod(cPath, origMode)
				if err != nil {
					toggledlog.Warn.Printf("Rmdir: Chmod rollback failed: %v", err)
				}
			}
		}()
	}
	if err != nil {
		toggledlog.Debug.Printf("Rmdir: Open: %v", err)
		return fuse.ToStatus(err)
	}
	dirfd := os.NewFile(uintptr(dirfdRaw), cName)
	defer dirfd.Close()

	children, err := dirfd.Readdirnames(10)
	if err != nil {
		toggledlog.Warn.Printf("Rmdir: Readdirnames: %v", err)
		return fuse.ToStatus(err)
	}
	// If the directory is not empty besides gocryptfs.diriv, do not even
	// attempt the dance around gocryptfs.diriv.
	if len(children) > 1 {
		return fuse.ToStatus(syscall.ENOTEMPTY)
	}

	// Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ"
	tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", cryptocore.RandUint64())
	toggledlog.Debug.Printf("Rmdir: Renaming %s to %s", nametransform.DirIVFilename, tmpName)
	// The directory is in an inconsistent state between rename and rmdir.
	// Protect against concurrent readers.
	fs.dirIVLock.Lock()
	defer fs.dirIVLock.Unlock()
	err = syscall.Renameat(int(dirfd.Fd()), nametransform.DirIVFilename,
		int(parentDirFd.Fd()), tmpName)
	if err != nil {
		toggledlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v",
			nametransform.DirIVFilename, tmpName, err)
		return fuse.ToStatus(err)
	}
	// Actual Rmdir
	// TODO Use syscall.Unlinkat with the AT_REMOVEDIR flag once it is available
	// in Go
	err = syscall.Rmdir(cPath)
	if err != nil {
		// This can happen if another file in the directory was created in the
		// meantime, undo the rename
		err2 := syscall.Renameat(int(parentDirFd.Fd()), tmpName,
			int(dirfd.Fd()), nametransform.DirIVFilename)
		if err != nil {
			toggledlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2)
		}
		return fuse.ToStatus(err)
	}
	// Delete "gocryptfs.diriv.rmdir.INODENUMBER"
	err = syscall.Unlinkat(int(parentDirFd.Fd()), tmpName)
	if err != nil {
		toggledlog.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err)
	}
	// Delete .name file
	if nametransform.IsLongContent(cName) {
		nametransform.DeleteLongName(parentDirFd, cName)
	}
	// The now-deleted directory may have been in the DirIV cache. Clear it.
	fs.nameTransform.DirIVCache.Clear()
	return fuse.OK
}

func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
	toggledlog.Debug.Printf("OpenDir(%s)", dirName)
	cDirName, err := fs.encryptPath(dirName)
	if err != nil {
		return nil, fuse.ToStatus(err)
	}
	// Read ciphertext directory
	cipherEntries, status := fs.FileSystem.OpenDir(cDirName, context)
	if cipherEntries == nil {
		return nil, status
	}
	// Get DirIV (stays nil if DirIV if off)
	var cachedIV []byte
	var cDirAbsPath string
	if fs.args.DirIV {
		// Read the DirIV once and use it for all later name decryptions
		cDirAbsPath = filepath.Join(fs.args.Cipherdir, cDirName)
		cachedIV, err = nametransform.ReadDirIV(cDirAbsPath)
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
	}
	// Filter and decrypt filenames
	var plain []fuse.DirEntry
	for i := range cipherEntries {
		cName := cipherEntries[i].Name
		if dirName == "" && cName == configfile.ConfDefaultName {
			// silently ignore "gocryptfs.conf" in the top level dir
			continue
		}
		if fs.args.DirIV && cName == nametransform.DirIVFilename {
			// silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled
			continue
		}

		if fs.args.PlaintextNames {
			plain = append(plain, cipherEntries[i])
			continue
		}

		if fs.args.LongNames {
			isLong := nametransform.NameType(cName)
			if isLong == nametransform.LongNameContent {
				cNameLong, err := nametransform.ReadLongName(filepath.Join(cDirAbsPath, cName))
				if err != nil {
					toggledlog.Warn.Printf("Could not read long name for file %s, skipping file", cName)
					continue
				}
				cName = cNameLong
			} else if isLong == nametransform.LongNameFilename {
				// ignore "gocryptfs.longname.*.name"
				continue
			}
		}
		name, err := fs.nameTransform.DecryptName(cName, cachedIV)
		if err != nil {
			toggledlog.Warn.Printf("Skipping invalid name '%s' in dir '%s': %s",
				cName, cDirName, err)
			continue
		}

		cipherEntries[i].Name = name
		plain = append(plain, cipherEntries[i])
	}
	return plain, status
}
<commit_msg>fusefrontend: report an error if all files in a directory were invalid<commit_after>package fusefrontend

// Mkdir and Rmdir

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/hanwen/go-fuse/fuse"

	"github.com/rfjakob/gocryptfs/internal/configfile"
	"github.com/rfjakob/gocryptfs/internal/cryptocore"
	"github.com/rfjakob/gocryptfs/internal/nametransform"
	"github.com/rfjakob/gocryptfs/internal/toggledlog"
)

func (fs *FS) mkdirWithIv(cPath string, mode uint32) error {
	// Between the creation of the directory and the creation of gocryptfs.diriv
	// the directory is inconsistent. Take the lock to prevent other readers.
	fs.dirIVLock.Lock()
	// The new directory may take the place of an older one that is still in the cache
	fs.nameTransform.DirIVCache.Clear()
	defer fs.dirIVLock.Unlock()
	err := os.Mkdir(cPath, os.FileMode(mode))
	if err != nil {
		return err
	}
	// Create gocryptfs.diriv
	err = nametransform.WriteDirIV(cPath)
	if err != nil {
		err2 := syscall.Rmdir(cPath)
		if err2 != nil {
			toggledlog.Warn.Printf("mkdirWithIv: rollback failed: %v", err2)
		}
	}
	return err
}

func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fuse.Status) {
	if fs.isFiltered(newPath) {
		return fuse.EPERM
	}
	cPath, err := fs.getBackingPath(newPath)
	if err != nil {
		return fuse.ToStatus(err)
	}
	if !fs.args.DirIV {
		return fuse.ToStatus(os.Mkdir(cPath, os.FileMode(mode)))
	}
	// We need write and execute permissions to create gocryptfs.diriv
	origMode := mode
	mode = mode | 0300

	// Handle long file name
	cName := filepath.Base(cPath)
	if nametransform.IsLongContent(cName) {
		var dirfd *os.File
		dirfd, err = os.Open(filepath.Dir(cPath))
		if err != nil {
			return fuse.ToStatus(err)
		}
		defer dirfd.Close()

		// Create ".name"
		err = fs.nameTransform.WriteLongName(dirfd, cName, newPath)
		if err != nil {
			return fuse.ToStatus(err)
		}

		// Create directory
		err = fs.mkdirWithIv(cPath, mode)
		if err != nil {
			nametransform.DeleteLongName(dirfd, cName)
			return fuse.ToStatus(err)
		}
	} else {
		err = fs.mkdirWithIv(cPath, mode)
		if err != nil {
			return fuse.ToStatus(err)
		}
	}

	// Set permissions back to what the user wanted
	if origMode != mode {
		err = os.Chmod(cPath, os.FileMode(origMode))
		if err != nil {
			toggledlog.Warn.Printf("Mkdir: Chmod failed: %v", err)
		}
	}

	return fuse.OK
}

func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
	cPath, err := fs.getBackingPath(path)
	if err != nil {
		return fuse.ToStatus(err)
	}
	if !fs.args.DirIV {
		return fuse.ToStatus(syscall.Rmdir(cPath))
	}

	parentDir := filepath.Dir(cPath)
	parentDirFd, err := os.Open(parentDir)
	if err != nil {
		return fuse.ToStatus(err)
	}
	defer parentDirFd.Close()

	cName := filepath.Base(cPath)
	dirfdRaw, err := syscall.Openat(int(parentDirFd.Fd()), cName,
		syscall.O_RDONLY, 0)
	if err == syscall.EACCES {
		// We need permission to read and modify the directory
		toggledlog.Debug.Printf("Rmdir: handling EACCESS")
		// TODO use syscall.Fstatat once it is available in Go
		var fi os.FileInfo
		fi, err = os.Lstat(cPath)
		if err != nil {
			toggledlog.Debug.Printf("Rmdir: Stat: %v", err)
			return fuse.ToStatus(err)
		}
		origMode := fi.Mode()
		// TODO use syscall.Chmodat once it is available in Go
		err = os.Chmod(cPath, origMode|0700)
		if err != nil {
			toggledlog.Debug.Printf("Rmdir: Chmod failed: %v", err)
			return fuse.ToStatus(err)
		}
		// Retry open
		var st syscall.Stat_t
		syscall.Lstat(cPath, &st)
		dirfdRaw, err = syscall.Openat(int(parentDirFd.Fd()), cName,
			syscall.O_RDONLY, 0)
		// Undo the chmod if removing the directory failed
		defer func() {
			if code != fuse.OK {
				err := os.Chmod(cPath, origMode)
				if err != nil {
					toggledlog.Warn.Printf("Rmdir: Chmod rollback failed: %v", err)
				}
			}
		}()
	}
	if err != nil {
		toggledlog.Debug.Printf("Rmdir: Open: %v", err)
		return fuse.ToStatus(err)
	}
	dirfd := os.NewFile(uintptr(dirfdRaw), cName)
	defer dirfd.Close()

	children, err := dirfd.Readdirnames(10)
	if err != nil {
		toggledlog.Warn.Printf("Rmdir: Readdirnames: %v", err)
		return fuse.ToStatus(err)
	}
	// If the directory is not empty besides gocryptfs.diriv, do not even
	// attempt the dance around gocryptfs.diriv.
	if len(children) > 1 {
		return fuse.ToStatus(syscall.ENOTEMPTY)
	}

	// Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ"
	tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", cryptocore.RandUint64())
	toggledlog.Debug.Printf("Rmdir: Renaming %s to %s", nametransform.DirIVFilename, tmpName)
	// The directory is in an inconsistent state between rename and rmdir.
	// Protect against concurrent readers.
	fs.dirIVLock.Lock()
	defer fs.dirIVLock.Unlock()
	err = syscall.Renameat(int(dirfd.Fd()), nametransform.DirIVFilename,
		int(parentDirFd.Fd()), tmpName)
	if err != nil {
		toggledlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v",
			nametransform.DirIVFilename, tmpName, err)
		return fuse.ToStatus(err)
	}
	// Actual Rmdir
	// TODO Use syscall.Unlinkat with the AT_REMOVEDIR flag once it is available
	// in Go
	err = syscall.Rmdir(cPath)
	if err != nil {
		// This can happen if another file in the directory was created in the
		// meantime, undo the rename
		err2 := syscall.Renameat(int(parentDirFd.Fd()), tmpName,
			int(dirfd.Fd()), nametransform.DirIVFilename)
		if err != nil {
			toggledlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2)
		}
		return fuse.ToStatus(err)
	}
	// Delete "gocryptfs.diriv.rmdir.INODENUMBER"
	err = syscall.Unlinkat(int(parentDirFd.Fd()), tmpName)
	if err != nil {
		toggledlog.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err)
	}
	// Delete .name file
	if nametransform.IsLongContent(cName) {
		nametransform.DeleteLongName(parentDirFd, cName)
	}
	// The now-deleted directory may have been in the DirIV cache. Clear it.
	fs.nameTransform.DirIVCache.Clear()
	return fuse.OK
}

func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
	toggledlog.Debug.Printf("OpenDir(%s)", dirName)
	cDirName, err := fs.encryptPath(dirName)
	if err != nil {
		return nil, fuse.ToStatus(err)
	}
	// Read ciphertext directory
	cipherEntries, status := fs.FileSystem.OpenDir(cDirName, context)
	if cipherEntries == nil {
		return nil, status
	}
	// Get DirIV (stays nil if DirIV if off)
	var cachedIV []byte
	var cDirAbsPath string
	if fs.args.DirIV {
		// Read the DirIV once and use it for all later name decryptions
		cDirAbsPath = filepath.Join(fs.args.Cipherdir, cDirName)
		cachedIV, err = nametransform.ReadDirIV(cDirAbsPath)
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
	}

	// Decrypted directory entries
	var plain []fuse.DirEntry
	var errorCount int
	// Filter and decrypt filenames
	for i := range cipherEntries {
		cName := cipherEntries[i].Name
		if dirName == "" && cName == configfile.ConfDefaultName {
			// silently ignore "gocryptfs.conf" in the top level dir
			continue
		}
		if fs.args.DirIV && cName == nametransform.DirIVFilename {
			// silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled
			continue
		}

		if fs.args.PlaintextNames {
			plain = append(plain, cipherEntries[i])
			continue
		}

		// Handle long file name
		isLong := nametransform.LongNameNone
		if fs.args.LongNames {
			isLong = nametransform.NameType(cName)
		}
		if isLong == nametransform.LongNameContent {
			cNameLong, err := nametransform.ReadLongName(filepath.Join(cDirAbsPath, cName))
			if err != nil {
				toggledlog.Warn.Printf("Skipping file %q in dir %q: Could not read .name: %v",
					cName, cDirName, err)
				errorCount++
				continue
			}
			cName = cNameLong
		} else if isLong == nametransform.LongNameFilename {
			// ignore "gocryptfs.longname.*.name"
			continue
		}

		name, err := fs.nameTransform.DecryptName(cName, cachedIV)
		if err != nil {
			toggledlog.Warn.Printf("Skipping invalid name %q in dir %q: %s",
				cName, cDirName, err)
			errorCount++
			continue
		}

		cipherEntries[i].Name = name
		plain = append(plain, cipherEntries[i])
	}

	if errorCount > 0 && len(plain) == 0 {
		// Don't let the user stare on an empty directory. Report that things went
		// wrong.
		toggledlog.Warn.Printf("All %d entries in directory %q were invalid, returning EIO",
			errorCount, cDirName)
		status = fuse.EIO
	}

	return plain, status
}
<|endoftext|>


<commit_before>// Package stupidgcm is a thin wrapper for OpenSSL's GCM encryption and
// decryption functions. It only support 32-byte keys and 16-bit IVs.
package stupidgcm

// #include <openssl/evp.h>
// #include <openssl/err.h>
// #cgo pkg-config: libcrypto
import "C"

import (
	"fmt"
	"log"
	"unsafe"
)

const (
	keyLen = 32
	ivLen  = 16
	tagLen = 16
)

func opensslPanic(msg string) {
	for {
		e := C.ERR_get_error()
		if e == 0 {
			break
		}
		log.Printf("openssl error: %d:%s:%s:%s", e,
			C.GoString(C.ERR_lib_error_string(e)),
			C.GoString(C.ERR_func_error_string(e)),
			C.GoString(C.ERR_reason_error_string(e)))
	}
	log.Printf("(no further openssl error messages)")
	log.Panic(msg)
}

// stupidGCM implements the cipher.AEAD interface
type stupidGCM struct {
	key []byte
}

func New(key []byte) stupidGCM {
	if len(key) != keyLen {
		log.Panicf("Only %d-byte keys are supported", keyLen)
	}
	return stupidGCM{key: key}
}

func (g stupidGCM) NonceSize() int {
	return ivLen
}

func (g stupidGCM) Overhead() int {
	return tagLen
}

// Seal - encrypt "in" using "iv" and "authData" and append the result to "dst"
func (g stupidGCM) Seal(dst, iv, in, authData []byte) []byte {
	if len(iv) != ivLen {
		log.Panicf("Only %d-byte IVs are supported", ivLen)
	}
	if len(in) == 0 {
		log.Panic("Zero-length input data is not supported")
	}
	buf := make([]byte, len(in)+tagLen)

	// https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode

	// Create scratch space "context"
	ctx := C.EVP_CIPHER_CTX_new()
	if ctx == nil {
		opensslPanic("EVP_CIPHER_CTX_new failed")
	}

	// Set cipher to AES-256
	if C.EVP_EncryptInit_ex(ctx, C.EVP_aes_256_gcm(), nil, nil, nil) != 1 {
		opensslPanic("EVP_EncryptInit_ex I failed")
	}

	// Use 16-byte IV
	if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_SET_IVLEN, ivLen, nil) != 1 {
		opensslPanic("EVP_CIPHER_CTX_ctrl EVP_CTRL_GCM_SET_IVLEN failed")
	}

	// Set key and IV
	if C.EVP_EncryptInit_ex(ctx, nil, nil, (*C.uchar)(&g.key[0]), (*C.uchar)(&iv[0])) != 1 {
		opensslPanic("EVP_EncryptInit_ex II failed")
	}

	// Provide authentication data
	var resultLen C.int
	if C.EVP_EncryptUpdate(ctx, nil, &resultLen, (*C.uchar)(&authData[0]), C.int(len(authData))) != 1 {
		opensslPanic("EVP_EncryptUpdate authData failed")
	}
	if int(resultLen) != len(authData) {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Encrypt "in" into "buf"
	if C.EVP_EncryptUpdate(ctx, (*C.uchar)(&buf[0]), &resultLen, (*C.uchar)(&in[0]), C.int(len(in))) != 1 {
		opensslPanic("EVP_EncryptUpdate failed")
	}
	if int(resultLen) != len(in) {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Finalise encryption
	// Because GCM is a stream encryption, this will not write out any data.
	dummy := make([]byte, 16)
	if C.EVP_EncryptFinal_ex(ctx, (*C.uchar)(&dummy[0]), &resultLen) != 1 {
		opensslPanic("EVP_EncryptFinal_ex failed")
	}
	if resultLen != 0 {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Get GMAC tag and append it to the ciphertext in "buf"
	if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_GET_TAG, tagLen, (unsafe.Pointer)(&buf[len(in)])) != 1 {
		opensslPanic("EVP_CIPHER_CTX_ctrl EVP_CTRL_GCM_GET_TAG failed")
	}

	// Free scratch space
	C.EVP_CIPHER_CTX_free(ctx)

	return append(dst, buf...)
}

// Open - decrypt "in" using "iv" and "authData" and append the result to "dst"
func (g stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {
	if len(iv) != ivLen {
		log.Panicf("Only %d-byte IVs are supported", ivLen)
	}
	if len(in) <= tagLen {
		log.Panic("Input data too short")
	}
	buf := make([]byte, len(in)-tagLen)
	ciphertext := in[:len(in)-tagLen]
	tag := in[len(in)-tagLen:]

	// https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode

	// Create scratch space "context"
	ctx := C.EVP_CIPHER_CTX_new()
	if ctx == nil {
		opensslPanic("EVP_CIPHER_CTX_new failed")
	}

	// Set cipher to AES-256
	if C.EVP_DecryptInit_ex(ctx, C.EVP_aes_256_gcm(), nil, nil, nil) != 1 {
		opensslPanic("EVP_DecryptInit_ex I failed")
	}

	// Use 16-byte IV
	if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_SET_IVLEN, ivLen, nil) != 1 {
		opensslPanic("EVP_CIPHER_CTX_ctrl EVP_CTRL_GCM_SET_IVLEN failed")
	}

	// Set key and IV
	if C.EVP_DecryptInit_ex(ctx, nil, nil, (*C.uchar)(&g.key[0]), (*C.uchar)(&iv[0])) != 1 {
		opensslPanic("EVP_DecryptInit_ex II failed")
	}

	// Set expected GMAC tag
	if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_SET_TAG, tagLen, (unsafe.Pointer)(&tag[0])) != 1 {
		opensslPanic("EVP_CIPHER_CTX_ctrl failed")
	}

	// Provide authentication data
	var resultLen C.int
	if C.EVP_DecryptUpdate(ctx, nil, &resultLen, (*C.uchar)(&authData[0]), C.int(len(authData))) != 1 {
		opensslPanic("EVP_DecryptUpdate authData failed")
	}
	if int(resultLen) != len(authData) {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Decrypt "ciphertext" into "buf"
	if C.EVP_DecryptUpdate(ctx, (*C.uchar)(&buf[0]), &resultLen, (*C.uchar)(&ciphertext[0]), C.int(len(ciphertext))) != 1 {
		opensslPanic("EVP_DecryptUpdate failed")
	}
	if int(resultLen) != len(ciphertext) {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Check GMAC
	dummy := make([]byte, 16)
	res := C.EVP_DecryptFinal_ex(ctx, (*C.uchar)(&dummy[0]), &resultLen)
	if resultLen != 0 {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Free scratch space
	C.EVP_CIPHER_CTX_free(ctx)

	if res != 1 {
		return nil, fmt.Errorf("stupidgcm: message authentication failed")
	}

	return append(dst, buf...), nil
}
<commit_msg>Revert "stupidgcm: print openssl error stack before panicing"<commit_after>// Package stupidgcm is a thin wrapper for OpenSSL's GCM encryption and
// decryption functions. It only support 32-byte keys and 16-bit IVs.
package stupidgcm

// #include <openssl/evp.h>
// #cgo pkg-config: libcrypto
import "C"

import (
	"fmt"
	"log"
	"unsafe"
)

const (
	keyLen = 32
	ivLen  = 16
	tagLen = 16
)

// stupidGCM implements the cipher.AEAD interface
type stupidGCM struct {
	key []byte
}

func New(key []byte) stupidGCM {
	if len(key) != keyLen {
		log.Panicf("Only %d-byte keys are supported", keyLen)
	}
	return stupidGCM{key: key}
}

func (g stupidGCM) NonceSize() int {
	return ivLen
}

func (g stupidGCM) Overhead() int {
	return tagLen
}

// Seal - encrypt "in" using "iv" and "authData" and append the result to "dst"
func (g stupidGCM) Seal(dst, iv, in, authData []byte) []byte {
	if len(iv) != ivLen {
		log.Panicf("Only %d-byte IVs are supported", ivLen)
	}
	if len(in) == 0 {
		log.Panic("Zero-length input data is not supported")
	}
	buf := make([]byte, len(in)+tagLen)

	// https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode

	// Create scratch space "context"
	ctx := C.EVP_CIPHER_CTX_new()
	if ctx == nil {
		panic("EVP_CIPHER_CTX_new failed")
	}

	// Set cipher to AES-256
	if C.EVP_EncryptInit_ex(ctx, C.EVP_aes_256_gcm(), nil, nil, nil) != 1 {
		panic("EVP_EncryptInit_ex I failed")
	}

	// Use 16-byte IV
	if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_SET_IVLEN, ivLen, nil) != 1 {
		panic("EVP_CIPHER_CTX_ctrl EVP_CTRL_GCM_SET_IVLEN failed")
	}

	// Set key and IV
	if C.EVP_EncryptInit_ex(ctx, nil, nil, (*C.uchar)(&g.key[0]), (*C.uchar)(&iv[0])) != 1 {
		panic("EVP_EncryptInit_ex II failed")
	}

	// Provide authentication data
	var resultLen C.int
	if C.EVP_EncryptUpdate(ctx, nil, &resultLen, (*C.uchar)(&authData[0]), C.int(len(authData))) != 1 {
		panic("EVP_EncryptUpdate authData failed")
	}
	if int(resultLen) != len(authData) {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Encrypt "in" into "buf"
	if C.EVP_EncryptUpdate(ctx, (*C.uchar)(&buf[0]), &resultLen, (*C.uchar)(&in[0]), C.int(len(in))) != 1 {
		panic("EVP_EncryptUpdate failed")
	}
	if int(resultLen) != len(in) {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Finalise encryption
	// Because GCM is a stream encryption, this will not write out any data.
	dummy := make([]byte, 16)
	if C.EVP_EncryptFinal_ex(ctx, (*C.uchar)(&dummy[0]), &resultLen) != 1 {
		panic("EVP_EncryptFinal_ex failed")
	}
	if resultLen != 0 {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Get GMAC tag and append it to the ciphertext in "buf"
	if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_GET_TAG, tagLen, (unsafe.Pointer)(&buf[len(in)])) != 1 {
		panic("EVP_CIPHER_CTX_ctrl EVP_CTRL_GCM_GET_TAG failed")
	}

	// Free scratch space
	C.EVP_CIPHER_CTX_free(ctx)

	return append(dst, buf...)
}

// Open - decrypt "in" using "iv" and "authData" and append the result to "dst"
func (g stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {
	if len(iv) != ivLen {
		log.Panicf("Only %d-byte IVs are supported", ivLen)
	}
	if len(in) <= tagLen {
		log.Panic("Input data too short")
	}
	buf := make([]byte, len(in)-tagLen)
	ciphertext := in[:len(in)-tagLen]
	tag := in[len(in)-tagLen:]

	// https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode

	// Create scratch space "context"
	ctx := C.EVP_CIPHER_CTX_new()
	if ctx == nil {
		panic("EVP_CIPHER_CTX_new failed")
	}

	// Set cipher to AES-256
	if C.EVP_DecryptInit_ex(ctx, C.EVP_aes_256_gcm(), nil, nil, nil) != 1 {
		panic("EVP_DecryptInit_ex I failed")
	}

	// Use 16-byte IV
	if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_SET_IVLEN, ivLen, nil) != 1 {
		panic("EVP_CIPHER_CTX_ctrl EVP_CTRL_GCM_SET_IVLEN failed")
	}

	// Set key and IV
	if C.EVP_DecryptInit_ex(ctx, nil, nil, (*C.uchar)(&g.key[0]), (*C.uchar)(&iv[0])) != 1 {
		panic("EVP_DecryptInit_ex II failed")
	}

	// Set expected GMAC tag
	if C.EVP_CIPHER_CTX_ctrl(ctx, C.EVP_CTRL_GCM_SET_TAG, tagLen, (unsafe.Pointer)(&tag[0])) != 1 {
		panic("EVP_CIPHER_CTX_ctrl failed")
	}

	// Provide authentication data
	var resultLen C.int
	if C.EVP_DecryptUpdate(ctx, nil, &resultLen, (*C.uchar)(&authData[0]), C.int(len(authData))) != 1 {
		panic("EVP_DecryptUpdate authData failed")
	}
	if int(resultLen) != len(authData) {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Decrypt "ciphertext" into "buf"
	if C.EVP_DecryptUpdate(ctx, (*C.uchar)(&buf[0]), &resultLen, (*C.uchar)(&ciphertext[0]), C.int(len(ciphertext))) != 1 {
		panic("EVP_DecryptUpdate failed")
	}
	if int(resultLen) != len(ciphertext) {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Check GMAC
	dummy := make([]byte, 16)
	res := C.EVP_DecryptFinal_ex(ctx, (*C.uchar)(&dummy[0]), &resultLen)
	if resultLen != 0 {
		log.Panicf("Unexpected length %d", resultLen)
	}

	// Free scratch space
	C.EVP_CIPHER_CTX_free(ctx)

	if res != 1 {
		return nil, fmt.Errorf("stupidgcm: message authentication failed")
	}

	return append(dst, buf...), nil
}
<|endoftext|>


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

package account

import (
	"github.com/realglobe-Inc/go-lib/erro"
	"gopkg.in/mgo.v2/bson"
)

// アカウント情報の実装。
type element struct {
	id    string
	name  string
	auth  Authenticator
	attrs map[string]interface{}
}

// 主にテスト用。
func New(id, name string, auth Authenticator, attrs map[string]interface{}) Element {
	return newElement(id, name, auth, attrs)
}

func newElement(id, name string, auth Authenticator, attrs map[string]interface{}) *element {
	return &element{id, name, auth, attrs}
}

func (this *element) Id() string {
	return this.id
}

func (this *element) Name() string {
	return this.name
}

func (this *element) Authenticator() Authenticator {
	return this.auth
}

func (this *element) Attribute(attrName string) interface{} {
	return this.attrs[attrName]
}

func (this *element) SetAttribute(attrName string, attr interface{}) {
	this.attrs[attrName] = attr
}

//  {
//      "id": <ID>,
//      "username": <ログイン名>,
//      "authenticator": <認証器>,
//      <属性名>: <属性値>,
//      ...
//  }
func (this *element) SetBSON(raw bson.Raw) error {
	var buff struct {
		Id    string                 `bson:"id"`
		Name  string                 `bson:"username"`
		Auth  map[string]interface{} `bson:"authenticator"`
		Attrs map[string]interface{} `bson:",inline"`
	}
	if err := raw.Unmarshal(&buff); err != nil {
		return erro.Wrap(err)
	}

	auth, err := authenticatorFromMap(buff.Auth)
	if err != nil {
		return erro.Wrap(err)
	}

	this.id = buff.Id
	this.name = buff.Name
	this.auth = auth
	this.attrs = buff.Attrs
	return nil
}
<commit_msg>ヌルポ潰し<commit_after>// Copyright 2015 realglobe, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package account

import (
	"github.com/realglobe-Inc/go-lib/erro"
	"gopkg.in/mgo.v2/bson"
)

// アカウント情報の実装。
type element struct {
	id    string
	name  string
	auth  Authenticator
	attrs map[string]interface{}
}

// 主にテスト用。
func New(id, name string, auth Authenticator, attrs map[string]interface{}) Element {
	return newElement(id, name, auth, attrs)
}

func newElement(id, name string, auth Authenticator, attrs map[string]interface{}) *element {
	return &element{id, name, auth, attrs}
}

func (this *element) Id() string {
	return this.id
}

func (this *element) Name() string {
	return this.name
}

func (this *element) Authenticator() Authenticator {
	return this.auth
}

func (this *element) Attribute(attrName string) interface{} {
	if this.attrs == nil {
		return nil
	}
	return this.attrs[attrName]
}

func (this *element) SetAttribute(attrName string, attr interface{}) {
	if this.attrs == nil {
		this.attrs = map[string]interface{}{}
	}
	this.attrs[attrName] = attr
}

//  {
//      "id": <ID>,
//      "username": <ログイン名>,
//      "authenticator": <認証器>,
//      <属性名>: <属性値>,
//      ...
//  }
func (this *element) SetBSON(raw bson.Raw) error {
	var buff struct {
		Id    string                 `bson:"id"`
		Name  string                 `bson:"username"`
		Auth  map[string]interface{} `bson:"authenticator"`
		Attrs map[string]interface{} `bson:",inline"`
	}
	if err := raw.Unmarshal(&buff); err != nil {
		return erro.Wrap(err)
	}

	auth, err := authenticatorFromMap(buff.Auth)
	if err != nil {
		return erro.Wrap(err)
	}

	this.id = buff.Id
	this.name = buff.Name
	this.auth = auth
	this.attrs = buff.Attrs
	return nil
}
<|endoftext|>