<commit_before>package main

import (
	"github.com/lnxjedi/gopherbot/bot"

	// NOTE: If compiling gopherbot yourself, you can comment out or remove
	// most of the import lines below to shrink the binary or remove unwanted
	// or unneeded funcationality. You'll need at least one connector for your
	// bot to be useful, however.

	// *** Included connectors

	_ "github.com/lnxjedi/gopherbot/connectors/slack"
	// NOTE: if you build with '-tags test', the terminal connector will also
	// show emitted events.
	_ "github.com/lnxjedi/gopherbot/connectors/terminal"

	// *** Included brain implementations

	_ "github.com/lnxjedi/gopherbot/brains/dynamodb"
	_ "github.com/lnxjedi/gopherbot/brains/file"
	_ "github.com/lnxjedi/gopherbot/brains/mem"

	// Many included plugins already have 'Disabled: true', but you can also
	// disable by adding that line to conf/plugins/<plugname>.yaml

	// *** Included Elevator plugins

	_ "github.com/lnxjedi/gopherbot/goplugins/duo"
	_ "github.com/lnxjedi/gopherbot/goplugins/totp"

	// *** Included Authorizer plugins

	_ "github.com/lnxjedi/gopherbot/goplugins/groups"

	// *** Included Go plugins, of varying quality

	_ "github.com/lnxjedi/gopherbot/goplugins/help"
	_ "github.com/lnxjedi/gopherbot/goplugins/knock"
	_ "github.com/lnxjedi/gopherbot/goplugins/links"
	_ "github.com/lnxjedi/gopherbot/goplugins/lists"
	_ "github.com/lnxjedi/gopherbot/goplugins/meme"
	_ "github.com/lnxjedi/gopherbot/goplugins/ping"

	/* Enable profiling. You can shrink the binary by removing this, but if the
	   robot ever stops responding for any reason, it's handy for getting a
	   dump of all goroutines. Example usage:

	   $ go tool pprof http://localhost:8888/debug/pprof/goroutine
	   ...
	   Entering interactive mode (type "help" for commands, "o" for options)
	   (pprof) list lnxjedi
	   Total: 11
	   ROUTINE ======================== github.com/lnxjedi/gopherbot/bot...
	*/
	_ "net/http/pprof"
)

var versionInfo = bot.VersionInfo{
	Version: "v1.1.2",
	Commit:  "(manual build)",
}

func main() {
	bot.Start(versionInfo)
}
<commit_msg>Fix version post merge<commit_after>package main

import (
	"github.com/lnxjedi/gopherbot/bot"

	// NOTE: If compiling gopherbot yourself, you can comment out or remove
	// most of the import lines below to shrink the binary or remove unwanted
	// or unneeded funcationality. You'll need at least one connector for your
	// bot to be useful, however.

	// *** Included connectors

	_ "github.com/lnxjedi/gopherbot/connectors/slack"
	// NOTE: if you build with '-tags test', the terminal connector will also
	// show emitted events.
	_ "github.com/lnxjedi/gopherbot/connectors/terminal"

	// *** Included brain implementations

	_ "github.com/lnxjedi/gopherbot/brains/dynamodb"
	_ "github.com/lnxjedi/gopherbot/brains/file"
	_ "github.com/lnxjedi/gopherbot/brains/mem"

	// Many included plugins already have 'Disabled: true', but you can also
	// disable by adding that line to conf/plugins/<plugname>.yaml

	// *** Included Elevator plugins

	_ "github.com/lnxjedi/gopherbot/goplugins/duo"
	_ "github.com/lnxjedi/gopherbot/goplugins/totp"

	// *** Included Authorizer plugins

	_ "github.com/lnxjedi/gopherbot/goplugins/groups"

	// *** Included Go plugins, of varying quality

	_ "github.com/lnxjedi/gopherbot/goplugins/help"
	_ "github.com/lnxjedi/gopherbot/goplugins/knock"
	_ "github.com/lnxjedi/gopherbot/goplugins/links"
	_ "github.com/lnxjedi/gopherbot/goplugins/lists"
	_ "github.com/lnxjedi/gopherbot/goplugins/meme"
	_ "github.com/lnxjedi/gopherbot/goplugins/ping"

	/* Enable profiling. You can shrink the binary by removing this, but if the
	   robot ever stops responding for any reason, it's handy for getting a
	   dump of all goroutines. Example usage:

	   $ go tool pprof http://localhost:8888/debug/pprof/goroutine
	   ...
	   Entering interactive mode (type "help" for commands, "o" for options)
	   (pprof) list lnxjedi
	   Total: 11
	   ROUTINE ======================== github.com/lnxjedi/gopherbot/bot...
	*/
	_ "net/http/pprof"
)

var versionInfo = bot.VersionInfo{
	Version: "v1.2.0-snapshot",
	Commit:  "(manual build)",
}

func main() {
	bot.Start(versionInfo)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/julienschmidt/httprouter"
	"net/http"
	"log"
	"os"
	"io"
	"fmt"
)

func Static(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	http.ServeFile(w,r,"./static/"+ps.ByName("file"))
}

func main() {
	conn,_ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
	defer conn.Close()
	router := httprouter.New()
	router.GET("/*file", Static)
	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}
	fmt.Printf("Starting server at localhost:%s...", port)
	log.Fatal(http.ListenAndServe(":"+port, router))
}<commit_msg>initial upload<commit_after>package main

import (
	"github.com/julienschmidt/httprouter"
	"net/http"
	"log"
	"os"
	"io"
	"fmt"
)

func Static(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	http.ServeFile(w,r,"./static/"+ps.ByName("file"))
}

func main() {
	router := httprouter.New()
	router.GET("/*file", Static)
	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}
	fmt.Printf("Starting server at localhost:%s...", port)
	log.Fatal(http.ListenAndServe(":"+port, router))
}<|endoftext|>


<commit_before>/* Copyright 2020 Google 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
    https://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 os

import (
	"context"
	"crypto/rand"
	"errors"
	"testing"

	"github.com/golang/protobuf/proto"
	"github.com/google/gnxi/gnoi/os/pb"
	"github.com/google/gnxi/utils/mockos"
	mockosPb "github.com/google/gnxi/utils/mockos/pb"
	"github.com/kylelemons/godebug/pretty"
)

type installResult struct {
	res *pb.InstallResponse
	err error
}

type mockTransferStream struct {
	pb.OS_InstallServer
	response chan *pb.InstallResponse
	errorReq *pb.InstallRequest
	result   chan *pb.InstallResponse
	os       *mockos.OS
}

func (m mockTransferStream) Send(res *pb.InstallResponse) error {
	switch res.Response.(type) {
	case *pb.InstallResponse_Validated:
		m.result <- res
	case *pb.InstallResponse_InstallError:
		m.result <- res
	default:
		m.response <- res
	}
	return nil
}

func (m mockTransferStream) Recv() (*pb.InstallRequest, error) {
	if request := m.errorReq; request != nil {
		return request, nil
	}
	select {
	case res := <-m.response:
		switch res.Response.(type) {
		case *pb.InstallResponse_TransferProgress:
			return &pb.InstallRequest{Request: &pb.InstallRequest_TransferEnd{}}, nil
		case *pb.InstallResponse_TransferReady:
			var out []byte
			if m.os.MockOS.Padding != nil {
				out, _ = proto.Marshal(&m.os.MockOS)
			} else {
				out = make([]byte, 10000000)
				rand.Read(out)
			}
			return &pb.InstallRequest{Request: &pb.InstallRequest_TransferContent{TransferContent: out}}, nil
		}
	default:
		return &pb.InstallRequest{Request: &pb.InstallRequest_TransferRequest{TransferRequest: &pb.TransferRequest{Version: m.os.MockOS.Version}}}, nil
	}
	return nil, nil
}

func TestTargetActivate(t *testing.T) {
	settings := &Settings{
		FactoryVersion:    "1",
		InstalledVersions: []string{"1.0.0a", "2.0.1c"},
	}
	server := NewServer(settings)
	tests := []struct {
		request *pb.ActivateRequest
		want    *pb.ActivateResponse
	}{
		{
			request: &pb.ActivateRequest{
				Version: "1.0.0a",
			},
			want: &pb.ActivateResponse{Response: &pb.ActivateResponse_ActivateOk{}},
		},
		{
			request: &pb.ActivateRequest{
				Version: "99.0a",
			},
			want: &pb.ActivateResponse{Response: &pb.ActivateResponse_ActivateError{
				ActivateError: &pb.ActivateError{Type: pb.ActivateError_NON_EXISTENT_VERSION},
			}},
		},
	}
	for _, test := range tests {
		got, _ := server.Activate(context.Background(), test.request)
		if diff := pretty.Compare(test.want.Response, got.Response); diff != "" {
			t.Errorf("Activate(context.Background(), %s): (-want +got):\n%s", test.request, diff)
		}
	}
}

func TestTargetVerify(t *testing.T) {
	tests := []struct {
		settings *Settings
		want     *pb.VerifyResponse
	}{
		{
			settings: &Settings{
				FactoryVersion: "1",
			},
			want: &pb.VerifyResponse{
				Version: "1",
			},
		},
	}
	for _, test := range tests {
		server := NewServer(test.settings)
		got, _ := server.Verify(context.Background(), &pb.VerifyRequest{})
		if diff := pretty.Compare(test.want, got); diff != "" {
			t.Errorf("Verify(context.Background(), &pb.VerifyRequest{}): (-want +got):\n%s", diff)
		}
	}
}

func TestTargetVerifyFail(t *testing.T) {
	tests := []struct {
		settings *Settings
		want     *pb.VerifyResponse
	}{
		{
			settings: &Settings{
				FactoryVersion: "1",
			},
			want: &pb.VerifyResponse{
				Version:               "1",
				ActivationFailMessage: "Failed to activate OS...",
			},
		},
	}
	for _, test := range tests {
		server := NewServer(test.settings)
		server.manager.activationFailMessage = "Failed to activate OS..."
		got, _ := server.Verify(context.Background(), &pb.VerifyRequest{})
		if diff := pretty.Compare(test.want, got); diff != "" {
			t.Errorf("Verify(context.Background(), &pb.VerifyRequest{}): (-want +got):\n%s", diff)
		}
	}
}

func TestTargetActivateAndVerify(t *testing.T) {
	test := struct {
		settings *Settings
		want     *pb.VerifyResponse
	}{
		settings: &Settings{
			FactoryVersion: "1",
		},
		want: &pb.VerifyResponse{
			Version:               "1",
			ActivationFailMessage: "Failed to activate OS...",
		},
	}
	server := NewServer(test.settings)
	server.manager.Install("1.0.1a", "Failed to activate OS...")
	server.Activate(context.Background(), &pb.ActivateRequest{Version: "1.0.1a"})
	got, _ := server.Verify(context.Background(), &pb.VerifyRequest{})
	if diff := pretty.Compare(test.want, got); diff != "" {
		t.Errorf("Verify(context.Background(), &pb.VerifyRequest{}): (-want +got):\n%s", diff)
	}
}

func TestTargetReceiveOS(t *testing.T) {
	buf := make([]byte, 10000000)
	rand.Read(buf)
	oS := &mockos.OS{MockOS: mockosPb.MockOS{
		Version: "1.0.2a",
		Cookie:  "cookiestring",
		Padding: buf,
	}}
	oS.Hash()
	tests := []struct {
		name   string
		stream *mockTransferStream
		err    error
	}{
		{
			name: "sending TransferContent request",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				os:       oS,
			},
			err: nil,
		},
		{
			name: "sending unexpected request type",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				errorReq: &pb.InstallRequest{Request: &pb.InstallRequest_TransferRequest{}}, // Unexpected request after transfer begins.
				os:       oS,
			},
			err: errors.New("Unknown request type"),
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			test.stream.response <- &pb.InstallResponse{Response: &pb.InstallResponse_TransferReady{}}
			_, err := ReceiveOS(test.stream)
			if diff := pretty.Compare(test.err, err); diff != "" {
				t.Errorf("ReceiveOS(stream): (-want +got):\n%s", diff)
			}
		})
	}
}

func TestTargetInstall(t *testing.T) {
	buf := make([]byte, 10000000)
	rand.Read(buf)
	oS := &mockos.OS{MockOS: mockosPb.MockOS{
		Version: "1.0.2a",
		Cookie:  "cookiestring",
		Padding: buf,
	}}
	oS.Hash()
	incompatibleOS := &mockos.OS{MockOS: mockosPb.MockOS{
		Version:      "1.0.2b",
		Cookie:       "cookiestring",
		Padding:      buf,
		Incompatible: true,
	}}
	incompatibleOS.Hash()
	tests := []struct {
		name   string
		stream *mockTransferStream
		want   *installResult
	}{
		{
			name: "transfer compatible os with valid hash",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os:       oS,
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_Validated{Validated: &pb.Validated{Version: oS.Version}}},
				err: nil,
			},
		},
		{
			name: "send bad request instead of InstallRequest_TransferRequest",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				errorReq: &pb.InstallRequest{Request: nil}, // Unexpected request.
				os:       oS,
			},
			want: &installResult{
				res: nil,
				err: errors.New("Failed to receive TransferRequest"),
			},
		},
		{
			name: "force transferring already running os",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os: &mockos.OS{MockOS: mockosPb.MockOS{
					Version: "1.0.0a",
				}},
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_InstallError{
					InstallError: &pb.InstallError{Type: pb.InstallError_INSTALL_RUN_PACKAGE},
				}},
				err: errors.New("Attempting to force transfer an OS of the same version as the currently running OS"),
			},
		},
		{
			name: "transferring already installed os",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os: &mockos.OS{MockOS: mockosPb.MockOS{
					Version: "1.0.3c",
				}},
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_Validated{Validated: &pb.Validated{Version: "1.0.3c"}}},
				err: nil,
			},
		},
		{
			name: "transferring os with bad hash",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os: &mockos.OS{MockOS: mockosPb.MockOS{
					Version: "1.0.2b",
					Cookie:  "cookiestring",
					Padding: buf,
					Hash:    []byte("BADHASH"),
				}},
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_InstallError{InstallError: &pb.InstallError{Type: pb.InstallError_INTEGRITY_FAIL}}},
				err: nil,
			},
		},
		{
			name: "transferring os with incompatible field true",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os:       incompatibleOS,
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_InstallError{InstallError: &pb.InstallError{Type: pb.InstallError_INCOMPATIBLE, Detail: "Unsupported OS Version"}}},
				err: nil,
			},
		},
		{
			name: "transferring random bytes instead of os package",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os: &mockos.OS{MockOS: mockosPb.MockOS{
					Version: "1.0.2c",
				}},
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_InstallError{InstallError: &pb.InstallError{Type: pb.InstallError_PARSE_FAIL}}},
				err: nil,
			},
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			server := NewServer(&Settings{FactoryVersion: "1.0.0a", InstalledVersions: []string{"1.0.3c"}})
			got := &installResult{
				err: server.Install(test.stream),
			}
			close(test.stream.result)
			got.res = <-test.stream.result
			if diff := pretty.Compare(test.want, got); diff != "" {
				t.Errorf("Install(stream pb.OS_InstallServer): (-want +got):\n%s", diff)
			}
		})
	}
}

// TestMultipleInstalls tests for mutual exclusion in the install service.
func TestMultipleInstalls(t *testing.T) {
	t.Run("testing mutual exclusion in install service", func(t *testing.T) {
		buf := make([]byte, 10000000)
		rand.Read(buf)
		oS := &mockos.OS{MockOS: mockosPb.MockOS{
			Version: "1.0.2a",
			Cookie:  "cookiestring",
			Padding: buf,
		}}
		oS.Hash()
		server := NewServer(&Settings{FactoryVersion: "1.0.0a"})
		s1 := &mockTransferStream{
			response: make(chan *pb.InstallResponse, 1),
			result:   make(chan *pb.InstallResponse, 1),
			os:       oS,
		}
		s2 := &mockTransferStream{
			response: make(chan *pb.InstallResponse, 1),
			result:   make(chan *pb.InstallResponse, 1),
			os:       oS,
		}
		go server.Install(s1)
		go server.Install(s2)
		s1res := <-s1.result
		s2res := <-s2.result
		expect := &pb.InstallResponse{Response: &pb.InstallResponse_InstallError{InstallError: &pb.InstallError{Type: pb.InstallError_INSTALL_IN_PROGRESS}}}
		diff1 := pretty.Compare(expect, s1res)
		diff2 := pretty.Compare(expect, s2res)
		if (diff1 != "" && diff2 != "") || diff1 == diff2 {
			t.Errorf("Install(stream pb.OS_InstallServer): (-want +got):\n%s\n%s", diff1, diff2)
		}
	})
}
<commit_msg>Increased Coverage<commit_after>/* Copyright 2020 Google 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
    https://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 os

import (
	"context"
	"crypto/rand"
	"errors"
	"testing"

	"github.com/golang/protobuf/proto"
	"github.com/google/gnxi/gnoi/os/pb"
	"github.com/google/gnxi/utils/mockos"
	mockosPb "github.com/google/gnxi/utils/mockos/pb"
	"github.com/kylelemons/godebug/pretty"
)

type installResult struct {
	res *pb.InstallResponse
	err error
}

type mockTransferStream struct {
	pb.OS_InstallServer
	response chan *pb.InstallResponse
	errorReq *pb.InstallRequest
	result   chan *pb.InstallResponse
	os       *mockos.OS
}

func (m mockTransferStream) Send(res *pb.InstallResponse) error {
	switch res.Response.(type) {
	case *pb.InstallResponse_Validated:
		m.result <- res
	case *pb.InstallResponse_InstallError:
		m.result <- res
	default:
		m.response <- res
	}
	return nil
}

func (m mockTransferStream) Recv() (*pb.InstallRequest, error) {
	if request := m.errorReq; request != nil {
		return request, nil
	}
	select {
	case res := <-m.response:
		switch res.Response.(type) {
		case *pb.InstallResponse_TransferProgress:
			return &pb.InstallRequest{Request: &pb.InstallRequest_TransferEnd{}}, nil
		case *pb.InstallResponse_TransferReady:
			var out []byte
			if m.os.MockOS.Padding != nil {
				out, _ = proto.Marshal(&m.os.MockOS)
			} else {
				out = make([]byte, 10000000)
				rand.Read(out)
			}
			return &pb.InstallRequest{Request: &pb.InstallRequest_TransferContent{TransferContent: out}}, nil
		}
	default:
		return &pb.InstallRequest{Request: &pb.InstallRequest_TransferRequest{TransferRequest: &pb.TransferRequest{Version: m.os.MockOS.Version}}}, nil
	}
	return nil, nil
}

func TestNewServer(t *testing.T) {
	test := struct {
		settings *Settings
		want     *Server
	}{
		settings: &Settings{
			FactoryVersion: "1.0.0a",
		},
		want: &Server{
			installToken: make(chan bool, 1),
			manager: &Manager{
				osMap:          map[string]bool{"1.0.0a": true},
				factoryVersion: "1.0.0a",
				runningVersion: "1.0.0a",
			},
		},
	}
	got := NewServer(test.settings)
	if diff := pretty.Compare(test.want, got); diff != "" {
		t.Errorf("NewServer(%v): (-want +got):\n%s", test.settings, diff)
	}
}

func TestTargetActivate(t *testing.T) {
	settings := &Settings{
		FactoryVersion:    "1",
		InstalledVersions: []string{"1.0.0a", "2.0.1c"},
	}
	server := NewServer(settings)
	tests := []struct {
		request *pb.ActivateRequest
		want    *pb.ActivateResponse
	}{
		{
			request: &pb.ActivateRequest{
				Version: "1.0.0a",
			},
			want: &pb.ActivateResponse{Response: &pb.ActivateResponse_ActivateOk{}},
		},
		{
			request: &pb.ActivateRequest{
				Version: "99.0a",
			},
			want: &pb.ActivateResponse{Response: &pb.ActivateResponse_ActivateError{
				ActivateError: &pb.ActivateError{Type: pb.ActivateError_NON_EXISTENT_VERSION},
			}},
		},
		{
			request: &pb.ActivateRequest{
				Version: "",
			},
			want: &pb.ActivateResponse{Response: &pb.ActivateResponse_ActivateError{
				ActivateError: &pb.ActivateError{Type: pb.ActivateError_UNSPECIFIED},
			}},
		},
	}
	for _, test := range tests {
		got, _ := server.Activate(context.Background(), test.request)
		if diff := pretty.Compare(test.want.Response, got.Response); diff != "" {
			t.Errorf("Activate(context.Background(), %s): (-want +got):\n%s", test.request, diff)
		}
	}
}

func TestTargetVerify(t *testing.T) {
	tests := []struct {
		settings *Settings
		want     *pb.VerifyResponse
	}{
		{
			settings: &Settings{
				FactoryVersion: "1",
			},
			want: &pb.VerifyResponse{
				Version: "1",
			},
		},
	}
	for _, test := range tests {
		server := NewServer(test.settings)
		got, _ := server.Verify(context.Background(), &pb.VerifyRequest{})
		if diff := pretty.Compare(test.want, got); diff != "" {
			t.Errorf("Verify(context.Background(), &pb.VerifyRequest{}): (-want +got):\n%s", diff)
		}
	}
}

func TestTargetVerifyFail(t *testing.T) {
	tests := []struct {
		settings *Settings
		want     *pb.VerifyResponse
	}{
		{
			settings: &Settings{
				FactoryVersion: "1",
			},
			want: &pb.VerifyResponse{
				Version:               "1",
				ActivationFailMessage: "Failed to activate OS...",
			},
		},
	}
	for _, test := range tests {
		server := NewServer(test.settings)
		server.manager.activationFailMessage = "Failed to activate OS..."
		got, _ := server.Verify(context.Background(), &pb.VerifyRequest{})
		if diff := pretty.Compare(test.want, got); diff != "" {
			t.Errorf("Verify(context.Background(), &pb.VerifyRequest{}): (-want +got):\n%s", diff)
		}
	}
}

func TestTargetActivateAndVerify(t *testing.T) {
	test := struct {
		settings *Settings
		want     *pb.VerifyResponse
	}{
		settings: &Settings{
			FactoryVersion: "1",
		},
		want: &pb.VerifyResponse{
			Version:               "1",
			ActivationFailMessage: "Failed to activate OS...",
		},
	}
	server := NewServer(test.settings)
	server.manager.Install("1.0.1a", "Failed to activate OS...")
	server.Activate(context.Background(), &pb.ActivateRequest{Version: "1.0.1a"})
	got, _ := server.Verify(context.Background(), &pb.VerifyRequest{})
	if diff := pretty.Compare(test.want, got); diff != "" {
		t.Errorf("Verify(context.Background(), &pb.VerifyRequest{}): (-want +got):\n%s", diff)
	}
}

func TestTargetReceiveOS(t *testing.T) {
	buf := make([]byte, 10000000)
	rand.Read(buf)
	oS := &mockos.OS{MockOS: mockosPb.MockOS{
		Version: "1.0.2a",
		Cookie:  "cookiestring",
		Padding: buf,
	}}
	oS.Hash()
	tests := []struct {
		name   string
		stream *mockTransferStream
		err    error
	}{
		{
			name: "sending TransferContent request",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				os:       oS,
			},
			err: nil,
		},
		{
			name: "sending unexpected request type",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				errorReq: &pb.InstallRequest{Request: &pb.InstallRequest_TransferRequest{}}, // Unexpected request after transfer begins.
				os:       oS,
			},
			err: errors.New("Unknown request type"),
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			test.stream.response <- &pb.InstallResponse{Response: &pb.InstallResponse_TransferReady{}}
			_, err := ReceiveOS(test.stream)
			if diff := pretty.Compare(test.err, err); diff != "" {
				t.Errorf("ReceiveOS(stream): (-want +got):\n%s", diff)
			}
		})
	}
}

func TestTargetInstall(t *testing.T) {
	buf := make([]byte, 10000000)
	rand.Read(buf)
	oS := &mockos.OS{MockOS: mockosPb.MockOS{
		Version: "1.0.2a",
		Cookie:  "cookiestring",
		Padding: buf,
	}}
	oS.Hash()
	incompatibleOS := &mockos.OS{MockOS: mockosPb.MockOS{
		Version:      "1.0.2b",
		Cookie:       "cookiestring",
		Padding:      buf,
		Incompatible: true,
	}}
	incompatibleOS.Hash()
	tests := []struct {
		name   string
		stream *mockTransferStream
		want   *installResult
	}{
		{
			name: "transfer compatible os with valid hash",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os:       oS,
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_Validated{Validated: &pb.Validated{Version: oS.Version}}},
				err: nil,
			},
		},
		{
			name: "send bad request instead of InstallRequest_TransferRequest",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				errorReq: &pb.InstallRequest{Request: nil}, // Unexpected request.
				os:       oS,
			},
			want: &installResult{
				res: nil,
				err: errors.New("Failed to receive TransferRequest"),
			},
		},
		{
			name: "force transferring already running os",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os: &mockos.OS{MockOS: mockosPb.MockOS{
					Version: "1.0.0a",
				}},
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_InstallError{
					InstallError: &pb.InstallError{Type: pb.InstallError_INSTALL_RUN_PACKAGE},
				}},
				err: errors.New("Attempting to force transfer an OS of the same version as the currently running OS"),
			},
		},
		{
			name: "transferring already installed os",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os: &mockos.OS{MockOS: mockosPb.MockOS{
					Version: "1.0.3c",
				}},
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_Validated{Validated: &pb.Validated{Version: "1.0.3c"}}},
				err: nil,
			},
		},
		{
			name: "transferring os with bad hash",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os: &mockos.OS{MockOS: mockosPb.MockOS{
					Version: "1.0.2b",
					Cookie:  "cookiestring",
					Padding: buf,
					Hash:    []byte("BADHASH"),
				}},
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_InstallError{InstallError: &pb.InstallError{Type: pb.InstallError_INTEGRITY_FAIL}}},
				err: nil,
			},
		},
		{
			name: "transferring os with incompatible field true",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os:       incompatibleOS,
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_InstallError{InstallError: &pb.InstallError{Type: pb.InstallError_INCOMPATIBLE, Detail: "Unsupported OS Version"}}},
				err: nil,
			},
		},
		{
			name: "transferring random bytes instead of os package",
			stream: &mockTransferStream{
				response: make(chan *pb.InstallResponse, 1),
				result:   make(chan *pb.InstallResponse, 1),
				os: &mockos.OS{MockOS: mockosPb.MockOS{
					Version: "1.0.2c",
				}},
			},
			want: &installResult{
				res: &pb.InstallResponse{Response: &pb.InstallResponse_InstallError{InstallError: &pb.InstallError{Type: pb.InstallError_PARSE_FAIL}}},
				err: nil,
			},
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			server := NewServer(&Settings{FactoryVersion: "1.0.0a", InstalledVersions: []string{"1.0.3c"}})
			got := &installResult{
				err: server.Install(test.stream),
			}
			close(test.stream.result)
			got.res = <-test.stream.result
			if diff := pretty.Compare(test.want, got); diff != "" {
				t.Errorf("Install(stream pb.OS_InstallServer): (-want +got):\n%s", diff)
			}
		})
	}
}

// TestMultipleInstalls tests for mutual exclusion in the install service.
func TestMultipleInstalls(t *testing.T) {
	t.Run("testing mutual exclusion in install service", func(t *testing.T) {
		buf := make([]byte, 10000000)
		rand.Read(buf)
		oS := &mockos.OS{MockOS: mockosPb.MockOS{
			Version: "1.0.2a",
			Cookie:  "cookiestring",
			Padding: buf,
		}}
		oS.Hash()
		server := NewServer(&Settings{FactoryVersion: "1.0.0a"})
		s1 := &mockTransferStream{
			response: make(chan *pb.InstallResponse, 1),
			result:   make(chan *pb.InstallResponse, 1),
			os:       oS,
		}
		s2 := &mockTransferStream{
			response: make(chan *pb.InstallResponse, 1),
			result:   make(chan *pb.InstallResponse, 1),
			os:       oS,
		}
		go server.Install(s1)
		go server.Install(s2)
		s1res := <-s1.result
		s2res := <-s2.result
		expect := &pb.InstallResponse{Response: &pb.InstallResponse_InstallError{InstallError: &pb.InstallError{Type: pb.InstallError_INSTALL_IN_PROGRESS}}}
		diff1 := pretty.Compare(expect, s1res)
		diff2 := pretty.Compare(expect, s2res)
		if (diff1 != "" && diff2 != "") || diff1 == diff2 {
			t.Errorf("Install(stream pb.OS_InstallServer): (-want +got):\n%s\n%s", diff1, diff2)
		}
	})
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// +build !windows
// socket_nix.go

package libkb

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/keybase/client/go/logger"
)

// Though I've never seen this come up in production, it definitely comes up
// in systests that multiple go-routines might race over the current
// working directory as they do the (chdir && dial) dance below. Make sure
// a lock is held whenever operating on sockets, so that two racing goroutines
// can't conflict here.
var bindLock sync.Mutex

func (s SocketInfo) BindToSocket() (net.Listener, error) {

	// Lock so that multiple goroutines can't race over current working dir.
	// See note above.
	bindLock.Lock()
	defer bindLock.Unlock()

	bindFile := s.bindFile
	if err := MakeParentDirs(s.log, bindFile); err != nil {
		return nil, err
	}

	// Path can't be longer than N characters.
	// In this case Chdir to the file directory first and use a local path.
	// On many linuxes, N=108, on some N=106, and on macOS N=104.
	// N=104 is the lowest I know of.
	// It's the length of the path buffer in sockaddr_un.
	// And there may be a null-terminator in there, not sure, so make it 103 for good luck.
	// https://github.com/golang/go/issues/6895#issuecomment-98006662
	// https://gist.github.com/mlsteele/16dc5b6eb3d112b914183928c9af71b8#file-un-h-L79
	// We could always Chdir, but then this function would be non-threadsafe more of the time.
	// Pick your poison.
	if len(bindFile) >= 103 {
		prevWd, err := os.Getwd()
		if err != nil {
			return nil, fmt.Errorf("Error getting working directory: %s", err)
		}
		s.log.Warning("Changing current working directory because path for binding is too long")
		if err := os.Chdir(filepath.Dir(bindFile)); err != nil {
			return nil, fmt.Errorf("Path can't be longer than 108 characters (failed to chdir): %s", err)
		}
		defer os.Chdir(prevWd)
		bindFile = filepath.Base(bindFile)
	}

	s.log.Info("Binding to unix:%s", bindFile)
	return net.Listen("unix", bindFile)
}

func (s SocketInfo) DialSocket() (net.Conn, error) {
	errs := []error{}
	for _, file := range s.dialFiles {
		ret, err := s.dialSocket(file)
		if err == nil {
			return ret, nil
		}
		errs = append(errs, err)
	}
	return nil, CombineErrors(errs...)
}

func (s SocketInfo) dialSocket(dialFile string) (net.Conn, error) {

	// Lock so that multiple goroutines can't race over current working dir.
	// See note above.
	bindLock.Lock()
	defer bindLock.Unlock()

	if dialFile == "" {
		return nil, fmt.Errorf("Can't dial empty path")
	}
	// Path can't be longer than 108 characters.
	// In this case Chdir to the file directory first.
	// https://github.com/golang/go/issues/6895#issuecomment-98006662
	if len(dialFile) >= 108 {
		prevWd, err := os.Getwd()
		if err != nil {
			return nil, fmt.Errorf("Error getting working directory: %s", err)
		}
		s.log.Warning("Changing current working directory because path for dialing is too long")
		if err := os.Chdir(filepath.Dir(dialFile)); err != nil {
			return nil, fmt.Errorf("Path can't be longer than 108 characters (failed to chdir): %s", err)
		}
		defer os.Chdir(prevWd)
		dialFile = filepath.Base(dialFile)
	}

	s.log.Debug("Dialing unix:%s", dialFile)
	return net.Dial("unix", dialFile)
}

func NewSocket(g *GlobalContext) (ret Socket, err error) {
	var dialFiles []string
	dialFiles, err = g.Env.GetSocketDialFiles()
	if err != nil {
		return
	}
	var bindFile string
	bindFile, err = g.Env.GetSocketBindFile()
	if err != nil {
		return
	}
	log := g.Log
	if log == nil {
		log = logger.NewNull()
	}
	ret = SocketInfo{
		log:       log,
		dialFiles: dialFiles,
		bindFile:  bindFile,
	}
	return
}

func NewSocketWithFiles(
	log logger.Logger, bindFile string, dialFiles []string) Socket {
	return SocketInfo{
		log:       log,
		bindFile:  bindFile,
		dialFiles: dialFiles,
	}
}

// net.errClosing isn't exported, so do this.. UGLY!
func IsSocketClosedError(e error) bool {
	return strings.HasSuffix(e.Error(), "use of closed network connection")
}
<commit_msg>better socket debugging (#9056)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// +build !windows
// socket_nix.go

package libkb

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/keybase/client/go/logger"
)

// Though I've never seen this come up in production, it definitely comes up
// in systests that multiple go-routines might race over the current
// working directory as they do the (chdir && dial) dance below. Make sure
// a lock is held whenever operating on sockets, so that two racing goroutines
// can't conflict here.
var bindLock sync.Mutex

func (s SocketInfo) BindToSocket() (ret net.Listener, err error) {

	// Lock so that multiple goroutines can't race over current working dir.
	// See note above.
	bindLock.Lock()
	defer bindLock.Unlock()

	bindFile := s.bindFile
	what := fmt.Sprintf("SocketInfo#BindToSocket(unix:%s)", bindFile)
	defer Trace(s.log, what, func() error { return err })()

	if err := MakeParentDirs(s.log, bindFile); err != nil {
		return nil, err
	}

	// Path can't be longer than N characters.
	// In this case Chdir to the file directory first and use a local path.
	// On many linuxes, N=108, on some N=106, and on macOS N=104.
	// N=104 is the lowest I know of.
	// It's the length of the path buffer in sockaddr_un.
	// And there may be a null-terminator in there, not sure, so make it 103 for good luck.
	// https://github.com/golang/go/issues/6895#issuecomment-98006662
	// https://gist.github.com/mlsteele/16dc5b6eb3d112b914183928c9af71b8#file-un-h-L79
	// We could always Chdir, but then this function would be non-threadsafe more of the time.
	// Pick your poison.
	if len(bindFile) >= 103 {
		prevWd, err := os.Getwd()
		if err != nil {
			return nil, fmt.Errorf("Error getting working directory: %s", err)
		}
		s.log.Warning("| Changing current working directory because path for binding is too long")
		dir := filepath.Dir(bindFile)
		s.log.Debug("| Chdir(%s)", dir)
		if err := os.Chdir(dir); err != nil {
			return nil, fmt.Errorf("Path can't be longer than 108 characters (failed to chdir): %s", err)
		}

		defer func() {
			s.log.Debug("| Chdir(%s)", prevWd)
			os.Chdir(prevWd)
		}()

		bindFile = filepath.Base(bindFile)
	}

	s.log.Info("| net.Listen on unix:%s", bindFile)
	ret, err = net.Listen("unix", bindFile)
	if err != nil {
		s.log.Warning("net.Listen failed with: %s", err.Error())
	}
	return ret, err
}

func (s SocketInfo) DialSocket() (net.Conn, error) {
	errs := []error{}
	for _, file := range s.dialFiles {
		ret, err := s.dialSocket(file)
		if err == nil {
			return ret, nil
		}
		errs = append(errs, err)
	}
	return nil, CombineErrors(errs...)
}

func (s SocketInfo) dialSocket(dialFile string) (ret net.Conn, err error) {

	// Lock so that multiple goroutines can't race over current working dir.
	// See note above.
	bindLock.Lock()
	defer bindLock.Unlock()

	what := fmt.Sprintf("SocketInfo#dialSocket(unix:%s)", dialFile)
	defer Trace(s.log, what, func() error { return err })()

	if dialFile == "" {
		return nil, fmt.Errorf("Can't dial empty path")
	}

	// Path can't be longer than 108 characters.
	// In this case Chdir to the file directory first.
	// https://github.com/golang/go/issues/6895#issuecomment-98006662
	if len(dialFile) >= 108 {
		prevWd, err := os.Getwd()
		if err != nil {
			return nil, fmt.Errorf("Error getting working directory: %s", err)
		}
		s.log.Warning("| Changing current working directory because path for dialing is too long")
		dir := filepath.Dir(dialFile)
		s.log.Debug("| os.Chdir(%s)", dir)
		if err := os.Chdir(dir); err != nil {
			return nil, fmt.Errorf("Path can't be longer than 108 characters (failed to chdir): %s", err)
		}
		defer os.Chdir(prevWd)
		dialFile = filepath.Base(dialFile)
	}

	s.log.Debug("| net.Dial(unix:%s)", dialFile)
	return net.Dial("unix", dialFile)
}

func NewSocket(g *GlobalContext) (ret Socket, err error) {
	var dialFiles []string
	dialFiles, err = g.Env.GetSocketDialFiles()
	if err != nil {
		return
	}
	var bindFile string
	bindFile, err = g.Env.GetSocketBindFile()
	if err != nil {
		return
	}
	log := g.Log
	if log == nil {
		log = logger.NewNull()
	}
	ret = SocketInfo{
		log:       log,
		dialFiles: dialFiles,
		bindFile:  bindFile,
	}
	return
}

func NewSocketWithFiles(
	log logger.Logger, bindFile string, dialFiles []string) Socket {
	return SocketInfo{
		log:       log,
		bindFile:  bindFile,
		dialFiles: dialFiles,
	}
}

// net.errClosing isn't exported, so do this.. UGLY!
func IsSocketClosedError(e error) bool {
	return strings.HasSuffix(e.Error(), "use of closed network connection")
}
<|endoftext|>


<commit_before>package goat

import (
	"database/sql"
	"log"
)

// FileUserRecord represents a file tracked by tracker
type FileUserRecord struct {
	FileID     int `db:"file_id"`
	UserID     int `db:"user_id"`
	IP         string
	Active     bool
	Completed  bool
	Announced  int
	Uploaded   int64
	Downloaded int64
	Left       int64
	Time       int64
}

// FileUserRecordRepository is used to contain methods to load multiple FileRecord structs
type FileUserRecordRepository struct {
}

// Save FileUserRecord to storage
func (f FileUserRecord) Save() bool {
	// Open database connection
	db, err := DBConnect()
	if err != nil {
		log.Println(err.Error())
		return false
	}

	// Insert or update a file/user relationship record
	query := "INSERT INTO files_users " +
		"(`file_id`, `user_id`, `ip`, `active`, `completed`, `announced`, `uploaded`, `downloaded`, `left`, `time`) " +
		"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP()) " +
		"ON DUPLICATE KEY UPDATE " +
		"`active`=values(`active`), `completed`=values(`completed`), `announced`=values(`announced`), " +
		"`uploaded`=values(`uploaded`), `downloaded`=values(`downloaded`), `left`=values(`left`), " +
		"`time`=UNIX_TIMESTAMP();"

	// Create database transaction, do insert, commit
	tx := db.MustBegin()
	tx.Execl(query, f.FileID, f.UserID, f.IP, f.Active, f.Completed, f.Announced, f.Uploaded, f.Downloaded, f.Left)
	tx.Commit()

	return true
}

// Load FileUserRecord from storage
func (f FileUserRecord) Load(fileID int, userID int, ip string) FileUserRecord {
	// Open database connection
	db, err := DBConnect()
	if err != nil {
		log.Println(err.Error())
		return f
	}

	// Fetch announce log into struct
	f = FileUserRecord{}
	err = db.Get(&f, "SELECT * FROM files_users WHERE `file_id`=? AND `user_id`=? AND `ip`=?", fileID, userID, ip)
	if err != nil && err != sql.ErrNoRows {
		log.Println(err.Error())
		return FileUserRecord{}
	}

	return f
}

// Select loads selected FileUserRecord structs from storage
func (f FileUserRecordRepository) Select(id interface{}, col string) []FileUserRecord {
	fileUsers := make([]FileUserRecord, 0)

	// Open database connection
	db, err := DBConnect()
	if err != nil {
		log.Println(err.Error())
		return fileUsers
	}

	// Load all files
	rows, err := db.Queryx("SELECT * FROM files_users WHERE `"+col+"`=?", id)
	if err != nil && err != sql.ErrNoRows {
		log.Println(err.Error())
		return fileUsers
	}

	// Iterate all rows and build array
	fileUser := FileUserRecord{}
	for rows.Next() {
		// Scan row results
		rows.StructScan(&fileUser)
		fileUsers = append(fileUsers[:], fileUser)
	}

	return fileUsers
}
<commit_msg>Add JSON fields to FileUserRecord<commit_after>package goat

import (
	"database/sql"
	"log"
)

// FileUserRecord represents a file tracked by tracker
type FileUserRecord struct {
	FileID     int    `db:"file_id" json:"fileId"`
	UserID     int    `db:"user_id" json:"userId"`
	IP         string `json:"ip"`
	Active     bool   `json:"active"`
	Completed  bool   `json:"completed"`
	Announced  int    `json:"announced"`
	Uploaded   int64  `json:"uploaded"`
	Downloaded int64  `json:"downloaded"`
	Left       int64  `json:"left"`
	Time       int64  `json:"time"`
}

// FileUserRecordRepository is used to contain methods to load multiple FileRecord structs
type FileUserRecordRepository struct {
}

// Save FileUserRecord to storage
func (f FileUserRecord) Save() bool {
	// Open database connection
	db, err := DBConnect()
	if err != nil {
		log.Println(err.Error())
		return false
	}

	// Insert or update a file/user relationship record
	query := "INSERT INTO files_users " +
		"(`file_id`, `user_id`, `ip`, `active`, `completed`, `announced`, `uploaded`, `downloaded`, `left`, `time`) " +
		"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP()) " +
		"ON DUPLICATE KEY UPDATE " +
		"`active`=values(`active`), `completed`=values(`completed`), `announced`=values(`announced`), " +
		"`uploaded`=values(`uploaded`), `downloaded`=values(`downloaded`), `left`=values(`left`), " +
		"`time`=UNIX_TIMESTAMP();"

	// Create database transaction, do insert, commit
	tx := db.MustBegin()
	tx.Execl(query, f.FileID, f.UserID, f.IP, f.Active, f.Completed, f.Announced, f.Uploaded, f.Downloaded, f.Left)
	tx.Commit()

	return true
}

// Load FileUserRecord from storage
func (f FileUserRecord) Load(fileID int, userID int, ip string) FileUserRecord {
	// Open database connection
	db, err := DBConnect()
	if err != nil {
		log.Println(err.Error())
		return f
	}

	// Fetch announce log into struct
	f = FileUserRecord{}
	err = db.Get(&f, "SELECT * FROM files_users WHERE `file_id`=? AND `user_id`=? AND `ip`=?", fileID, userID, ip)
	if err != nil && err != sql.ErrNoRows {
		log.Println(err.Error())
		return FileUserRecord{}
	}

	return f
}

// Select loads selected FileUserRecord structs from storage
func (f FileUserRecordRepository) Select(id interface{}, col string) []FileUserRecord {
	fileUsers := make([]FileUserRecord, 0)

	// Open database connection
	db, err := DBConnect()
	if err != nil {
		log.Println(err.Error())
		return fileUsers
	}

	// Load all files
	rows, err := db.Queryx("SELECT * FROM files_users WHERE `"+col+"`=?", id)
	if err != nil && err != sql.ErrNoRows {
		log.Println(err.Error())
		return fileUsers
	}

	// Iterate all rows and build array
	fileUser := FileUserRecord{}
	for rows.Next() {
		// Scan row results
		rows.StructScan(&fileUser)
		fileUsers = append(fileUsers[:], fileUser)
	}

	return fileUsers
}
<|endoftext|>


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

package main

import (
	"fmt"
	jwt "github.com/dgrijalva/jwt-go"
	"io/ioutil"
	"net/http"
	"time"
)

// openssl genrsa -out demo.rsa 1024 # the 1024 is the size of the key we are generating
// openssl rsa -in demo.rsa -pubout > demo.rsa.pub
var (
    privateKey []byte
    publicKey  []byte
)

func init() {
    privateKey, _ = ioutil.ReadFile("keys/demo.rsa")
    publicKey, _ = ioutil.ReadFile("keys/demo.rsa.pub")
}

func handleLogin(w http.ResponseWriter, r *http.Request) {
    token := jwt.New(jwt.GetSigningMethod("RS256"))
    token.Claims["ID"] = "This is my super fake ID"
    token.Claims["exp"] = time.Now().Unix() + 30
    tokenString, _ := token.SignedString(privateKey)

    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, `{"token": %s}`, tokenString)
}

func jwtHandler(w http.ResponseWriter, r *http.Request) {
    token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error) {
        return publicKey, nil
    })

    fmt.Println(err)
    fmt.Println(token)

    if err != nil {
        http.Error(w, "Bad request", 500)
    }

	if token.Valid {
        fmt.Fprintf(w, "Yo man! ")
	} else {
        fmt.Fprintf(w, "Bad ! ")
	}
}

func authHandler(w http.ResponseWriter, r *http.Request) {
	login, password := r.FormValue("login"), r.FormValue("password")

	if login == "foo" && password == "secret" {
		fmt.Fprintf(w, "Connect\n")
		// jwtHandler(w, r)
	} else {
		fmt.Fprintf(w, "Connect\n")
	}

	fmt.Fprintf(w, "Server start...\n")
	fmt.Fprintf(w, login)
}

func main() {
	mux := http.NewServeMux()
	mux.Handle("/login", http.HandlerFunc(handleLogin))
	mux.Handle("/api", http.HandlerFunc(authHandler))
	http.ListenAndServe(":8080", mux)
}
<commit_msg>Update main.go<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
    "fmt"
    jwt "github.com/dgrijalva/jwt-go"
    "io/ioutil"
    "net/http"
    "time"
)

// openssl genrsa -out demo.rsa 1024 # the 1024 is the size of the key we are generating
// openssl rsa -in demo.rsa -pubout > demo.rsa.pub
var (
    privateKey []byte
    publicKey  []byte
)

func init() {
    privateKey, _ = ioutil.ReadFile("keys/demo.rsa")
    publicKey, _ = ioutil.ReadFile("keys/demo.rsa.pub")
}

func handleLogin(w http.ResponseWriter, r *http.Request) {
    token := jwt.New(jwt.GetSigningMethod("RS256"))
    token.Claims["ID"] = "This is my super fake ID"
    token.Claims["exp"] = time.Now().Unix() + 30
    tokenString, _ := token.SignedString(privateKey)

    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, `{"token": %s}`, tokenString)
}

func jwtHandler(w http.ResponseWriter, r *http.Request) {
    token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error) {
        return publicKey, nil
    })

    fmt.Println(err)
    fmt.Println(token)

    if err != nil {
        http.Error(w, "Bad request", 500)
    }

	if token.Valid {
        fmt.Fprintf(w, "Yo man! ")
	} else {
        fmt.Fprintf(w, "Bad ! ")
	}
}

func authHandler(w http.ResponseWriter, r *http.Request) {
    login, password := r.FormValue("login"), r.FormValue("password")

    if login == "foo" && password == "secret" {
        fmt.Fprintf(w, "Connect\n")
		// jwtHandler(w, r)
    } else {
        fmt.Fprintf(w, "Connect\n")
    }

    fmt.Fprintf(w, "Server start...\n")
    fmt.Fprintf(w, login)
}

func main() {
    mux := http.NewServeMux()
    mux.Handle("/login", http.HandlerFunc(handleLogin))
    mux.Handle("/api", http.HandlerFunc(authHandler))
    http.ListenAndServe(":8080", mux)
}
<|endoftext|>


<commit_before><commit_msg>lock a guid<commit_after><|endoftext|>


<commit_before>package main

import (
	"flag"

	"math/rand"
	"time"

	"os"

	_ "net/http/pprof"

	"runtime"

	"os/signal"

	"syscall"

	"github.com/xiaonanln/goworld/components/binutil"
	"github.com/xiaonanln/goworld/components/dispatcher/dispatcher_client"
	"github.com/xiaonanln/goworld/config"
	"github.com/xiaonanln/goworld/gwlog"
	"github.com/xiaonanln/goworld/netutil"
	"github.com/xiaonanln/goworld/proto"
)

var (
	gateid      uint16
	configFile  string
	logLevel    string
	gateService *GateService
	signalChan  = make(chan os.Signal, 1)
)

func init() {
	parseArgs()
}

func parseArgs() {
	var gateIdArg int
	flag.IntVar(&gateIdArg, "gid", 0, "set gateid")
	flag.StringVar(&configFile, "configfile", "", "set config file path")
	flag.StringVar(&logLevel, "log", "", "set log level, will override log level in config")
	flag.Parse()
	gateid = uint16(gateIdArg)
}

func main() {
	rand.Seed(time.Now().UnixNano())

	if configFile != "" {
		config.SetConfigFile(configFile)
	}

	gateConfig := config.GetGate(gateid)
	if gateConfig.GoMaxProcs > 0 {
		gwlog.Info("SET GOMAXPROCS = %d", gateConfig.GoMaxProcs)
		runtime.GOMAXPROCS(gateConfig.GoMaxProcs)
	}
	if logLevel == "" {
		logLevel = gateConfig.LogLevel
	}
	binutil.SetupGWLog(logLevel, gateConfig.LogFile, gateConfig.LogStderr)

	binutil.SetupPprofServer(gateConfig.PProfIp, gateConfig.PProfPort)
	dispatcher_client.Initialize(&dispatcherClientDelegate{})
	gateService = newGateService()
	setupSignals()
	gateService.run() // run gate service in another goroutine
}

func setupSignals() {
	gwlog.Info("Setup signals ...")
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		for {
			sig := <-signalChan
			if sig == syscall.SIGINT || sig == syscall.SIGTERM {
				// terminating gate ...
				gwlog.Info("Terminating gate service ...")
				gateService.terminate()
				gateService.terminated.Wait()
				gwlog.Info("Terminating game service ...")
				os.Exit(0)
			} else {
				gwlog.Error("unexpected signal: %s", sig)
			}
		}
	}()
}

type dispatcherClientDelegate struct {
}

func (delegate *dispatcherClientDelegate) OnDispatcherClientConnect(dispatcherClient *dispatcher_client.DispatcherClient, isReconnect bool) {
	// called when connected / reconnected to dispatcher (not in main routine)
	dispatcherClient.SendSetGateID(gateid)
}

var lastWarnGateServiceQueueLen = 0

func (delegate *dispatcherClientDelegate) HandleDispatcherClientPacket(msgtype proto.MsgType_t, packet *netutil.Packet) {
	gateService.packetQueue.Push(packetQueueItem{
		msgtype: msgtype,
		packet:  packet,
	})
	qlen := gateService.packetQueue.Len()
	if qlen >= 1000 && qlen%1000 == 0 && lastWarnGateServiceQueueLen != qlen {
		gwlog.Warn("Gate service queue length = %d", qlen)
		lastWarnGateServiceQueueLen = qlen
	}
}

func GetGateID() uint16 {
	return gateid
}
<commit_msg>gate terminated gracefully<commit_after>package main

import (
	"flag"

	"math/rand"
	"time"

	"os"

	_ "net/http/pprof"

	"runtime"

	"os/signal"

	"syscall"

	"github.com/xiaonanln/goworld/components/binutil"
	"github.com/xiaonanln/goworld/components/dispatcher/dispatcher_client"
	"github.com/xiaonanln/goworld/config"
	"github.com/xiaonanln/goworld/gwlog"
	"github.com/xiaonanln/goworld/netutil"
	"github.com/xiaonanln/goworld/proto"
)

var (
	gateid      uint16
	configFile  string
	logLevel    string
	gateService *GateService
	signalChan  = make(chan os.Signal, 1)
)

func init() {
	parseArgs()
}

func parseArgs() {
	var gateIdArg int
	flag.IntVar(&gateIdArg, "gid", 0, "set gateid")
	flag.StringVar(&configFile, "configfile", "", "set config file path")
	flag.StringVar(&logLevel, "log", "", "set log level, will override log level in config")
	flag.Parse()
	gateid = uint16(gateIdArg)
}

func main() {
	rand.Seed(time.Now().UnixNano())

	if configFile != "" {
		config.SetConfigFile(configFile)
	}

	gateConfig := config.GetGate(gateid)
	if gateConfig.GoMaxProcs > 0 {
		gwlog.Info("SET GOMAXPROCS = %d", gateConfig.GoMaxProcs)
		runtime.GOMAXPROCS(gateConfig.GoMaxProcs)
	}
	if logLevel == "" {
		logLevel = gateConfig.LogLevel
	}
	binutil.SetupGWLog(logLevel, gateConfig.LogFile, gateConfig.LogStderr)

	binutil.SetupPprofServer(gateConfig.PProfIp, gateConfig.PProfPort)
	dispatcher_client.Initialize(&dispatcherClientDelegate{})
	gateService = newGateService()
	setupSignals()
	gateService.run() // run gate service in another goroutine
}

func setupSignals() {
	gwlog.Info("Setup signals ...")
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		for {
			sig := <-signalChan
			if sig == syscall.SIGINT || sig == syscall.SIGTERM {
				// terminating gate ...
				gwlog.Info("Terminating gate service ...")
				gateService.terminate()
				gateService.terminated.Wait()
				gwlog.Info("Gate %d terminated gracefully.", gateid)
				os.Exit(0)
			} else {
				gwlog.Error("unexpected signal: %s", sig)
			}
		}
	}()
}

type dispatcherClientDelegate struct {
}

func (delegate *dispatcherClientDelegate) OnDispatcherClientConnect(dispatcherClient *dispatcher_client.DispatcherClient, isReconnect bool) {
	// called when connected / reconnected to dispatcher (not in main routine)
	dispatcherClient.SendSetGateID(gateid)
}

var lastWarnGateServiceQueueLen = 0

func (delegate *dispatcherClientDelegate) HandleDispatcherClientPacket(msgtype proto.MsgType_t, packet *netutil.Packet) {
	gateService.packetQueue.Push(packetQueueItem{
		msgtype: msgtype,
		packet:  packet,
	})
	qlen := gateService.packetQueue.Len()
	if qlen >= 1000 && qlen%1000 == 0 && lastWarnGateServiceQueueLen != qlen {
		gwlog.Warn("Gate service queue length = %d", qlen)
		lastWarnGateServiceQueueLen = qlen
	}
}

func GetGateID() uint16 {
	return gateid
}
<|endoftext|>


<commit_before>package main

import "flag"
import "log"
import "net/http"
import "net/url"
import "os"
import "strconv"
import "text/template"

import "github.com/3ofcoins/cardea"

var cfg cardea.Config

var nginxConfigTemplateSource = `
set $cardea_server {{.ServerURL}};
set $cardea_handler {{.HandlerURL}};
set $cardea_location "/.-cardea-.";

location $cardea_location {
  proxy_pass $cardea_handler/;
  proxy_pass_request_body off;
  proxy_set_header Content-Length "";
  proxy_set_header X-Cardea-RequestInfo "$remote_addr $scheme://$host$request_uri";
  internal;
}

auth_request $cardea_location;
error_page 403 =301 $cardea_server?reason=$cardea_nonce&ref=$scheme://$host$request_uri;

auth_request_set $cardea_user $upstream_http_x_cardea_user;
auth_request_set $cardea_roles $upstream_http_x_cardea_roles;
auth_request_set $cardea_nonce $upstream_http_x_cardea_nonce;
auth_request_set $remote_user $upstream_http_x_cardea_user;
`

type nginxConfigParameters struct {
	ServerURL  string
	HandlerURL string
}

func main() {
	default_expiration_sec := cardea.DEFAULT_EXPIRATION_SEC
	default_cookie_name := cardea.DEFAULT_COOKIE_NAME

	if env := os.Getenv("CARDEA_COOKIE"); env != "" {
		default_cookie_name = env
	}

	if env := os.Getenv("CARDEA_EXPIRATION_SEC"); env != "" {
		if n, err := strconv.ParseUint(env, 10, 64); err == nil {
			default_expiration_sec = n
		} else {
			log.Fatal(err)
		}
	}

	flag.StringVar(&cfg.Secret, "secret", os.Getenv("CARDEA_SECRET"),
		"(not recommended, use CARDEA_SECRET environment variable instead if possible)")
	flag.StringVar(&cfg.Cookie, "cookie", default_cookie_name,
		"Name of Cardea's cookie")
	flag.Uint64Var(&cfg.ExpirationSec, "expiration-sec", default_expiration_sec,
		"Cookie older than this many seconds will be considered expired")
	listen := flag.String("listen", ":8080", "ip:port to listen on")
	flag.BoolVar(&cfg.ConflateWebkitUA, "conflate-webkit-ua", false,
		"Conflate WebKit user-agents (for legacy compatibility)")
	flag.Parse()

	if cfg.Secret == "" {
		log.Fatal("Need a secret to start; set CARDEA_SECRET environment variable")
	}

	log.Println("Starting httpd on", *listen)

	mux := http.NewServeMux()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/" {
			http.NotFound(w, r)
		} else {
			cfg.ServeHTTP(w, r)
		}
	})

	nginxConfigTemplate := template.New("nginx.conf")
	nginxConfigTemplate.Parse(nginxConfigTemplateSource)

	mux.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) {
		var params nginxConfigParameters
		qp := r.URL.Query()

		if qp["server"] != nil {
			params.ServerURL = qp["server"][0]
		} else {
			params.ServerURL = "FIXME"
		}

		if qp["handler"] != nil {
			params.HandlerURL = qp["handler"][0]
		} else {
			u := &url.URL{"http", "", nil, r.Host, "", "", ""}
			if r.Header["X-Forwarded-Proto"] != nil {
				u.Scheme = r.Header["X-Forwarded-Proto"][0]
			}
			params.HandlerURL = u.String()
		}

		nginxConfigTemplate.Execute(w, params)
	})

	http.ListenAndServe(*listen, mux)
}
<commit_msg>Don't try to reset read-only $remote_user<commit_after>package main

import "flag"
import "log"
import "net/http"
import "net/url"
import "os"
import "strconv"
import "text/template"

import "github.com/3ofcoins/cardea"

var cfg cardea.Config

var nginxConfigTemplateSource = `
set $cardea_server {{.ServerURL}};
set $cardea_handler {{.HandlerURL}};
set $cardea_location "/.-cardea-.";

location $cardea_location {
  proxy_pass $cardea_handler/;
  proxy_pass_request_body off;
  proxy_set_header Content-Length "";
  proxy_set_header X-Cardea-RequestInfo "$remote_addr $scheme://$host$request_uri";
  internal;
}

auth_request $cardea_location;
error_page 403 =301 $cardea_server?reason=$cardea_nonce&ref=$scheme://$host$request_uri;

auth_request_set $cardea_user $upstream_http_x_cardea_user;
auth_request_set $cardea_roles $upstream_http_x_cardea_roles;
auth_request_set $cardea_nonce $upstream_http_x_cardea_nonce;
`

type nginxConfigParameters struct {
	ServerURL  string
	HandlerURL string
}

func main() {
	default_expiration_sec := cardea.DEFAULT_EXPIRATION_SEC
	default_cookie_name := cardea.DEFAULT_COOKIE_NAME

	if env := os.Getenv("CARDEA_COOKIE"); env != "" {
		default_cookie_name = env
	}

	if env := os.Getenv("CARDEA_EXPIRATION_SEC"); env != "" {
		if n, err := strconv.ParseUint(env, 10, 64); err == nil {
			default_expiration_sec = n
		} else {
			log.Fatal(err)
		}
	}

	flag.StringVar(&cfg.Secret, "secret", os.Getenv("CARDEA_SECRET"),
		"(not recommended, use CARDEA_SECRET environment variable instead if possible)")
	flag.StringVar(&cfg.Cookie, "cookie", default_cookie_name,
		"Name of Cardea's cookie")
	flag.Uint64Var(&cfg.ExpirationSec, "expiration-sec", default_expiration_sec,
		"Cookie older than this many seconds will be considered expired")
	listen := flag.String("listen", ":8080", "ip:port to listen on")
	flag.BoolVar(&cfg.ConflateWebkitUA, "conflate-webkit-ua", false,
		"Conflate WebKit user-agents (for legacy compatibility)")
	flag.Parse()

	if cfg.Secret == "" {
		log.Fatal("Need a secret to start; set CARDEA_SECRET environment variable")
	}

	log.Println("Starting httpd on", *listen)

	mux := http.NewServeMux()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/" {
			http.NotFound(w, r)
		} else {
			cfg.ServeHTTP(w, r)
		}
	})

	nginxConfigTemplate := template.New("nginx.conf")
	nginxConfigTemplate.Parse(nginxConfigTemplateSource)

	mux.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) {
		var params nginxConfigParameters
		qp := r.URL.Query()

		if qp["server"] != nil {
			params.ServerURL = qp["server"][0]
		} else {
			params.ServerURL = "FIXME"
		}

		if qp["handler"] != nil {
			params.HandlerURL = qp["handler"][0]
		} else {
			u := &url.URL{"http", "", nil, r.Host, "", "", ""}
			if r.Header["X-Forwarded-Proto"] != nil {
				u.Scheme = r.Header["X-Forwarded-Proto"][0]
			}
			params.HandlerURL = u.String()
		}

		nginxConfigTemplate.Execute(w, params)
	})

	http.ListenAndServe(*listen, mux)
}
<|endoftext|>


<commit_before>package wallet

import (
	"bytes"
	"errors"
	"fmt"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
	"github.com/NebulousLabs/bolt"
	"github.com/NebulousLabs/fastrand"
)

var (
	errAlreadyUnlocked   = errors.New("wallet has already been unlocked")
	errReencrypt         = errors.New("wallet is already encrypted, cannot encrypt again")
	errUnencryptedWallet = errors.New("wallet has not been encrypted yet")

	// verificationPlaintext is the plaintext used to verify encryption keys.
	// By storing the corresponding ciphertext for a given key, we can later
	// verify that a key is correct by using it to decrypt the ciphertext and
	// comparing the result to verificationPlaintext.
	verificationPlaintext = make([]byte, 32)
)

// uidEncryptionKey creates an encryption key that is used to decrypt a
// specific key file.
func uidEncryptionKey(masterKey crypto.TwofishKey, uid uniqueID) crypto.TwofishKey {
	return crypto.TwofishKey(crypto.HashAll(masterKey, uid))
}

// verifyEncryption verifies that key properly decrypts the ciphertext to a
// preset plaintext.
func verifyEncryption(key crypto.TwofishKey, encrypted crypto.Ciphertext) error {
	verification, err := key.DecryptBytes(encrypted)
	if err != nil {
		return modules.ErrBadEncryptionKey
	}
	if !bytes.Equal(verificationPlaintext, verification) {
		return modules.ErrBadEncryptionKey
	}
	return nil
}

// checkMasterKey verifies that the masterKey is the key used to encrypt the wallet.
func checkMasterKey(tx *bolt.Tx, masterKey crypto.TwofishKey) error {
	uk := uidEncryptionKey(masterKey, dbGetWalletUID(tx))
	encryptedVerification := tx.Bucket(bucketWallet).Get(keyEncryptionVerification)
	return verifyEncryption(uk, encryptedVerification)
}

// initEncryption initializes and encrypts the primary SeedFile.
func (w *Wallet) initEncryption(masterKey crypto.TwofishKey, seed modules.Seed) (modules.Seed, error) {
	wb := w.dbTx.Bucket(bucketWallet)
	// Check if the wallet encryption key has already been set.
	if wb.Get(keyEncryptionVerification) != nil {
		return modules.Seed{}, errReencrypt
	}

	// create a seedFile for the seed
	sf := createSeedFile(masterKey, seed)

	// set this as the primary seedFile
	err := wb.Put(keyPrimarySeedFile, encoding.Marshal(sf))
	if err != nil {
		return modules.Seed{}, err
	}
	err = wb.Put(keyPrimarySeedProgress, encoding.Marshal(uint64(0)))
	if err != nil {
		return modules.Seed{}, err
	}

	// Establish the encryption verification using the masterKey. After this
	// point, the wallet is encrypted.
	uk := uidEncryptionKey(masterKey, dbGetWalletUID(w.dbTx))
	err = wb.Put(keyEncryptionVerification, uk.EncryptBytes(verificationPlaintext))
	if err != nil {
		return modules.Seed{}, err
	}

	// on future startups, this field will be set by w.initPersist
	w.encrypted = true

	return seed, nil
}

// managedUnlock loads all of the encrypted file structures into wallet memory. Even
// after loading, the structures are kept encrypted, but some data such as
// addresses are decrypted so that the wallet knows what to track.
func (w *Wallet) managedUnlock(masterKey crypto.TwofishKey) error {
	w.mu.RLock()
	unlocked := w.unlocked
	encrypted := w.encrypted
	w.mu.RUnlock()
	if unlocked {
		return errAlreadyUnlocked
	} else if !encrypted {
		return errUnencryptedWallet
	}

	// Load db objects into memory.
	var lastChange modules.ConsensusChangeID
	var primarySeedFile seedFile
	var primarySeedProgress uint64
	var auxiliarySeedFiles []seedFile
	var unseededKeyFiles []spendableKeyFile
	err := func() error {
		w.mu.Lock()
		defer w.mu.Unlock()

		// verify masterKey
		err := checkMasterKey(w.dbTx, masterKey)
		if err != nil {
			return err
		}

		// lastChange
		lastChange = dbGetConsensusChangeID(w.dbTx)

		// primarySeedFile + primarySeedProgress
		wb := w.dbTx.Bucket(bucketWallet)
		err = encoding.Unmarshal(wb.Get(keyPrimarySeedFile), &primarySeedFile)
		if err != nil {
			return err
		}
		err = encoding.Unmarshal(wb.Get(keyPrimarySeedProgress), &primarySeedProgress)
		if err != nil {
			return err
		}

		// auxiliarySeedFiles
		err = encoding.Unmarshal(wb.Get(keyAuxiliarySeedFiles), &auxiliarySeedFiles)
		if err != nil {
			return err
		}

		// unseededKeyFiles
		err = encoding.Unmarshal(wb.Get(keySpendableKeyFiles), &unseededKeyFiles)
		if err != nil {
			return err
		}

		return nil
	}()
	if err != nil {
		return err
	}

	// Decrypt + load keys.
	err = func() error {
		w.mu.Lock()
		defer w.mu.Unlock()

		// primarySeedFile
		primarySeed, err := decryptSeedFile(masterKey, primarySeedFile)
		if err != nil {
			return err
		}
		w.integrateSeed(primarySeed, primarySeedProgress)
		w.primarySeed = primarySeed

		// auxiliarySeedFiles
		for _, sf := range auxiliarySeedFiles {
			auxSeed, err := decryptSeedFile(masterKey, sf)
			if err != nil {
				return err
			}
			w.integrateSeed(auxSeed, modules.PublicKeysPerSeed)
			w.seeds = append(w.seeds, auxSeed)
		}

		// unseededKeyFiles
		for _, uk := range unseededKeyFiles {
			sk, err := decryptSpendableKeyFile(masterKey, uk)
			if err != nil {
				return err
			}
			w.integrateSpendableKey(masterKey, sk)
		}
		return nil
	}()
	if err != nil {
		return err
	}

	// Subscribe to the consensus set if this is the first unlock for the
	// wallet object.
	w.mu.RLock()
	subscribed := w.subscribed
	w.mu.RUnlock()
	if !subscribed {
		if primarySeedProgress == 0 {
			// If the wallet has not been used, we don't need to scan the entire
			// blockchain looking for relevant transactions. Instead, just grab
			// the current height and subscribe to future blocks.
			err = w.cs.ConsensusSetSubscribe(w, modules.ConsensusChangeRecent)
			if err != nil {
				return fmt.Errorf("wallet subscription failed: %v", err)
			}
			w.mu.Lock()
			err = dbPutConsensusHeight(w.dbTx, w.cs.Height())
			w.mu.Unlock()
			if err != nil {
				return fmt.Errorf("wallet subscription failed: %v", err)
			}
		} else {
			// If the wallet has been used, we need to scan the entire
			// blockchain. This can take a while, so spawn a goroutine to
			// print the wallet height every few seconds. (If subscription
			// completes quickly, nothing will be printed.)
			done := make(chan struct{})
			go w.rescanMessage(done)
			defer close(done)

			err = w.cs.ConsensusSetSubscribe(w, lastChange)
			if err == modules.ErrInvalidConsensusChangeID {
				// something went wrong; resubscribe from the beginning
				err = dbPutConsensusChangeID(w.dbTx, modules.ConsensusChangeBeginning)
				if err != nil {
					return fmt.Errorf("failed to reset db during rescan: %v", err)
				}
				err = dbPutConsensusHeight(w.dbTx, 0)
				if err != nil {
					return fmt.Errorf("failed to reset db during rescan: %v", err)
				}
				err = w.cs.ConsensusSetSubscribe(w, modules.ConsensusChangeBeginning)
			}
			if err != nil {
				return fmt.Errorf("wallet subscription failed: %v", err)
			}
		}
		// Subscribe to the transaction pool to receive unconfirmed
		// transactions.
		w.tpool.TransactionPoolSubscribe(w)
	}

	w.mu.Lock()
	w.unlocked = true
	w.subscribed = true
	w.mu.Unlock()
	return nil
}

// rescanMessage prints the blockheight every 3 seconds until done is closed.
func (w *Wallet) rescanMessage(done chan struct{}) {
	if build.Release == "testing" {
		return
	}

	// sleep first because we may not need to print a message at all if
	// done is closed quickly.
	select {
	case <-done:
		return
	case <-time.After(3 * time.Second):
	}

	for {
		w.mu.Lock()
		height, _ := dbGetConsensusHeight(w.dbTx)
		w.mu.Unlock()
		print("\rWallet: scanned to height ", height, "...")

		select {
		case <-done:
			println("\nDone!")
			return
		case <-time.After(3 * time.Second):
		}
	}
}

// wipeSecrets erases all of the seeds and secret keys in the wallet.
func (w *Wallet) wipeSecrets() {
	// 'for i := range' must be used to prevent copies of secret data from
	// being made.
	for i := range w.keys {
		for j := range w.keys[i].SecretKeys {
			crypto.SecureWipe(w.keys[i].SecretKeys[j][:])
		}
	}
	for i := range w.seeds {
		crypto.SecureWipe(w.seeds[i][:])
	}
	crypto.SecureWipe(w.primarySeed[:])
	w.seeds = w.seeds[:0]
}

// Encrypted returns whether or not the wallet has been encrypted.
func (w *Wallet) Encrypted() bool {
	w.mu.Lock()
	defer w.mu.Unlock()
	if build.DEBUG && w.unlocked && !w.encrypted {
		panic("wallet is both unlocked and unencrypted")
	}
	return w.encrypted
}

// Encrypt will create a primary seed for the wallet and encrypt it using
// masterKey. If masterKey is blank, then the hash of the primary seed will be
// used instead. The wallet will still be locked after Encrypt is called.
//
// Encrypt can only be called once throughout the life of the wallet, and will
// return an error on subsequent calls (even after restarting the wallet). To
// reset the wallet, the wallet files must be moved to a different directory
// or deleted.
func (w *Wallet) Encrypt(masterKey crypto.TwofishKey) (modules.Seed, error) {
	if err := w.tg.Add(); err != nil {
		return modules.Seed{}, err
	}
	defer w.tg.Done()
	w.mu.Lock()
	defer w.mu.Unlock()

	// Create a random seed.
	var seed modules.Seed
	fastrand.Read(seed[:])

	// If masterKey is blank, use the hash of the seed.
	if masterKey == (crypto.TwofishKey{}) {
		masterKey = crypto.TwofishKey(crypto.HashObject(seed))
	}

	return w.initEncryption(masterKey, seed)
}

// Reset will reset the wallet, clearing the database and returning it to
// the unencrypted state. Reset can only be called on a wallet that has
// already been encrypted.
func (w *Wallet) Reset() error {
	if err := w.tg.Add(); err != nil {
		return err
	}
	defer w.tg.Done()
	w.mu.Lock()
	defer w.mu.Unlock()

	wb := w.dbTx.Bucket(bucketWallet)
	if wb.Get(keyEncryptionVerification) == nil {
		return errUnencryptedWallet
	}

	w.cs.Unsubscribe(w)
	w.tpool.Unsubscribe(w)

	err := dbReset(w.dbTx)
	if err != nil {
		return err
	}
	w.wipeSecrets()
	w.keys = make(map[types.UnlockHash]spendableKey)
	w.seeds = []modules.Seed{}
	w.unconfirmedProcessedTransactions = []modules.ProcessedTransaction{}
	w.unlocked = false
	w.encrypted = false
	w.subscribed = false

	return nil
}

// InitFromSeed functions like Init, but using a specified seed. Unlike Init,
// the blockchain will be scanned to determine the seed's progress. For this
// reason, InitFromSeed should not be called until the blockchain is fully
// synced.
func (w *Wallet) InitFromSeed(masterKey crypto.TwofishKey, seed modules.Seed) error {
	if err := w.tg.Add(); err != nil {
		return err
	}
	defer w.tg.Done()

	if !w.cs.Synced() {
		return errors.New("cannot init from seed until blockchain is synced")
	}

	// If masterKey is blank, use the hash of the seed.
	if masterKey == (crypto.TwofishKey{}) {
		masterKey = crypto.TwofishKey(crypto.HashObject(seed))
	}

	w.mu.Lock()
	if _, err := w.initEncryption(masterKey, seed); err != nil {
		w.mu.Unlock()
		return err
	}
	w.mu.Unlock()

	// estimate the primarySeedProgress by scanning the blockchain
	s := newSeedScanner(seed, w.log)
	if err := s.scan(w.cs); err != nil {
		return err
	}
	// NOTE: each time the wallet generates a key for index n, it sets its
	// progress to n+1, so the progress should be the largest index seen + 1.
	// We also add 10% as a buffer because the seed may have addresses in the
	// wild that have not appeared in the blockchain yet.
	progress := s.largestIndexSeen + 1
	progress += progress / 10
	w.log.Printf("INFO: found key index %v in blockchain. Setting primary seed progress to %v", s.largestIndexSeen, progress)
	// set primarySeedProgress
	return dbPutPrimarySeedProgress(w.dbTx, uint64(progress))
}

// Unlocked indicates whether the wallet is locked or unlocked.
func (w *Wallet) Unlocked() bool {
	w.mu.RLock()
	defer w.mu.RUnlock()
	return w.unlocked
}

// Lock will erase all keys from memory and prevent the wallet from spending
// coins until it is unlocked.
func (w *Wallet) Lock() error {
	w.mu.Lock()
	defer w.mu.Unlock()
	if !w.unlocked {
		return modules.ErrLockedWallet
	}
	w.log.Println("INFO: Locking wallet.")

	// Wipe all of the seeds and secret keys. They will be replaced upon
	// calling 'Unlock' again. Note that since the public keys are not wiped,
	// we can continue processing blocks.
	w.wipeSecrets()
	w.unlocked = false
	return nil
}

// Unlock will decrypt the wallet seed and load all of the addresses into
// memory.
func (w *Wallet) Unlock(masterKey crypto.TwofishKey) error {
	// By having the wallet's ThreadGroup track the Unlock method, we ensure
	// that Unlock will never unlock the wallet once the ThreadGroup has been
	// stopped. Without this precaution, the wallet's Close method would be
	// unsafe because it would theoretically be possible for another function
	// to Unlock the wallet in the short interval after Close calls w.Lock
	// and before Close calls w.mu.Lock.
	if err := w.tg.Add(); err != nil {
		return err
	}
	defer w.tg.Done()
	w.log.Println("INFO: Unlocking wallet.")
	return w.managedUnlock(masterKey)
}
<commit_msg>Revert "skip needless work when unlocking fresh wallet"<commit_after>package wallet

import (
	"bytes"
	"errors"
	"fmt"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
	"github.com/NebulousLabs/bolt"
	"github.com/NebulousLabs/fastrand"
)

var (
	errAlreadyUnlocked   = errors.New("wallet has already been unlocked")
	errReencrypt         = errors.New("wallet is already encrypted, cannot encrypt again")
	errUnencryptedWallet = errors.New("wallet has not been encrypted yet")

	// verificationPlaintext is the plaintext used to verify encryption keys.
	// By storing the corresponding ciphertext for a given key, we can later
	// verify that a key is correct by using it to decrypt the ciphertext and
	// comparing the result to verificationPlaintext.
	verificationPlaintext = make([]byte, 32)
)

// uidEncryptionKey creates an encryption key that is used to decrypt a
// specific key file.
func uidEncryptionKey(masterKey crypto.TwofishKey, uid uniqueID) crypto.TwofishKey {
	return crypto.TwofishKey(crypto.HashAll(masterKey, uid))
}

// verifyEncryption verifies that key properly decrypts the ciphertext to a
// preset plaintext.
func verifyEncryption(key crypto.TwofishKey, encrypted crypto.Ciphertext) error {
	verification, err := key.DecryptBytes(encrypted)
	if err != nil {
		return modules.ErrBadEncryptionKey
	}
	if !bytes.Equal(verificationPlaintext, verification) {
		return modules.ErrBadEncryptionKey
	}
	return nil
}

// checkMasterKey verifies that the masterKey is the key used to encrypt the wallet.
func checkMasterKey(tx *bolt.Tx, masterKey crypto.TwofishKey) error {
	uk := uidEncryptionKey(masterKey, dbGetWalletUID(tx))
	encryptedVerification := tx.Bucket(bucketWallet).Get(keyEncryptionVerification)
	return verifyEncryption(uk, encryptedVerification)
}

// initEncryption initializes and encrypts the primary SeedFile.
func (w *Wallet) initEncryption(masterKey crypto.TwofishKey, seed modules.Seed) (modules.Seed, error) {
	wb := w.dbTx.Bucket(bucketWallet)
	// Check if the wallet encryption key has already been set.
	if wb.Get(keyEncryptionVerification) != nil {
		return modules.Seed{}, errReencrypt
	}

	// create a seedFile for the seed
	sf := createSeedFile(masterKey, seed)

	// set this as the primary seedFile
	err := wb.Put(keyPrimarySeedFile, encoding.Marshal(sf))
	if err != nil {
		return modules.Seed{}, err
	}
	err = wb.Put(keyPrimarySeedProgress, encoding.Marshal(uint64(0)))
	if err != nil {
		return modules.Seed{}, err
	}

	// Establish the encryption verification using the masterKey. After this
	// point, the wallet is encrypted.
	uk := uidEncryptionKey(masterKey, dbGetWalletUID(w.dbTx))
	err = wb.Put(keyEncryptionVerification, uk.EncryptBytes(verificationPlaintext))
	if err != nil {
		return modules.Seed{}, err
	}

	// on future startups, this field will be set by w.initPersist
	w.encrypted = true

	return seed, nil
}

// managedUnlock loads all of the encrypted file structures into wallet memory. Even
// after loading, the structures are kept encrypted, but some data such as
// addresses are decrypted so that the wallet knows what to track.
func (w *Wallet) managedUnlock(masterKey crypto.TwofishKey) error {
	w.mu.RLock()
	unlocked := w.unlocked
	encrypted := w.encrypted
	w.mu.RUnlock()
	if unlocked {
		return errAlreadyUnlocked
	} else if !encrypted {
		return errUnencryptedWallet
	}

	// Load db objects into memory.
	var lastChange modules.ConsensusChangeID
	var primarySeedFile seedFile
	var primarySeedProgress uint64
	var auxiliarySeedFiles []seedFile
	var unseededKeyFiles []spendableKeyFile
	err := func() error {
		w.mu.Lock()
		defer w.mu.Unlock()

		// verify masterKey
		err := checkMasterKey(w.dbTx, masterKey)
		if err != nil {
			return err
		}

		// lastChange
		lastChange = dbGetConsensusChangeID(w.dbTx)

		// primarySeedFile + primarySeedProgress
		wb := w.dbTx.Bucket(bucketWallet)
		err = encoding.Unmarshal(wb.Get(keyPrimarySeedFile), &primarySeedFile)
		if err != nil {
			return err
		}
		err = encoding.Unmarshal(wb.Get(keyPrimarySeedProgress), &primarySeedProgress)
		if err != nil {
			return err
		}

		// auxiliarySeedFiles
		err = encoding.Unmarshal(wb.Get(keyAuxiliarySeedFiles), &auxiliarySeedFiles)
		if err != nil {
			return err
		}

		// unseededKeyFiles
		err = encoding.Unmarshal(wb.Get(keySpendableKeyFiles), &unseededKeyFiles)
		if err != nil {
			return err
		}

		return nil
	}()
	if err != nil {
		return err
	}

	// Decrypt + load keys.
	err = func() error {
		w.mu.Lock()
		defer w.mu.Unlock()

		// primarySeedFile
		primarySeed, err := decryptSeedFile(masterKey, primarySeedFile)
		if err != nil {
			return err
		}
		w.integrateSeed(primarySeed, primarySeedProgress)
		w.primarySeed = primarySeed

		// auxiliarySeedFiles
		for _, sf := range auxiliarySeedFiles {
			auxSeed, err := decryptSeedFile(masterKey, sf)
			if err != nil {
				return err
			}
			w.integrateSeed(auxSeed, modules.PublicKeysPerSeed)
			w.seeds = append(w.seeds, auxSeed)
		}

		// unseededKeyFiles
		for _, uk := range unseededKeyFiles {
			sk, err := decryptSpendableKeyFile(masterKey, uk)
			if err != nil {
				return err
			}
			w.integrateSpendableKey(masterKey, sk)
		}
		return nil
	}()
	if err != nil {
		return err
	}

	// Subscribe to the consensus set if this is the first unlock for the
	// wallet object.
	w.mu.RLock()
	subscribed := w.subscribed
	w.mu.RUnlock()
	if !subscribed {
		// Subscription can take a while, so spawn a goroutine to print the
		// wallet height every few seconds. (If subscription completes
		// quickly, nothing will be printed.)
		done := make(chan struct{})
		go w.rescanMessage(done)
		defer close(done)

		err = w.cs.ConsensusSetSubscribe(w, lastChange)
		if err == modules.ErrInvalidConsensusChangeID {
			// something went wrong; resubscribe from the beginning
			err = dbPutConsensusChangeID(w.dbTx, modules.ConsensusChangeBeginning)
			if err != nil {
				return fmt.Errorf("failed to reset db during rescan: %v", err)
			}
			err = dbPutConsensusHeight(w.dbTx, 0)
			if err != nil {
				return fmt.Errorf("failed to reset db during rescan: %v", err)
			}
			err = w.cs.ConsensusSetSubscribe(w, modules.ConsensusChangeBeginning)
		}
		if err != nil {
			return fmt.Errorf("wallet subscription failed: %v", err)
		}
		w.tpool.TransactionPoolSubscribe(w)
	}

	w.mu.Lock()
	w.unlocked = true
	w.subscribed = true
	w.mu.Unlock()
	return nil
}

// rescanMessage prints the blockheight every 3 seconds until done is closed.
func (w *Wallet) rescanMessage(done chan struct{}) {
	if build.Release == "testing" {
		return
	}

	// sleep first because we may not need to print a message at all if
	// done is closed quickly.
	select {
	case <-done:
		return
	case <-time.After(3 * time.Second):
	}

	for {
		w.mu.Lock()
		height, _ := dbGetConsensusHeight(w.dbTx)
		w.mu.Unlock()
		print("\rWallet: scanned to height ", height, "...")

		select {
		case <-done:
			println("\nDone!")
			return
		case <-time.After(3 * time.Second):
		}
	}
}

// wipeSecrets erases all of the seeds and secret keys in the wallet.
func (w *Wallet) wipeSecrets() {
	// 'for i := range' must be used to prevent copies of secret data from
	// being made.
	for i := range w.keys {
		for j := range w.keys[i].SecretKeys {
			crypto.SecureWipe(w.keys[i].SecretKeys[j][:])
		}
	}
	for i := range w.seeds {
		crypto.SecureWipe(w.seeds[i][:])
	}
	crypto.SecureWipe(w.primarySeed[:])
	w.seeds = w.seeds[:0]
}

// Encrypted returns whether or not the wallet has been encrypted.
func (w *Wallet) Encrypted() bool {
	w.mu.Lock()
	defer w.mu.Unlock()
	if build.DEBUG && w.unlocked && !w.encrypted {
		panic("wallet is both unlocked and unencrypted")
	}
	return w.encrypted
}

// Encrypt will create a primary seed for the wallet and encrypt it using
// masterKey. If masterKey is blank, then the hash of the primary seed will be
// used instead. The wallet will still be locked after Encrypt is called.
//
// Encrypt can only be called once throughout the life of the wallet, and will
// return an error on subsequent calls (even after restarting the wallet). To
// reset the wallet, the wallet files must be moved to a different directory
// or deleted.
func (w *Wallet) Encrypt(masterKey crypto.TwofishKey) (modules.Seed, error) {
	if err := w.tg.Add(); err != nil {
		return modules.Seed{}, err
	}
	defer w.tg.Done()
	w.mu.Lock()
	defer w.mu.Unlock()

	// Create a random seed.
	var seed modules.Seed
	fastrand.Read(seed[:])

	// If masterKey is blank, use the hash of the seed.
	if masterKey == (crypto.TwofishKey{}) {
		masterKey = crypto.TwofishKey(crypto.HashObject(seed))
	}

	return w.initEncryption(masterKey, seed)
}

// Reset will reset the wallet, clearing the database and returning it to
// the unencrypted state. Reset can only be called on a wallet that has
// already been encrypted.
func (w *Wallet) Reset() error {
	if err := w.tg.Add(); err != nil {
		return err
	}
	defer w.tg.Done()
	w.mu.Lock()
	defer w.mu.Unlock()

	wb := w.dbTx.Bucket(bucketWallet)
	if wb.Get(keyEncryptionVerification) == nil {
		return errUnencryptedWallet
	}

	w.cs.Unsubscribe(w)
	w.tpool.Unsubscribe(w)

	err := dbReset(w.dbTx)
	if err != nil {
		return err
	}
	w.wipeSecrets()
	w.keys = make(map[types.UnlockHash]spendableKey)
	w.seeds = []modules.Seed{}
	w.unconfirmedProcessedTransactions = []modules.ProcessedTransaction{}
	w.unlocked = false
	w.encrypted = false
	w.subscribed = false

	return nil
}

// InitFromSeed functions like Init, but using a specified seed. Unlike Init,
// the blockchain will be scanned to determine the seed's progress. For this
// reason, InitFromSeed should not be called until the blockchain is fully
// synced.
func (w *Wallet) InitFromSeed(masterKey crypto.TwofishKey, seed modules.Seed) error {
	if err := w.tg.Add(); err != nil {
		return err
	}
	defer w.tg.Done()

	if !w.cs.Synced() {
		return errors.New("cannot init from seed until blockchain is synced")
	}

	// If masterKey is blank, use the hash of the seed.
	if masterKey == (crypto.TwofishKey{}) {
		masterKey = crypto.TwofishKey(crypto.HashObject(seed))
	}

	w.mu.Lock()
	if _, err := w.initEncryption(masterKey, seed); err != nil {
		w.mu.Unlock()
		return err
	}
	w.mu.Unlock()

	// estimate the primarySeedProgress by scanning the blockchain
	s := newSeedScanner(seed, w.log)
	if err := s.scan(w.cs); err != nil {
		return err
	}
	// NOTE: each time the wallet generates a key for index n, it sets its
	// progress to n+1, so the progress should be the largest index seen + 1.
	// We also add 10% as a buffer because the seed may have addresses in the
	// wild that have not appeared in the blockchain yet.
	progress := s.largestIndexSeen + 1
	progress += progress / 10
	w.log.Printf("INFO: found key index %v in blockchain. Setting primary seed progress to %v", s.largestIndexSeen, progress)
	// set primarySeedProgress
	return dbPutPrimarySeedProgress(w.dbTx, uint64(progress))
}

// Unlocked indicates whether the wallet is locked or unlocked.
func (w *Wallet) Unlocked() bool {
	w.mu.RLock()
	defer w.mu.RUnlock()
	return w.unlocked
}

// Lock will erase all keys from memory and prevent the wallet from spending
// coins until it is unlocked.
func (w *Wallet) Lock() error {
	w.mu.Lock()
	defer w.mu.Unlock()
	if !w.unlocked {
		return modules.ErrLockedWallet
	}
	w.log.Println("INFO: Locking wallet.")

	// Wipe all of the seeds and secret keys. They will be replaced upon
	// calling 'Unlock' again. Note that since the public keys are not wiped,
	// we can continue processing blocks.
	w.wipeSecrets()
	w.unlocked = false
	return nil
}

// Unlock will decrypt the wallet seed and load all of the addresses into
// memory.
func (w *Wallet) Unlock(masterKey crypto.TwofishKey) error {
	// By having the wallet's ThreadGroup track the Unlock method, we ensure
	// that Unlock will never unlock the wallet once the ThreadGroup has been
	// stopped. Without this precaution, the wallet's Close method would be
	// unsafe because it would theoretically be possible for another function
	// to Unlock the wallet in the short interval after Close calls w.Lock
	// and before Close calls w.mu.Lock.
	if err := w.tg.Add(); err != nil {
		return err
	}
	defer w.tg.Done()
	w.log.Println("INFO: Unlocking wallet.")

	// Initialize all of the keys in the wallet under a lock. While holding the
	// lock, also grab the subscriber status.
	return w.managedUnlock(masterKey)
}
<|endoftext|>