<commit_before>package tour

import "sort"

func init() {
	for _, t := range allTopics {
		Topics[t.ID] = t
		IDs = append(IDs, t.ID)
	}

	sort.Sort(IDSlice(IDs))
}

// TODO move content into individual files if desired

// TODO(brian): If sub-topics are needed, write recursively (as tree comprised
// of Section nodes:
//
// type Section interface {
// 	Sections() []Section
// 	Topic() Topic
// }

var (
	Introduction = Chapter(0)
	FileBasics   = Chapter(1)
	NodeBasics   = Chapter(2)
	MerkleDag    = Chapter(3)
	Network      = Chapter(4)
	Daemon       = Chapter(5)
	Routing      = Chapter(6)
	Exchange     = Chapter(7)
	Ipns         = Chapter(8)
	Mounting     = Chapter(9)
	Plumbing     = Chapter(10)
	Formats      = Chapter(11)
)

// Topics contains a mapping of Tour Topic ID to Topic
var allTopics = []Topic{
	Topic{ID: Introduction(0), Content: IntroHelloMars},
	Topic{ID: Introduction(1), Content: IntroTour},
	Topic{ID: Introduction(2), Content: IntroAboutIpfs},

	Topic{ID: FileBasics(1), Content: FileBasicsFilesystem},
	Topic{ID: FileBasics(2), Content: FileBasicsGetting},
	Topic{ID: FileBasics(3), Content: FileBasicsAdding},
	Topic{ID: FileBasics(4), Content: FileBasicsDirectories},
	Topic{ID: FileBasics(5), Content: FileBasicsDistributed},
	Topic{ID: FileBasics(6), Content: FileBasicsMounting},

	Topic{NodeBasics(0), NodeBasicsInit},
	Topic{NodeBasics(1), NodeBasicsHelp},
	Topic{NodeBasics(2), NodeBasicsUpdate},
	Topic{NodeBasics(3), NodeBasicsConfig},

	Topic{MerkleDag(0), MerkleDagIntro},
	Topic{MerkleDag(1), MerkleDagContentAddressing},
	Topic{MerkleDag(2), MerkleDagContentAddressingLinks},
	Topic{MerkleDag(3), MerkleDagRedux},
	Topic{MerkleDag(4), MerkleDagIpfsObjects},
	Topic{MerkleDag(5), MerkleDagIpfsPaths},
	Topic{MerkleDag(6), MerkleDagImmutability},
	Topic{MerkleDag(7), MerkleDagUseCaseUnixFS},
	Topic{MerkleDag(8), MerkleDagUseCaseGitObjects},
	Topic{MerkleDag(9), MerkleDagUseCaseOperationalTransforms},

	Topic{Network(0), Network_Intro},
	Topic{Network(1), Network_Ipfs_Peers},
	Topic{Network(2), Network_Daemon},
	Topic{Network(3), Network_Routing},
	Topic{Network(4), Network_Exchange},
	Topic{Network(5), Network_Intro},

	Topic{Daemon(0), Daemon_Intro},
	Topic{Daemon(1), Daemon_Running_Commands},
	Topic{Daemon(2), Daemon_Web_UI},

	Topic{Routing(0), Routing_Intro},
	Topic{Routing(1), Rouing_Interface},
	Topic{Routing(2), Routing_Resolving},
	Topic{Routing(3), Routing_DHT},
	Topic{Routing(4), Routing_Other},
}

// Introduction

var IntroHelloMars = Content{
	Title: "Hello Mars",
	Text: `
	check things work
	`,
}
var IntroTour = Content{
	Title: "Hello Mars",
	Text: `
	how this works
	`,
}
var IntroAboutIpfs = Content{
	Title: "About IPFS",
}

// File Basics

var FileBasicsFilesystem = Content{
	Title: "Filesystem",
	Text: `
	`,
}
var FileBasicsGetting = Content{
	Title: "Getting Files",
	Text: `ipfs cat
	`,
}
var FileBasicsAdding = Content{
	Title: "Adding Files",
	Text: `ipfs add
	`,
}
var FileBasicsDirectories = Content{
	Title: "Directories",
	Text: `ipfs ls
	`,
}
var FileBasicsDistributed = Content{
	Title: "Distributed",
	Text: `ipfs cat from mars
	`,
}
var FileBasicsMounting = Content{
	Title: "Getting Files",
	Text: `ipfs mount (simple)
	`,
}

// Node Basics

var NodeBasicsInit = Content{
	Title: "Basics - init",
	Text: `
	`,
}
var NodeBasicsHelp = Content{
	Title: "Basics - help",
	Text: `
	`,
}
var NodeBasicsUpdate = Content{
	Title: "Basics - update",
	Text: `
	`,
}
var NodeBasicsConfig = Content{
	Title: "Basics - config",
	Text: `
	`,
}

// Merkle DAG
var MerkleDagIntro = Content{}
var MerkleDagContentAddressing = Content{}
var MerkleDagContentAddressingLinks = Content{}
var MerkleDagRedux = Content{}
var MerkleDagIpfsObjects = Content{}
var MerkleDagIpfsPaths = Content{}
var MerkleDagImmutability = Content{}
var MerkleDagUseCaseUnixFS = Content{}
var MerkleDagUseCaseGitObjects = Content{}
var MerkleDagUseCaseOperationalTransforms = Content{}

var Network_Intro = Content{}
var Network_Ipfs_Peers = Content{}
var Network_Daemon = Content{}
var Network_Routing = Content{}
var Network_Exchange = Content{}
var Network_Naming = Content{}

var Daemon_Intro = Content{}
var Daemon_Running_Commands = Content{}
var Daemon_Web_UI = Content{}

var Routing_Intro = Content{}
var Rouing_Interface = Content{}
var Routing_Resolving = Content{}
var Routing_DHT = Content{}
var Routing_Other = Content{}
<commit_msg>feat(tour) exchange, ipns<commit_after>package tour

import "sort"

func init() {
	for _, t := range allTopics {
		Topics[t.ID] = t
		IDs = append(IDs, t.ID)
	}

	sort.Sort(IDSlice(IDs))
}

// TODO move content into individual files if desired

// TODO(brian): If sub-topics are needed, write recursively (as tree comprised
// of Section nodes:
//
// type Section interface {
// 	Sections() []Section
// 	Topic() Topic
// }

var (
	Introduction = Chapter(0)
	FileBasics   = Chapter(1)
	NodeBasics   = Chapter(2)
	MerkleDag    = Chapter(3)
	Network      = Chapter(4)
	Daemon       = Chapter(5)
	Routing      = Chapter(6)
	Exchange     = Chapter(7)
	Ipns         = Chapter(8)
	Mounting     = Chapter(9)
	Plumbing     = Chapter(10)
	Formats      = Chapter(11)
)

// Topics contains a mapping of Tour Topic ID to Topic
var allTopics = []Topic{
	Topic{ID: Introduction(0), Content: IntroHelloMars},
	Topic{ID: Introduction(1), Content: IntroTour},
	Topic{ID: Introduction(2), Content: IntroAboutIpfs},

	Topic{ID: FileBasics(1), Content: FileBasicsFilesystem},
	Topic{ID: FileBasics(2), Content: FileBasicsGetting},
	Topic{ID: FileBasics(3), Content: FileBasicsAdding},
	Topic{ID: FileBasics(4), Content: FileBasicsDirectories},
	Topic{ID: FileBasics(5), Content: FileBasicsDistributed},
	Topic{ID: FileBasics(6), Content: FileBasicsMounting},

	Topic{NodeBasics(0), NodeBasicsInit},
	Topic{NodeBasics(1), NodeBasicsHelp},
	Topic{NodeBasics(2), NodeBasicsUpdate},
	Topic{NodeBasics(3), NodeBasicsConfig},

	Topic{MerkleDag(0), MerkleDagIntro},
	Topic{MerkleDag(1), MerkleDagContentAddressing},
	Topic{MerkleDag(2), MerkleDagContentAddressingLinks},
	Topic{MerkleDag(3), MerkleDagRedux},
	Topic{MerkleDag(4), MerkleDagIpfsObjects},
	Topic{MerkleDag(5), MerkleDagIpfsPaths},
	Topic{MerkleDag(6), MerkleDagImmutability},
	Topic{MerkleDag(7), MerkleDagUseCaseUnixFS},
	Topic{MerkleDag(8), MerkleDagUseCaseGitObjects},
	Topic{MerkleDag(9), MerkleDagUseCaseOperationalTransforms},

	Topic{Network(0), Network_Intro},
	Topic{Network(1), Network_Ipfs_Peers},
	Topic{Network(2), Network_Daemon},
	Topic{Network(3), Network_Routing},
	Topic{Network(4), Network_Exchange},
	Topic{Network(5), Network_Intro},

	Topic{Daemon(0), Daemon_Intro},
	Topic{Daemon(1), Daemon_Running_Commands},
	Topic{Daemon(2), Daemon_Web_UI},

	Topic{Routing(0), Routing_Intro},
	Topic{Routing(1), Rouing_Interface},
	Topic{Routing(2), Routing_Resolving},
	Topic{Routing(3), Routing_DHT},
	Topic{Routing(4), Routing_Other},

	Topic{Exchange(0), Exchange_Intro},
	Topic{Exchange(1), Exchange_Getting_Blocks},
	Topic{Exchange(2), Exchange_Strategies},
	Topic{Exchange(3), Exchange_Bitswap},

	Topic{Ipns(0), Ipns_Name_System},
	Topic{Ipns(1), Ipns_Mutability},
	Topic{Ipns(2), Ipns_PKI_Review},
	Topic{Ipns(3), Ipns_Publishing},
	Topic{Ipns(4), Ipns_Resolving},
	Topic{Ipns(5), Ipns_Consistency},
	Topic{Ipns(6), Ipns_Records_Etc},
}

// Introduction

var IntroHelloMars = Content{
	Title: "Hello Mars",
	Text: `
	check things work
	`,
}
var IntroTour = Content{
	Title: "Hello Mars",
	Text: `
	how this works
	`,
}
var IntroAboutIpfs = Content{
	Title: "About IPFS",
}

// File Basics

var FileBasicsFilesystem = Content{
	Title: "Filesystem",
	Text: `
	`,
}
var FileBasicsGetting = Content{
	Title: "Getting Files",
	Text: `ipfs cat
	`,
}
var FileBasicsAdding = Content{
	Title: "Adding Files",
	Text: `ipfs add
	`,
}
var FileBasicsDirectories = Content{
	Title: "Directories",
	Text: `ipfs ls
	`,
}
var FileBasicsDistributed = Content{
	Title: "Distributed",
	Text: `ipfs cat from mars
	`,
}
var FileBasicsMounting = Content{
	Title: "Getting Files",
	Text: `ipfs mount (simple)
	`,
}

// Node Basics

var NodeBasicsInit = Content{
	Title: "Basics - init",
	Text: `
	`,
}
var NodeBasicsHelp = Content{
	Title: "Basics - help",
	Text: `
	`,
}
var NodeBasicsUpdate = Content{
	Title: "Basics - update",
	Text: `
	`,
}
var NodeBasicsConfig = Content{
	Title: "Basics - config",
	Text: `
	`,
}

// Merkle DAG
var MerkleDagIntro = Content{}
var MerkleDagContentAddressing = Content{}
var MerkleDagContentAddressingLinks = Content{}
var MerkleDagRedux = Content{}
var MerkleDagIpfsObjects = Content{}
var MerkleDagIpfsPaths = Content{}
var MerkleDagImmutability = Content{}
var MerkleDagUseCaseUnixFS = Content{}
var MerkleDagUseCaseGitObjects = Content{}
var MerkleDagUseCaseOperationalTransforms = Content{}

var Network_Intro = Content{}
var Network_Ipfs_Peers = Content{}
var Network_Daemon = Content{}
var Network_Routing = Content{}
var Network_Exchange = Content{}
var Network_Naming = Content{}

var Daemon_Intro = Content{}
var Daemon_Running_Commands = Content{}
var Daemon_Web_UI = Content{}

var Routing_Intro = Content{}
var Rouing_Interface = Content{}
var Routing_Resolving = Content{}
var Routing_DHT = Content{}
var Routing_Other = Content{}

var Exchange_Intro = Content{}
var Exchange_Bitswap = Content{}
var Exchange_Strategies = Content{}
var Exchange_Getting_Blocks = Content{}

var Ipns_Consistency = Content{}
var Ipns_Mutability = Content{}
var Ipns_Name_System = Content{}
var Ipns_PKI_Review = Content{}
var Ipns_Publishing = Content{}
var Ipns_Records_Etc = Content{}
var Ipns_Resolving = Content{}
<|endoftext|>


<commit_before>package utils

import (
	"bytes"
	crand "crypto/rand"
	"errors"
	"github.com/stretchr/testify/assert"
	"image"
	"image/color"
	"image/jpeg"
	"image/png"
	"io"
	"math/rand"
	"mime/multipart"
	"net/http"
	"testing"

	"github.com/eirka/eirka-libs/config"
)

func testPng(size int) *bytes.Buffer {

	output := new(bytes.Buffer)

	myimage := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{size, size}})

	// This loop just fills the image with random data
	for x := 0; x < size; x++ {
		for y := 0; y < size; y++ {
			c := color.RGBA{uint8(rand.Intn(255)), uint8(rand.Intn(255)), uint8(rand.Intn(255)), 255}
			myimage.Set(x, y, c)
		}
	}

	png.Encode(output, myimage)

	return output
}

func testJpeg(size int) []byte {

	output := new(bytes.Buffer)

	myimage := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{size, size}})

	// This loop just fills the image with random data
	for x := 0; x < size; x++ {
		for y := 0; y < size; y++ {
			c := color.RGBA{uint8(rand.Intn(255)), uint8(rand.Intn(255)), uint8(rand.Intn(255)), 255}
			myimage.Set(x, y, c)
		}
	}

	jpeg.Encode(output, myimage, nil)

	return output
}

func testRandom() []byte {
	bytes := make([]byte, 20000)

	if _, err := io.ReadFull(crand.Reader, bytes); err != nil {
		panic(err)
	}

	return bytes
}

func formJpegRequest(size int, filename string) *http.Request {

	var b bytes.Buffer

	w := multipart.NewWriter(&b)

	fw, _ := w.CreateFormFile("file", filename)

	io.Copy(fw, bytes.NewReader(testJpeg(size)))

	w.Close()

	req, _ := http.NewRequest("POST", "/reply", &b)
	req.Header.Set("Content-Type", w.FormDataContentType())

	return req
}

func formRandomRequest(filename string) *http.Request {

	var b bytes.Buffer

	w := multipart.NewWriter(&b)

	fw, _ := w.CreateFormFile("file", filename)

	io.Copy(fw, bytes.NewReader(testRandom()))

	w.Close()

	req, _ := http.NewRequest("POST", "/reply", &b)
	req.Header.Set("Content-Type", w.FormDataContentType())

	return req
}

func TestIsAllowedExt(t *testing.T) {

	assert.False(t, isAllowedExt(".png.exe"), "Should not be allowed")

	assert.False(t, isAllowedExt(".exe.png"), "Should not be allowed")

	assert.False(t, isAllowedExt(""), "Should not be allowed")

	assert.False(t, isAllowedExt("."), "Should not be allowed")

	assert.False(t, isAllowedExt(".pdf"), "Should not be allowed")

	assert.True(t, isAllowedExt(".jpg"), "Should be allowed")

	assert.True(t, isAllowedExt(".JPEG"), "Should be allowed")

}

func TestCheckReqGoodExt(t *testing.T) {

	var err error

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err = img.checkReqExt()
	assert.NoError(t, err, "An error was not expected")

}

func TestCheckReqBadExt(t *testing.T) {

	req := formJpegRequest(300, "test.crap")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("format not supported"), "Error should match")
	}

}

func TestCheckReqBadExtExploit1(t *testing.T) {

	req := formRandomRequest("test.exe.png")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	assert.NoError(t, err, "An error was not expected")

	err = img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("unknown file type"), "Error should match")
	}

}

func TestCheckReqBadExtExploit2(t *testing.T) {

	req := formRandomRequest("test.png.exe")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("format not supported"), "Error should match")
	}

}

func TestCheckReqNoExt(t *testing.T) {

	req := formJpegRequest(300, "test")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("no file extension"), "Error should match")
	}

}

func TestGetMD5(t *testing.T) {

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

}

func TestCheckMagicGood(t *testing.T) {

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.NoError(t, err, "An error was not expected") {
		assert.Equal(t, img.mime, "image/jpeg", "Mime type should be the same")
	}

}

func TestCheckMagicBad(t *testing.T) {

	req := formRandomRequest("test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("unknown file type"), "Error should match")
	}

}

func TestGetStatsGoodPng(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000000

	img := ImageType{}

	img.image = testPng(400)

	err := img.getStats()
	assert.NoError(t, err, "An error was not expected")

}

func TestGetStatsGoodJpeg(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000000

	img := ImageType{}

	img.image = testJpeg(400)

	err := img.getStats()
	assert.NoError(t, err, "An error was not expected")

}

func TestGetStatsBadSize(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000

	img := ImageType{}

	img.image = testPng(400)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image size too large"), "Error should match")
	}

}

func TestGetStatsBadMin(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 300000

	img := ImageType{}

	img.image = testPng(50)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image width too small"), "Error should match")
	}

}

func TestGetStatsBadMax(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 300000

	img := ImageType{}

	img.image = testPng(1200)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image width too large"), "Error should match")
	}

}

func TestMakeFilenames(t *testing.T) {

	img := ImageType{}

	img.makeFilenames()

	assert.NotEmpty(t, img.Filename, "Filename should be returned")

	assert.NotEmpty(t, img.Thumbnail, "Thumbnail name should be returned")

}
<commit_msg>add tests and change image functions a bit<commit_after>package utils

import (
	"bytes"
	crand "crypto/rand"
	"errors"
	"github.com/stretchr/testify/assert"
	"image"
	"image/color"
	"image/jpeg"
	"image/png"
	"io"
	"math/rand"
	"mime/multipart"
	"net/http"
	"testing"

	"github.com/eirka/eirka-libs/config"
)

func testPng(size int) *bytes.Buffer {

	output := new(bytes.Buffer)

	myimage := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{size, size}})

	// This loop just fills the image with random data
	for x := 0; x < size; x++ {
		for y := 0; y < size; y++ {
			c := color.RGBA{uint8(rand.Intn(255)), uint8(rand.Intn(255)), uint8(rand.Intn(255)), 255}
			myimage.Set(x, y, c)
		}
	}

	png.Encode(output, myimage)

	return output
}

func testJpeg(size int) *bytes.Buffer {

	output := new(bytes.Buffer)

	myimage := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{size, size}})

	// This loop just fills the image with random data
	for x := 0; x < size; x++ {
		for y := 0; y < size; y++ {
			c := color.RGBA{uint8(rand.Intn(255)), uint8(rand.Intn(255)), uint8(rand.Intn(255)), 255}
			myimage.Set(x, y, c)
		}
	}

	jpeg.Encode(output, myimage, nil)

	return output
}

func testRandom() []byte {
	bytes := make([]byte, 20000)

	if _, err := io.ReadFull(crand.Reader, bytes); err != nil {
		panic(err)
	}

	return bytes
}

func formJpegRequest(size int, filename string) *http.Request {

	var b bytes.Buffer

	w := multipart.NewWriter(&b)

	fw, _ := w.CreateFormFile("file", filename)

	io.Copy(fw, bytes.NewReader(testJpeg(size)))

	w.Close()

	req, _ := http.NewRequest("POST", "/reply", &b)
	req.Header.Set("Content-Type", w.FormDataContentType())

	return req
}

func formRandomRequest(filename string) *http.Request {

	var b bytes.Buffer

	w := multipart.NewWriter(&b)

	fw, _ := w.CreateFormFile("file", filename)

	io.Copy(fw, bytes.NewReader(testRandom()))

	w.Close()

	req, _ := http.NewRequest("POST", "/reply", &b)
	req.Header.Set("Content-Type", w.FormDataContentType())

	return req
}

func TestIsAllowedExt(t *testing.T) {

	assert.False(t, isAllowedExt(".png.exe"), "Should not be allowed")

	assert.False(t, isAllowedExt(".exe.png"), "Should not be allowed")

	assert.False(t, isAllowedExt(""), "Should not be allowed")

	assert.False(t, isAllowedExt("."), "Should not be allowed")

	assert.False(t, isAllowedExt(".pdf"), "Should not be allowed")

	assert.True(t, isAllowedExt(".jpg"), "Should be allowed")

	assert.True(t, isAllowedExt(".JPEG"), "Should be allowed")

}

func TestCheckReqGoodExt(t *testing.T) {

	var err error

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err = img.checkReqExt()
	assert.NoError(t, err, "An error was not expected")

}

func TestCheckReqBadExt(t *testing.T) {

	req := formJpegRequest(300, "test.crap")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("format not supported"), "Error should match")
	}

}

func TestCheckReqBadExtExploit1(t *testing.T) {

	req := formRandomRequest("test.exe.png")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	assert.NoError(t, err, "An error was not expected")

	err = img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("unknown file type"), "Error should match")
	}

}

func TestCheckReqBadExtExploit2(t *testing.T) {

	req := formRandomRequest("test.png.exe")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("format not supported"), "Error should match")
	}

}

func TestCheckReqNoExt(t *testing.T) {

	req := formJpegRequest(300, "test")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.checkReqExt()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("no file extension"), "Error should match")
	}

}

func TestGetMD5(t *testing.T) {

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

}

func TestCheckMagicGood(t *testing.T) {

	req := formJpegRequest(300, "test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.NoError(t, err, "An error was not expected") {
		assert.Equal(t, img.mime, "image/jpeg", "Mime type should be the same")
	}

}

func TestCheckMagicBad(t *testing.T) {

	req := formRandomRequest("test.jpeg")

	img := ImageType{}

	img.File, img.Header, _ = req.FormFile("file")

	err := img.getMD5()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, img.MD5, "MD5 should be returned")
	}

	err = img.checkMagic()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("unknown file type"), "Error should match")
	}

}

func TestGetStatsGoodPng(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000000

	img := ImageType{}

	img.image = testPng(400)

	err := img.getStats()
	assert.NoError(t, err, "An error was not expected")

}

func TestGetStatsGoodJpeg(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000000

	img := ImageType{}

	img.image = testJpeg(400)

	err := img.getStats()
	assert.NoError(t, err, "An error was not expected")

}

func TestGetStatsBadSize(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 3000

	img := ImageType{}

	img.image = testPng(400)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image size too large"), "Error should match")
	}

}

func TestGetStatsBadMin(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 300000

	img := ImageType{}

	img.image = testPng(50)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image width too small"), "Error should match")
	}

}

func TestGetStatsBadMax(t *testing.T) {

	config.Settings.Limits.ImageMaxWidth = 1000
	config.Settings.Limits.ImageMinWidth = 100
	config.Settings.Limits.ImageMaxHeight = 1000
	config.Settings.Limits.ImageMinHeight = 100
	config.Settings.Limits.ImageMaxSize = 300000

	img := ImageType{}

	img.image = testPng(1200)

	err := img.getStats()
	if assert.Error(t, err, "An error was expected") {
		assert.Equal(t, err, errors.New("image width too large"), "Error should match")
	}

}

func TestMakeFilenames(t *testing.T) {

	img := ImageType{}

	img.makeFilenames()

	assert.NotEmpty(t, img.Filename, "Filename should be returned")

	assert.NotEmpty(t, img.Thumbnail, "Thumbnail name should be returned")

}
<|endoftext|>


<commit_before>/*
Package trayhost is a library for placing a Go
application in the task bar (system tray,
notification area, or dock) in a consistent
manner across multiple platforms. Currently,
there is built-in support for Windows, Mac OSX,
and Linux systems that support GTK+ 3 status
icons (including Gnome 2, KDE 4, Cinnamon,
MATE and other desktop environments).

The indended usage is for applications that
utilize web technology for the user interface, but
require access to the client system beyond what
is offered in a browser sandbox (for instance,
an application that requires access to the user's
file system).

The library places a tray icon on the host system's
task bar that can be used to open a URL, giving users
easy access to the web-based user interface.

Further information can be found at the project's
home at http://github.com/cratonica/trayhost

Clint Caywood

http://github.com/cratonica/trayhost
*/
package trayhost

import (
	"reflect"
	"unsafe"
)

/*
#cgo linux pkg-config: gtk+-2.0
#cgo linux CFLAGS: -DLINUX -I/usr/include/libappindicator-0.1
#cgo linux LDFLAGS: -ldl
#cgo windows CFLAGS: -DWIN32
#cgo darwin CFLAGS: -DDARWIN -x objective-c
#cgo darwin LDFLAGS: -framework Cocoa
#include <stdlib.h>
#include "platform/platform.h"
*/
import "C"

var isExiting bool
var urlPtr unsafe.Pointer
var menuItems MenuItems

type MenuItem struct {
	Title    string
	Disabled bool
	Handler  func()
}

type MenuItems []MenuItem

// Run the host system's event loop
func Initialize(title string, imageData []byte, items MenuItems) {
	menuItems = items

	defer C.free(urlPtr)

	cTitle := C.CString(title)
	defer C.free(unsafe.Pointer(cTitle))

	// Copy the image data into unmanaged memory
	cImageData := C.malloc(C.size_t(len(imageData)))
	defer C.free(cImageData)
	var cImageDataSlice []C.uchar
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&cImageDataSlice))
	sliceHeader.Cap = len(imageData)
	sliceHeader.Len = len(imageData)
	sliceHeader.Data = uintptr(cImageData)

	for i, v := range imageData {
		cImageDataSlice[i] = C.uchar(v)
	}

	// Initialize menu
	C.init(cTitle, &cImageDataSlice[0], C.uint(len(imageData)))

	for id, item := range menuItems {
		AddMenuItem(id, item)
	}

}

func EnterLoop() {
	C.native_loop()
	// If reached, user clicked Exit
	isExiting = true
}

func Exit() {
	C.exit_loop()
}

// Creates a separator MenuItem.
func SeparatorMenuItem() MenuItem { return MenuItem{Title: ""} }

func AddMenuItem(id int, item MenuItem) {
	if item.Title == "" {
		C.add_separator_item()
	} else {
		// ignore errors
		addMenuItem(id, item)
		// titlePtr, _ := syscall.UTF16PtrFromString(item.Title)
		// C.add_menu_item((C.int)(id), (*C.char)(unsafe.Pointer(titlePtr)), cbool(item.Disabled))
	}
}

func cAddMenuItem(id C.int, title *C.char, disabled C.int) {
	C.add_menu_item(id, title, disabled)
}

func cbool(b bool) C.int {
	if b {
		return 1
	} else {
		return 0
	}
}
<commit_msg>Remove MenuItems type.<commit_after>/*
Package trayhost is a library for placing a Go
application in the task bar (system tray,
notification area, or dock) in a consistent
manner across multiple platforms. Currently,
there is built-in support for Windows, Mac OSX,
and Linux systems that support GTK+ 3 status
icons (including Gnome 2, KDE 4, Cinnamon,
MATE and other desktop environments).

The indended usage is for applications that
utilize web technology for the user interface, but
require access to the client system beyond what
is offered in a browser sandbox (for instance,
an application that requires access to the user's
file system).

The library places a tray icon on the host system's
task bar that can be used to open a URL, giving users
easy access to the web-based user interface.

Further information can be found at the project's
home at http://github.com/cratonica/trayhost

Clint Caywood

http://github.com/cratonica/trayhost
*/
package trayhost

import (
	"reflect"
	"unsafe"
)

/*
#cgo linux pkg-config: gtk+-2.0
#cgo linux CFLAGS: -DLINUX -I/usr/include/libappindicator-0.1
#cgo linux LDFLAGS: -ldl
#cgo windows CFLAGS: -DWIN32
#cgo darwin CFLAGS: -DDARWIN -x objective-c
#cgo darwin LDFLAGS: -framework Cocoa
#include <stdlib.h>
#include "platform/platform.h"
*/
import "C"

var isExiting bool
var urlPtr unsafe.Pointer
var menuItems []MenuItem

type MenuItem struct {
	Title    string
	Disabled bool
	Handler  func()
}

// Run the host system's event loop
func Initialize(title string, imageData []byte, items []MenuItem) {
	menuItems = items

	defer C.free(urlPtr)

	cTitle := C.CString(title)
	defer C.free(unsafe.Pointer(cTitle))

	// Copy the image data into unmanaged memory
	cImageData := C.malloc(C.size_t(len(imageData)))
	defer C.free(cImageData)
	var cImageDataSlice []C.uchar
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&cImageDataSlice))
	sliceHeader.Cap = len(imageData)
	sliceHeader.Len = len(imageData)
	sliceHeader.Data = uintptr(cImageData)

	for i, v := range imageData {
		cImageDataSlice[i] = C.uchar(v)
	}

	// Initialize menu
	C.init(cTitle, &cImageDataSlice[0], C.uint(len(imageData)))

	for id, item := range menuItems {
		AddMenuItem(id, item)
	}

}

func EnterLoop() {
	C.native_loop()
	// If reached, user clicked Exit
	isExiting = true
}

func Exit() {
	C.exit_loop()
}

// Creates a separator MenuItem.
func SeparatorMenuItem() MenuItem { return MenuItem{Title: ""} }

func AddMenuItem(id int, item MenuItem) {
	if item.Title == "" {
		C.add_separator_item()
	} else {
		// ignore errors
		addMenuItem(id, item)
		// titlePtr, _ := syscall.UTF16PtrFromString(item.Title)
		// C.add_menu_item((C.int)(id), (*C.char)(unsafe.Pointer(titlePtr)), cbool(item.Disabled))
	}
}

func cAddMenuItem(id C.int, title *C.char, disabled C.int) {
	C.add_menu_item(id, title, disabled)
}

func cbool(b bool) C.int {
	if b {
		return 1
	} else {
		return 0
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Robin Engel
 *
 * 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 core

import (
	"time"
	"math/rand"
	"crypto/md5"
	"encoding/hex"
	"log"
)

type sessionCache struct {
	sessions    map[string]session
}

type session struct {
	keyHash []byte
	validTo time.Time
}

func NewSessionCache() *sessionCache {
	m := make(map[string]session)
	return &sessionCache{m}
}

func (cache sessionCache) NewSession(ctx MoselServerContext, user string) (string, time.Time) {
	millis := time.Now().UnixNano() / int64(time.Millisecond)

	s := session{}

	b := make([]byte, 256)
	rand.Read(b)

	key := cache.hash(
		[]byte(string(millis) + user + string(b)))

	keyHash := cache.hash([]byte(key[:]))

	s.keyHash = []byte(keyHash[:])
	s.validTo = time.Now()

	keyHashString := cache.hashToString(s.keyHash[:])

	cache.sessions[keyHashString] = s
	log.Println(cache.sessions)

	return cache.hashToString(key[:]), time.Now()
}

func (cache sessionCache) ValidateSession(ctx MoselServerContext, key string) bool {
	log.Println(cache.sessions)

	keyBin, _ := hex.DecodeString(key)
	hash := cache.hash(keyBin)
	hashString := cache.hashToString(hash)
	//log.Printf("Check hash: %s", hashString)
	_, ok := ctx.Sessions.sessions[hashString]
	//log.Printf("Status: %s", ok)
	return ok
}

func (cache sessionCache) hashToString(b []byte) string {
	return hex.EncodeToString(b[:])
}

func (cache sessionCache) hash(b []byte) []byte {
	r := md5.Sum(b)
	return r[:]
}<commit_msg>remove obsolete debug logging<commit_after>/*
 * Copyright 2016 Robin Engel
 *
 * 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 core

import (
	"time"
	"math/rand"
	"crypto/md5"
	"encoding/hex"
)

type sessionCache struct {
	sessions map[string]session
}

type session struct {
	keyHash []byte
	validTo time.Time
}

func NewSessionCache() *sessionCache {
	m := make(map[string]session)
	return &sessionCache{m}
}

func (cache sessionCache) NewSession(ctx MoselServerContext, user string) (string, time.Time) {
	millis := time.Now().UnixNano() / int64(time.Millisecond)

	s := session{}

	b := make([]byte, 256)
	rand.Read(b)

	key := cache.hash(
		[]byte(string(millis) + user + string(b)))

	keyHash := cache.hash([]byte(key[:]))

	s.keyHash = []byte(keyHash[:])
	s.validTo = time.Now()

	keyHashString := cache.hashToString(s.keyHash[:])

	cache.sessions[keyHashString] = s

	return cache.hashToString(key[:]), time.Now()
}

func (cache sessionCache) ValidateSession(ctx MoselServerContext, key string) bool {
	keyBin, _ := hex.DecodeString(key)
	hash := cache.hash(keyBin)
	hashString := cache.hashToString(hash)
	_, ok := ctx.Sessions.sessions[hashString]
	return ok
}

func (cache sessionCache) hashToString(b []byte) string {
	return hex.EncodeToString(b[:])
}

func (cache sessionCache) hash(b []byte) []byte {
	r := md5.Sum(b)
	return r[:]
}<|endoftext|>


<commit_before>package model

import (
	"fmt"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/oinume/lekcije/server/bootstrap"
	"github.com/oinume/lekcije/server/util"
)

type TestHelper struct {
	dbURL           string
	db              *gorm.DB
	mCountryService *MCountryService
}

func NewTestHelper() *TestHelper {
	return &TestHelper{}
}

func (h *TestHelper) DB() *gorm.DB {
	if h.db != nil {
		return h.db
	}
	bootstrap.CheckCLIEnvVars()
	h.dbURL = ReplaceToTestDBURL(bootstrap.CLIEnvVars.DBURL())
	db, err := OpenDB(h.dbURL, 1, false /* TODO: env */)
	if err != nil {
		panic(fmt.Sprintf("Failed to OpenDB(): err=%v", err))
	}
	h.db = db
	return db
}

func (h *TestHelper) getDBName(dbURL string) string {
	if index := strings.LastIndex(dbURL, "/"); index != -1 {
		return dbURL[index+1:]
	}
	return ""
}

func (h *TestHelper) LoadAllTables(db *gorm.DB) []string {
	type Table struct {
		Name string `gorm:"column:table_name"`
	}
	tables := []Table{}
	sql := "SELECT table_name FROM information_schema.tables WHERE table_schema = ?"
	if err := db.Raw(sql, h.getDBName(h.dbURL)).Scan(&tables).Error; err != nil {
		panic(fmt.Sprintf("Failed to select table names: err=%v", err))
	}

	tableNames := []string{}
	for _, t := range tables {
		if t.Name == "goose_db_version" {
			continue
		}
		tableNames = append(tableNames, t.Name)
	}
	return tableNames
}

func (h *TestHelper) TruncateAllTables(db *gorm.DB) {
	tables := h.LoadAllTables(db)
	for _, t := range tables {
		if strings.HasPrefix(t, "m_") {
			continue
		}
		if err := db.Exec("TRUNCATE TABLE " + t).Error; err != nil {
			panic(fmt.Sprintf("Failed to truncate table: table=%v, err=%v", t, err))
		}
	}
}

func (h *TestHelper) CreateUser(name, email string) *User {
	db := h.DB()
	user, err := NewUserService(db).Create(name, email)
	if err != nil {
		panic(fmt.Sprintf("Failed to CreateUser(): err=%v", err))
	}
	return user
}

func (h *TestHelper) CreateRandomUser() *User {
	name := util.RandomString(16)
	return h.CreateUser(name, name+"@example.com")
}

func (h *TestHelper) CreateUserGoogle(googleID string, userID uint32) *UserGoogle {
	userGoogle := &UserGoogle{
		GoogleID: googleID,
		UserID:   userID,
	}
	if err := h.DB().Create(userGoogle).Error; err != nil {
		panic(fmt.Sprintf("Failed to CreateUserGoogle(): %v", err))
	}
	return userGoogle
}

func (h *TestHelper) CreateTeacher(id uint32, name string) *Teacher {
	db := h.DB()
	teacher := &Teacher{
		ID:     id,
		Name:   name,
		Gender: "female",
	}
	if err := NewTeacherService(db).CreateOrUpdate(teacher); err != nil {
		panic(fmt.Sprintf("Failed to CreateTeacher(): err=%v", err))
	}
	return teacher
}

func (h *TestHelper) CreateRandomTeacher() *Teacher {
	return h.CreateTeacher(uint32(util.RandomInt(99999)), util.RandomString(6))
}

func (h *TestHelper) LoadMCountries() *MCountries {
	db := h.DB()
	// TODO: cache
	mCountries, err := NewMCountryService(db).LoadAll()
	if err != nil {
		panic(fmt.Sprintf("Failed to MCountryService.LoadAll(): err=%v", err))
	}
	return mCountries
}

func (h *TestHelper) CreateFollowingTeacher(userID uint32, teacher *Teacher) *FollowingTeacher {
	now := time.Now()
	ft, err := NewFollowingTeacherService(h.DB()).FollowTeacher(userID, teacher, now)
	if err != nil {
		panic(fmt.Sprintf("Failed to FollowTeacher(): err=%v", err))
	}
	return ft
}
<commit_msg>Fix Failed to create Teacher<commit_after>package model

import (
	"fmt"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/oinume/lekcije/server/bootstrap"
	"github.com/oinume/lekcije/server/util"
)

type TestHelper struct {
	dbURL           string
	db              *gorm.DB
	mCountryService *MCountryService
}

func NewTestHelper() *TestHelper {
	return &TestHelper{}
}

func (h *TestHelper) DB() *gorm.DB {
	if h.db != nil {
		return h.db
	}
	bootstrap.CheckCLIEnvVars()
	h.dbURL = ReplaceToTestDBURL(bootstrap.CLIEnvVars.DBURL())
	db, err := OpenDB(h.dbURL, 1, false /* TODO: env */)
	if err != nil {
		panic(fmt.Sprintf("Failed to OpenDB(): err=%v", err))
	}
	h.db = db
	return db
}

func (h *TestHelper) getDBName(dbURL string) string {
	if index := strings.LastIndex(dbURL, "/"); index != -1 {
		return dbURL[index+1:]
	}
	return ""
}

func (h *TestHelper) LoadAllTables(db *gorm.DB) []string {
	type Table struct {
		Name string `gorm:"column:table_name"`
	}
	tables := []Table{}
	sql := "SELECT table_name FROM information_schema.tables WHERE table_schema = ?"
	if err := db.Raw(sql, h.getDBName(h.dbURL)).Scan(&tables).Error; err != nil {
		panic(fmt.Sprintf("Failed to select table names: err=%v", err))
	}

	tableNames := []string{}
	for _, t := range tables {
		if t.Name == "goose_db_version" {
			continue
		}
		tableNames = append(tableNames, t.Name)
	}
	return tableNames
}

func (h *TestHelper) TruncateAllTables(db *gorm.DB) {
	tables := h.LoadAllTables(db)
	for _, t := range tables {
		if strings.HasPrefix(t, "m_") {
			continue
		}
		if err := db.Exec("TRUNCATE TABLE " + t).Error; err != nil {
			panic(fmt.Sprintf("Failed to truncate table: table=%v, err=%v", t, err))
		}
	}
}

func (h *TestHelper) CreateUser(name, email string) *User {
	db := h.DB()
	user, err := NewUserService(db).Create(name, email)
	if err != nil {
		panic(fmt.Sprintf("Failed to CreateUser(): err=%v", err))
	}
	return user
}

func (h *TestHelper) CreateRandomUser() *User {
	name := util.RandomString(16)
	return h.CreateUser(name, name+"@example.com")
}

func (h *TestHelper) CreateUserGoogle(googleID string, userID uint32) *UserGoogle {
	userGoogle := &UserGoogle{
		GoogleID: googleID,
		UserID:   userID,
	}
	if err := h.DB().Create(userGoogle).Error; err != nil {
		panic(fmt.Sprintf("Failed to CreateUserGoogle(): %v", err))
	}
	return userGoogle
}

func (h *TestHelper) CreateTeacher(id uint32, name string) *Teacher {
	db := h.DB()
	teacher := &Teacher{
		ID:       id,
		Name:     name,
		Gender:   "female",
		Birthday: time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC),
	}
	if err := NewTeacherService(db).CreateOrUpdate(teacher); err != nil {
		panic(fmt.Sprintf("Failed to CreateTeacher(): err=%v", err))
	}
	return teacher
}

func (h *TestHelper) CreateRandomTeacher() *Teacher {
	return h.CreateTeacher(uint32(util.RandomInt(99999)), util.RandomString(6))
}

func (h *TestHelper) LoadMCountries() *MCountries {
	db := h.DB()
	// TODO: cache
	mCountries, err := NewMCountryService(db).LoadAll()
	if err != nil {
		panic(fmt.Sprintf("Failed to MCountryService.LoadAll(): err=%v", err))
	}
	return mCountries
}

func (h *TestHelper) CreateFollowingTeacher(userID uint32, teacher *Teacher) *FollowingTeacher {
	now := time.Now()
	ft, err := NewFollowingTeacherService(h.DB()).FollowTeacher(userID, teacher, now)
	if err != nil {
		panic(fmt.Sprintf("Failed to FollowTeacher(): err=%v", err))
	}
	return ft
}
<|endoftext|>


<commit_before>package utils

import "testing"

func TestGenerateKey(t *testing.T) {
	pri, pub, err := GenerateKey()
	if err != nil {
		panic(err)
	}
	priK, err := ParsePrivateKey(pri)
	if err != nil {
		panic(err)
	}
	pubK, err := ParsePublicKey(pub)
	if err != nil {
		panic(err)
	}
	pubFrPriv := PublicKeyFromPrivate(priK)
	if pubK.E != pubFrPriv.E {
		panic("key gen pub key not match")
	}
}

func TestVerifySign(t *testing.T) {
	pri, pub, err := GenerateKey()
	if err != nil {
		panic(err)
	}
	priK, err := ParsePrivateKey(pri)
	if err != nil {
		panic(err)
	}
	pubK, err := ParsePublicKey(pub)
	if err != nil {
		panic(err)
	}
	data := []byte("test signature")
	sig := Sign(priK, data)
	if err := VerifySign(pubK, sig, data); err != nil {
		panic(err)
	}
}<commit_msg>test from private<commit_after>package utils

import "testing"

func TestGenerateKey(t *testing.T) {
	pri, pub, err := GenerateKey()
	if err != nil {
		panic(err)
	}
	priK, err := ParsePrivateKey(pri)
	if err != nil {
		panic(err)
	}
	pubK, err := ParsePublicKey(pub)
	if err != nil {
		panic(err)
	}
	pubFrPriv := PublicKeyFromPrivate(priK)
	if pubK.E != pubFrPriv.E {
		panic("key gen pub key not match")
	}
}

func TestVerifySign(t *testing.T) {
	pri, pub, err := GenerateKey()
	if err != nil {
		panic(err)
	}
	priK, err := ParsePrivateKey(pri)
	if err != nil {
		panic(err)
	}
	pubK, err := ParsePublicKey(pub)
	if err != nil {
		panic(err)
	}
	pubFrPriv := PublicKeyFromPrivate(priK)
	data := []byte("test signature")
	sig := Sign(priK, data)
	if err := VerifySign(pubK, sig, data); err != nil {
		panic(err)
	}
	if err := VerifySign(pubFrPriv, sig, data); err != nil {
		panic(err)
	}
}<|endoftext|>


<commit_before>package s3

import (
	"bytes"
	"io"
	"net/http"
	"strings"
	"fmt"
	"crypto/md5"
	"encoding/base64"

	"github.com/gogo/protobuf/types"
	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pfs"
)

type objectHandler struct {
	pc *client.APIClient
}

func newObjectHandler(pc *client.APIClient) *objectHandler {
	return &objectHandler{pc: pc}
}

func (h *objectHandler) get(w http.ResponseWriter, r *http.Request) {
	repo, branch, file := objectArgs(w, r)
	branchInfo, err := h.pc.InspectBranch(repo, branch)
	if err != nil {
		maybeNotFoundError(w, r, err)
		return
	}
	if branchInfo.Head == nil {
		noSuchKeyError(w, r)
		return
	}
	if strings.HasSuffix(file, "/") {
		invalidFilePathError(w, r)
		return
	}

	fileInfos, err := h.pc.ListFileHistory(branchInfo.Branch.Repo.Name, branchInfo.Head.ID, file, 1)
	if err != nil {
		maybeNotFoundError(w, r, err)
		return
	}
	if len(fileInfos) != 1 || fileInfos[0].FileType != pfs.FileType_FILE {
		noSuchKeyError(w, r)
		return
	}

	timestamp, err := types.TimestampFromProto(fileInfos[0].Committed)
	if err != nil {
		internalError(w, r, err)
		return
	}

	w.Header().Set("ETag", fmt.Sprintf("\"%x\"", fileInfos[0].Hash))
	reader, err := h.pc.GetFileReadSeeker(branchInfo.Branch.Repo.Name, branchInfo.Head.ID, file)
	if err != nil {
		internalError(w, r, err)
		return
	}

	http.ServeContent(w, r, file, timestamp, reader)
}

func (h *objectHandler) put(w http.ResponseWriter, r *http.Request) {
	repo, branch, file := objectArgs(w, r)
	branchInfo, err := h.pc.InspectBranch(repo, branch)
	if err != nil {
		maybeNotFoundError(w, r, err)
		return
	}
	if strings.HasSuffix(file, "/") {
		invalidFilePathError(w, r)
		return
	}

	expectedHash, ok := r.Header["Content-Md5"]
	var expectedHashBytes []uint8
	if ok && len(expectedHash) == 1 {
		expectedHashBytes, err = base64.StdEncoding.DecodeString(expectedHash[0])
		if err != nil || len(expectedHashBytes) != 16 {
			invalidDigestError(w, r)
			return
		}
	}

	hasher := md5.New()
	reader := io.TeeReader(r.Body, hasher)
	success := false

	_, err = h.pc.PutFileOverwrite(branchInfo.Branch.Repo.Name, branchInfo.Branch.Name, file, reader, 0)
	if err != nil {
		internalError(w, r, err)
		return
	}

	defer func() {
		// try to clean up the file if an error occurred
		if !success {
	        if err = h.pc.DeleteFile(branchInfo.Branch.Repo.Name, branchInfo.Branch.Name, file); err != nil {
	            requestLogger(r).Errorf("could not cleanup file after an error: %v", err)
	        }
		}
	}()

	actualHashBytes := hasher.Sum(nil)
	if expectedHashBytes != nil && !bytes.Equal(expectedHashBytes, actualHashBytes) {
		badDigestError(w, r)
		return
	}

	fileInfo, err := h.pc.InspectFile(branchInfo.Branch.Repo.Name, branchInfo.Branch.Name, file)
	if err != nil {
		internalError(w, r, err)
		return
	}

	success = true
	w.Header().Set("ETag", fmt.Sprintf("\"%x\"", fileInfo.Hash))
	w.WriteHeader(http.StatusOK)
}

func (h *objectHandler) del(w http.ResponseWriter, r *http.Request) {
	repo, branch, file := objectArgs(w, r)
	branchInfo, err := h.pc.InspectBranch(repo, branch)
	if err != nil {
		maybeNotFoundError(w, r, err)
		return
	}
	if branchInfo.Head == nil {
		noSuchKeyError(w, r)
		return
	}
	if strings.HasSuffix(file, "/") {
		invalidFilePathError(w, r)
		return
	}

	if err := h.pc.DeleteFile(branchInfo.Branch.Repo.Name, branchInfo.Branch.Name, file); err != nil {
		maybeNotFoundError(w, r, err)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
<commit_msg>Revert "Pull more accurate timestamp"<commit_after>package s3

import (
	"bytes"
	"io"
	"net/http"
	"strings"
	"fmt"
	"crypto/md5"
	"encoding/base64"

	"github.com/gogo/protobuf/types"
	"github.com/pachyderm/pachyderm/src/client"
)

type objectHandler struct {
	pc *client.APIClient
}

func newObjectHandler(pc *client.APIClient) *objectHandler {
	return &objectHandler{pc: pc}
}

func (h *objectHandler) get(w http.ResponseWriter, r *http.Request) {
	repo, branch, file := objectArgs(w, r)
	branchInfo, err := h.pc.InspectBranch(repo, branch)
	if err != nil {
		maybeNotFoundError(w, r, err)
		return
	}
	if branchInfo.Head == nil {
		noSuchKeyError(w, r)
		return
	}
	if strings.HasSuffix(file, "/") {
		invalidFilePathError(w, r)
		return
	}

	fileInfo, err := h.pc.InspectFile(branchInfo.Branch.Repo.Name, branchInfo.Head.ID, file)
	if err != nil {
		maybeNotFoundError(w, r, err)
		return
	}

	timestamp, err := types.TimestampFromProto(fileInfo.Committed)
	if err != nil {
		internalError(w, r, err)
		return
	}

	w.Header().Set("ETag", fmt.Sprintf("\"%x\"", fileInfo.Hash))
	reader, err := h.pc.GetFileReadSeeker(branchInfo.Branch.Repo.Name, branchInfo.Head.ID, file)
	if err != nil {
		internalError(w, r, err)
		return
	}

	http.ServeContent(w, r, file, timestamp, reader)
}

func (h *objectHandler) put(w http.ResponseWriter, r *http.Request) {
	repo, branch, file := objectArgs(w, r)
	branchInfo, err := h.pc.InspectBranch(repo, branch)
	if err != nil {
		maybeNotFoundError(w, r, err)
		return
	}
	if strings.HasSuffix(file, "/") {
		invalidFilePathError(w, r)
		return
	}

	expectedHash, ok := r.Header["Content-Md5"]
	var expectedHashBytes []uint8
	if ok && len(expectedHash) == 1 {
		expectedHashBytes, err = base64.StdEncoding.DecodeString(expectedHash[0])
		if err != nil || len(expectedHashBytes) != 16 {
			invalidDigestError(w, r)
			return
		}
	}

	hasher := md5.New()
	reader := io.TeeReader(r.Body, hasher)
	success := false

	_, err = h.pc.PutFileOverwrite(branchInfo.Branch.Repo.Name, branchInfo.Branch.Name, file, reader, 0)
	if err != nil {
		internalError(w, r, err)
		return
	}

	defer func() {
		// try to clean up the file if an error occurred
		if !success {
	        if err = h.pc.DeleteFile(branchInfo.Branch.Repo.Name, branchInfo.Branch.Name, file); err != nil {
	            requestLogger(r).Errorf("could not cleanup file after an error: %v", err)
	        }
		}
	}()

	actualHashBytes := hasher.Sum(nil)
	if expectedHashBytes != nil && !bytes.Equal(expectedHashBytes, actualHashBytes) {
		badDigestError(w, r)
		return
	}

	fileInfo, err := h.pc.InspectFile(branchInfo.Branch.Repo.Name, branchInfo.Branch.Name, file)
	if err != nil {
		internalError(w, r, err)
		return
	}

	success = true
	w.Header().Set("ETag", fmt.Sprintf("\"%x\"", fileInfo.Hash))
	w.WriteHeader(http.StatusOK)
}

func (h *objectHandler) del(w http.ResponseWriter, r *http.Request) {
	repo, branch, file := objectArgs(w, r)
	branchInfo, err := h.pc.InspectBranch(repo, branch)
	if err != nil {
		maybeNotFoundError(w, r, err)
		return
	}
	if branchInfo.Head == nil {
		noSuchKeyError(w, r)
		return
	}
	if strings.HasSuffix(file, "/") {
		invalidFilePathError(w, r)
		return
	}

	if err := h.pc.DeleteFile(branchInfo.Branch.Repo.Name, branchInfo.Branch.Name, file); err != nil {
		maybeNotFoundError(w, r, err)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
<|endoftext|>


<commit_before>package main

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

const (
	pageTop = `<!DOCTYPE HTML>
<title>Statistics</title>

Statistics

Computes basic statistics for a given list of numbers
`
	form = `<form action="/" method="POST">
Numbers (comma or space-separated):

<input type="text" name="numbers" size="30">

<input type="submit" value="Calculate">
</form>`
	pageBottom = `</body></html>`
	anError    = `<p class="error">%s</p>`
)

func homePage(writer http.ResponseWriter, request *http.Request) {
	err := request.ParseForm() // Must be called before writing response
	fmt.Fprint(writer, pageTop, form)
	if err != nil {
		fmt.Fprintf(writer, anError, err)
	} else {
		if numbers, message, ok := processRequest(request); ok {
			precision := 4
			stats := NewStatistics(precision)
			stats.Compute(numbers)
			fmt.Fprint(writer, formatStats(stats))
		} else if message != "" {
			fmt.Fprintf(writer, anError, message)
		}
	}
	fmt.Fprint(writer, pageBottom)
}

func processRequest(request *http.Request) ([]float64, string, bool) {
	var numbers []float64
	if slice, found := request.Form["numbers"]; found && len(slice) > 0 {
		text := strings.Replace(slice[0], ",", " ", -1)
		for _, field := range strings.Fields(text) {
			if x, err := strconv.ParseFloat(field, 64); err != nil {
				return numbers, "'" + field + "' is invalid", false
			} else {
				numbers = append(numbers, x)
			}
		}
	}
	if len(numbers) == 0 {
		return numbers, "", false // no data first time form is shown
	}
	return numbers, "", true
}

func formatStats(stats *statistics) string {
	return fmt.Sprintf(`

Results

Numbers	%v

Count	%d

Mean	%f

Median	%f

Standard Deviation	%f

`, stats.numbers, len(stats.numbers), stats.mean, stats.median, stats.standardDeviation)
}
<commit_msg>Update web page to include modes result.<commit_after>package main

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

const (
	pageTop = `<!DOCTYPE HTML>
<title>Statistics</title>

Statistics

Computes basic statistics for a given list of numbers
`
	form = `<form action="/" method="POST">
Numbers (comma or space-separated):

<input type="text" name="numbers" size="30">

<input type="submit" value="Calculate">
</form>`
	pageBottom = `</body></html>`
	anError    = `<p class="error">%s</p>`
)

func homePage(writer http.ResponseWriter, request *http.Request) {
	err := request.ParseForm() // Must be called before writing response
	fmt.Fprint(writer, pageTop, form)
	if err != nil {
		fmt.Fprintf(writer, anError, err)
	} else {
		if numbers, message, ok := processRequest(request); ok {
			precision := 4
			stats := NewStatistics(precision)
			stats.Compute(numbers)
			fmt.Fprint(writer, formatStats(stats))
		} else if message != "" {
			fmt.Fprintf(writer, anError, message)
		}
	}
	fmt.Fprint(writer, pageBottom)
}

func processRequest(request *http.Request) ([]float64, string, bool) {
	var numbers []float64
	if slice, found := request.Form["numbers"]; found && len(slice) > 0 {
		text := strings.Replace(slice[0], ",", " ", -1)
		for _, field := range strings.Fields(text) {
			if x, err := strconv.ParseFloat(field, 64); err != nil {
				return numbers, "'" + field + "' is invalid", false
			} else {
				numbers = append(numbers, x)
			}
		}
	}
	if len(numbers) == 0 {
		return numbers, "", false // no data first time form is shown
	}
	return numbers, "", true
}

func formatStats(stats *statistics) string {
	return fmt.Sprintf(`

Results

Numbers	%v

Count	%d

Mean	%f

Median	%f

Modes	%v

Standard Deviation	%f

`, stats.numbers, len(stats.numbers), stats.mean, stats.median, stats.modes, stats.standardDeviation)
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package storage

import (
	"encoding/binary"

	"github.com/syndtr/goleveldb/leveldb"
	ldb_util "github.com/syndtr/goleveldb/leveldb/util"

	"github.com/bitmark-inc/logger"
)

// PoolHandle - the structure of a pool handle
type PoolHandle struct {
	prefix     byte
	limit      []byte
	dataAccess DataAccess
}

// Element - a binary data item
type Element struct {
	Key   []byte
	Value []byte
}

// prepend the prefix onto the key
func (p *PoolHandle) prefixKey(key []byte) []byte {
	prefixedKey := make([]byte, 1, len(key)+1)
	prefixedKey[0] = p.prefix
	return append(prefixedKey, key...)
}

// Put - store a key/value bytes pair to the database
func (p *PoolHandle) Put(key []byte, value []byte) {
	poolData.RLock()
	defer poolData.RUnlock()
	if nil == p.dataAccess {
		logger.Panic("pool.Put nil database")
		return
	}
	p.dataAccess.Put(p.prefixKey(key), value)
}

// PutN - store a uint8 as an 8 byte sequence
func (p *PoolHandle) PutN(key []byte, value uint64) {
	buffer := make([]byte, 8)
	binary.BigEndian.PutUint64(buffer, value)
	p.Put(key, buffer)
}

// Delete - remove a key from the database
func (p *PoolHandle) Delete(key []byte) {
	poolData.RLock()
	defer poolData.RUnlock()
	p.dataAccess.Delete(p.prefixKey(key))
}

// Get - read a value for a given key
//
// this returns the actual element - copy the result if it must be preserved
func (p *PoolHandle) Get(key []byte) []byte {
	poolData.RLock()
	defer poolData.RUnlock()
	if nil == p.dataAccess {
		return nil
	}
	value, err := p.dataAccess.Get(p.prefixKey(key))
	if leveldb.ErrNotFound == err {
		return nil
	}
	logger.PanicIfError("pool.GetB", err)
	return value
}

// GetN - read a record and decode first 8 bytes as big endian uint64
//
// second parameter is false if record was not found
// panics if not 8 (or more) bytes in the record
func (p *PoolHandle) GetN(key []byte) (uint64, bool) {
	buffer := p.Get(key)
	if nil == buffer {
		return 0, false
	}
	if len(buffer) < 8 {
		logger.Panicf("pool.GetN truncated record for: %x: %s", key, buffer)
	}
	n := binary.BigEndian.Uint64(buffer[:8])
	return n, true
}

// GetNB - read a record and decode first 8 bytes as big endian uint64
// and return the rest of the record as byte slice
//
// second parameter is nil if record was not found
// panics if not 9 (or more) bytes in the record
// this returns the actual element in the second parameter - copy the result if it must be preserved
func (p *PoolHandle) GetNB(key []byte) (uint64, []byte) {
	buffer := p.Get(key)
	if nil == buffer {
		return 0, nil
	}
	if len(buffer) < 9 { // must have at least one byte after the N value
		logger.Panicf("pool.GetNB truncated record for: %x: %s", key, buffer)
	}
	n := binary.BigEndian.Uint64(buffer[:8])
	return n, buffer[8:]
}

// Has - Check if a key exists
func (p *PoolHandle) Has(key []byte) bool {
	poolData.RLock()
	defer poolData.RUnlock()
	if nil == p.dataAccess {
		return false
	}
	value, err := p.dataAccess.Has(p.prefixKey(key))
	logger.PanicIfError("pool.Has", err)
	return value
}

// LastElement - get the last element in a pool
func (p *PoolHandle) LastElement() (Element, bool) {
	maxRange := ldb_util.Range{
		Start: []byte{p.prefix}, // Start of key range, included in the range
		Limit: p.limit,          // Limit of key range, excluded from the range
	}

	poolData.RLock()
	defer poolData.RUnlock()
	if nil == p.dataAccess {
		return Element{}, false
	}

	iter := p.dataAccess.Iterator(&maxRange)

	found := false
	result := Element{}
	if iter.Last() {

		// contents of the returned slice must not be modified, and are
		// only valid until the next call to Next
		key := iter.Key()
		value := iter.Value()

		dataKey := make([]byte, len(key)-1) // strip the prefix
		copy(dataKey, key[1:])              // ...

		dataValue := make([]byte, len(value))
		copy(dataValue, value)

		result.Key = dataKey
		result.Value = dataValue
		found = true
	}
	iter.Release()
	err := iter.Error()
	logger.PanicIfError("pool.LastElement", err)
	return result, found
}

func (p *PoolHandle) BeginDBTransaction() {
	p.dataAccess.Begin()
}

func (p *PoolHandle) WriteDBTransaction() {
	p.dataAccess.Write()
}
<commit_msg>leveldb-2pc: DataAccess interface name change from Write to Commit<commit_after>// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package storage

import (
	"encoding/binary"

	"github.com/syndtr/goleveldb/leveldb"
	ldb_util "github.com/syndtr/goleveldb/leveldb/util"

	"github.com/bitmark-inc/logger"
)

// PoolHandle - the structure of a pool handle
type PoolHandle struct {
	prefix     byte
	limit      []byte
	dataAccess DataAccess
}

// Element - a binary data item
type Element struct {
	Key   []byte
	Value []byte
}

// prepend the prefix onto the key
func (p *PoolHandle) prefixKey(key []byte) []byte {
	prefixedKey := make([]byte, 1, len(key)+1)
	prefixedKey[0] = p.prefix
	return append(prefixedKey, key...)
}

// Put - store a key/value bytes pair to the database
func (p *PoolHandle) Put(key []byte, value []byte) {
	poolData.RLock()
	defer poolData.RUnlock()
	if nil == p.dataAccess {
		logger.Panic("pool.Put nil database")
		return
	}
	p.dataAccess.Put(p.prefixKey(key), value)
}

// PutN - store a uint8 as an 8 byte sequence
func (p *PoolHandle) PutN(key []byte, value uint64) {
	buffer := make([]byte, 8)
	binary.BigEndian.PutUint64(buffer, value)
	p.Put(key, buffer)
}

// Delete - remove a key from the database
func (p *PoolHandle) Delete(key []byte) {
	poolData.RLock()
	defer poolData.RUnlock()
	p.dataAccess.Delete(p.prefixKey(key))
}

// Get - read a value for a given key
//
// this returns the actual element - copy the result if it must be preserved
func (p *PoolHandle) Get(key []byte) []byte {
	poolData.RLock()
	defer poolData.RUnlock()
	if nil == p.dataAccess {
		return nil
	}
	value, err := p.dataAccess.Get(p.prefixKey(key))
	if leveldb.ErrNotFound == err {
		return nil
	}
	logger.PanicIfError("pool.GetB", err)
	return value
}

// GetN - read a record and decode first 8 bytes as big endian uint64
//
// second parameter is false if record was not found
// panics if not 8 (or more) bytes in the record
func (p *PoolHandle) GetN(key []byte) (uint64, bool) {
	buffer := p.Get(key)
	if nil == buffer {
		return 0, false
	}
	if len(buffer) < 8 {
		logger.Panicf("pool.GetN truncated record for: %x: %s", key, buffer)
	}
	n := binary.BigEndian.Uint64(buffer[:8])
	return n, true
}

// GetNB - read a record and decode first 8 bytes as big endian uint64
// and return the rest of the record as byte slice
//
// second parameter is nil if record was not found
// panics if not 9 (or more) bytes in the record
// this returns the actual element in the second parameter - copy the result if it must be preserved
func (p *PoolHandle) GetNB(key []byte) (uint64, []byte) {
	buffer := p.Get(key)
	if nil == buffer {
		return 0, nil
	}
	if len(buffer) < 9 { // must have at least one byte after the N value
		logger.Panicf("pool.GetNB truncated record for: %x: %s", key, buffer)
	}
	n := binary.BigEndian.Uint64(buffer[:8])
	return n, buffer[8:]
}

// Has - Check if a key exists
func (p *PoolHandle) Has(key []byte) bool {
	poolData.RLock()
	defer poolData.RUnlock()
	if nil == p.dataAccess {
		return false
	}
	value, err := p.dataAccess.Has(p.prefixKey(key))
	logger.PanicIfError("pool.Has", err)
	return value
}

// LastElement - get the last element in a pool
func (p *PoolHandle) LastElement() (Element, bool) {
	maxRange := ldb_util.Range{
		Start: []byte{p.prefix}, // Start of key range, included in the range
		Limit: p.limit,          // Limit of key range, excluded from the range
	}

	poolData.RLock()
	defer poolData.RUnlock()
	if nil == p.dataAccess {
		return Element{}, false
	}

	iter := p.dataAccess.Iterator(&maxRange)

	found := false
	result := Element{}
	if iter.Last() {

		// contents of the returned slice must not be modified, and are
		// only valid until the next call to Next
		key := iter.Key()
		value := iter.Value()

		dataKey := make([]byte, len(key)-1) // strip the prefix
		copy(dataKey, key[1:])              // ...

		dataValue := make([]byte, len(value))
		copy(dataValue, value)

		result.Key = dataKey
		result.Value = dataValue
		found = true
	}
	iter.Release()
	err := iter.Error()
	logger.PanicIfError("pool.LastElement", err)
	return result, found
}

func (p *PoolHandle) BeginDBTransaction() {
	p.dataAccess.Begin()
}

func (p *PoolHandle) WriteDBTransaction() {
	p.dataAccess.Commit()
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"strconv"
	"strings"
	"sync"
)

type DeviceStats struct {
	name     string
	rrqmps   float64
	wrqmps   float64
	rps      float64
	wps      float64
	rkbps    float64
	wkbps    float64
	avgrq_sz float64
	avgqu_sz float64
	await    float64
	r_await  float64
	w_await  float64
	svctm    float64
	util     float64
}

func (d *DeviceStats) GetName() string {
	return d.name
}

func (d *DeviceStats) Aggregate(item IItem) {}

func DeviceStatsIsHeader(row string) bool {
	return strings.HasPrefix(row, "Device")
}

func DeviceStatsParseRow(row string) (IItem, error) {
	tokens := strings.Fields(row)

	if len(tokens) != 14 {
		return nil, errors.New("Failed to parse iostat")
	}

	var err error
	device_stat := &DeviceStats{}

	device_stat.name = tokens[0]

	if device_stat.rrqmps, err = strconv.ParseFloat(tokens[1], 64); err != nil {
		return nil, err
	}

	if device_stat.wrqmps, err = strconv.ParseFloat(tokens[2], 64); err != nil {
		return nil, err
	}

	if device_stat.rps, err = strconv.ParseFloat(tokens[3], 64); err != nil {
		return nil, err
	}

	if device_stat.wps, err = strconv.ParseFloat(tokens[4], 64); err != nil {
		return nil, err
	}

	if device_stat.rkbps, err = strconv.ParseFloat(tokens[5], 64); err != nil {
		return nil, err
	}

	if device_stat.wkbps, err = strconv.ParseFloat(tokens[6], 64); err != nil {
		return nil, err
	}

	if device_stat.avgrq_sz, err = strconv.ParseFloat(tokens[7], 64); err != nil {
		return nil, err
	}

	if device_stat.avgqu_sz, err = strconv.ParseFloat(tokens[8], 64); err != nil {
		return nil, err
	}

	if device_stat.await, err = strconv.ParseFloat(tokens[9], 64); err != nil {
		return nil, err
	}

	if device_stat.r_await, err = strconv.ParseFloat(tokens[10], 64); err != nil {
		return nil, err
	}

	if device_stat.w_await, err = strconv.ParseFloat(tokens[11], 64); err != nil {
		return nil, err
	}

	if device_stat.svctm, err = strconv.ParseFloat(tokens[12], 64); err != nil {
		return nil, err
	}

	if device_stat.util, err = strconv.ParseFloat(tokens[13], 64); err != nil {
		return nil, err
	}

	return device_stat, nil
}

type DeviceStatsProcessor struct {
	lock sync.Mutex
	// Looks like this could be a seperate type
	rrqmps   map[string]ISampleStats
	wrqmps   map[string]ISampleStats
	rps      map[string]ISampleStats
	wps      map[string]ISampleStats
	rkbps    map[string]ISampleStats
	wkbps    map[string]ISampleStats
	avgrq_sz map[string]ISampleStats
	avgqu_sz map[string]ISampleStats
	await    map[string]ISampleStats
	r_await  map[string]ISampleStats
	w_await  map[string]ISampleStats
	svctm    map[string]ISampleStats
	util     map[string]ISampleStats
}

func NewDeviceStatsProcessor() *DeviceStatsProcessor {
	return &DeviceStatsProcessor{
		rrqmps:   make(map[string]ISampleStats),
		wrqmps:   make(map[string]ISampleStats),
		rps:      make(map[string]ISampleStats),
		wps:      make(map[string]ISampleStats),
		rkbps:    make(map[string]ISampleStats),
		wkbps:    make(map[string]ISampleStats),
		avgrq_sz: make(map[string]ISampleStats),
		avgqu_sz: make(map[string]ISampleStats),
		await:    make(map[string]ISampleStats),
		r_await:  make(map[string]ISampleStats),
		w_await:  make(map[string]ISampleStats),
		svctm:    make(map[string]ISampleStats),
		util:     make(map[string]ISampleStats),
	}
}

func (p *DeviceStatsProcessor) GetCmdName() string {
	return "iostat"
}

func (p *DeviceStatsProcessor) GetCmdArgs() []string {
	return []string{"-xd", "10"}
}

func (d *DeviceStatsProcessor) AggregateSample(sample ISample) {
	d.lock.Lock()

	for key, item := range sample.GetMap() {
		ds := item.(*DeviceStats)

		if entry, has := d.rrqmps[key]; has {
			entry.Append(ds.rrqmps)
		} else {
			d.rrqmps[key] = NewStatSample(ds.rrqmps)
		}

		if entry, has := d.wrqmps[key]; has {
			entry.Append(ds.wrqmps)
		} else {
			d.wrqmps[key] = NewStatSample(ds.wrqmps)
		}

		if entry, has := d.rps[key]; has {
			entry.Append(ds.rps)
		} else {
			d.rps[key] = NewStatSample(ds.rps)
		}

		if entry, has := d.wps[key]; has {
			entry.Append(ds.wps)
		} else {
			d.wps[key] = NewStatSample(ds.wps)
		}

		if entry, has := d.rkbps[key]; has {
			entry.Append(ds.rkbps)
		} else {
			d.rkbps[key] = NewStatSample(ds.rkbps)
		}

		if entry, has := d.wkbps[key]; has {
			entry.Append(ds.wkbps)
		} else {
			d.wkbps[key] = NewStatSample(ds.wkbps)
		}

		if entry, has := d.avgrq_sz[key]; has {
			entry.Append(ds.avgrq_sz)
		} else {
			d.avgrq_sz[key] = NewStatSample(ds.avgrq_sz)
		}

		if entry, has := d.avgqu_sz[key]; has {
			entry.Append(ds.avgqu_sz)
		} else {
			d.avgqu_sz[key] = NewStatSample(ds.avgqu_sz)
		}

		if entry, has := d.await[key]; has {
			entry.Append(ds.await)
		} else {
			d.await[key] = NewStatSample(ds.await)
		}

		if entry, has := d.r_await[key]; has {
			entry.Append(ds.r_await)
		} else {
			d.r_await[key] = NewStatSample(ds.r_await)
		}

		if entry, has := d.w_await[key]; has {
			entry.Append(ds.w_await)
		} else {
			d.w_await[key] = NewStatSample(ds.w_await)
		}

		if entry, has := d.svctm[key]; has {
			entry.Append(ds.svctm)
		} else {
			d.svctm[key] = NewStatSample(ds.svctm)
		}

		if entry, has := d.util[key]; has {
			entry.Append(ds.util)
		} else {
			d.util[key] = NewStatSample(ds.util)
		}

	}

	d.lock.Unlock()
}

func (d *DeviceStatsProcessor) NewSample() ISample {
	return NewSample()
}

func (d *DeviceStatsProcessor) IsHeader(row string) bool {
	return DeviceStatsIsHeader(row)
}

func (d *DeviceStatsProcessor) ParseRow(row string) (IItem, error) {
	return DeviceStatsParseRow(row)
}

func (d *DeviceStatsProcessor) GetAndPurgeRrqmps() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.rrqmps
	d.rrqmps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeWrqmps() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.wrqmps
	d.wrqmps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeRps() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.rps
	d.rps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeWps() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.wps
	d.wps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeRkbps() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.rkbps
	d.rkbps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeWkbps() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.wkbps
	d.wkbps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeAvgrq_sz() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.avgrq_sz
	d.avgrq_sz = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeAvgqu_sz() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.avgqu_sz
	d.avgqu_sz = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeAwait() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.await
	d.await = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeRawait() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.r_await
	d.r_await = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeWawait() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.w_await
	d.w_await = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeSvctm() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.svctm
	d.svctm = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeUtil() map[string]ISampleStats {
	// todo start here
	d.lock.Lock()
	samples := d.util
	d.util = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

// this could be a method on map[string]*StatSample
func (d *DeviceStatsProcessor) GetUniqKeys(data map[string]ISampleStats) []string {
	var sample_list []string

	for key := range data {
		sample_list = append(sample_list, key)
	}

	return sample_list
}
<commit_msg>Remove todo<commit_after>package main

import (
	"errors"
	"strconv"
	"strings"
	"sync"
)

type DeviceStats struct {
	name     string
	rrqmps   float64
	wrqmps   float64
	rps      float64
	wps      float64
	rkbps    float64
	wkbps    float64
	avgrq_sz float64
	avgqu_sz float64
	await    float64
	r_await  float64
	w_await  float64
	svctm    float64
	util     float64
}

func (d *DeviceStats) GetName() string {
	return d.name
}

func (d *DeviceStats) Aggregate(item IItem) {}

func DeviceStatsIsHeader(row string) bool {
	return strings.HasPrefix(row, "Device")
}

func DeviceStatsParseRow(row string) (IItem, error) {
	tokens := strings.Fields(row)

	if len(tokens) != 14 {
		return nil, errors.New("Failed to parse iostat")
	}

	var err error
	device_stat := &DeviceStats{}

	device_stat.name = tokens[0]

	if device_stat.rrqmps, err = strconv.ParseFloat(tokens[1], 64); err != nil {
		return nil, err
	}

	if device_stat.wrqmps, err = strconv.ParseFloat(tokens[2], 64); err != nil {
		return nil, err
	}

	if device_stat.rps, err = strconv.ParseFloat(tokens[3], 64); err != nil {
		return nil, err
	}

	if device_stat.wps, err = strconv.ParseFloat(tokens[4], 64); err != nil {
		return nil, err
	}

	if device_stat.rkbps, err = strconv.ParseFloat(tokens[5], 64); err != nil {
		return nil, err
	}

	if device_stat.wkbps, err = strconv.ParseFloat(tokens[6], 64); err != nil {
		return nil, err
	}

	if device_stat.avgrq_sz, err = strconv.ParseFloat(tokens[7], 64); err != nil {
		return nil, err
	}

	if device_stat.avgqu_sz, err = strconv.ParseFloat(tokens[8], 64); err != nil {
		return nil, err
	}

	if device_stat.await, err = strconv.ParseFloat(tokens[9], 64); err != nil {
		return nil, err
	}

	if device_stat.r_await, err = strconv.ParseFloat(tokens[10], 64); err != nil {
		return nil, err
	}

	if device_stat.w_await, err = strconv.ParseFloat(tokens[11], 64); err != nil {
		return nil, err
	}

	if device_stat.svctm, err = strconv.ParseFloat(tokens[12], 64); err != nil {
		return nil, err
	}

	if device_stat.util, err = strconv.ParseFloat(tokens[13], 64); err != nil {
		return nil, err
	}

	return device_stat, nil
}

type DeviceStatsProcessor struct {
	lock sync.Mutex
	// Looks like this could be a seperate type
	rrqmps   map[string]ISampleStats
	wrqmps   map[string]ISampleStats
	rps      map[string]ISampleStats
	wps      map[string]ISampleStats
	rkbps    map[string]ISampleStats
	wkbps    map[string]ISampleStats
	avgrq_sz map[string]ISampleStats
	avgqu_sz map[string]ISampleStats
	await    map[string]ISampleStats
	r_await  map[string]ISampleStats
	w_await  map[string]ISampleStats
	svctm    map[string]ISampleStats
	util     map[string]ISampleStats
}

func NewDeviceStatsProcessor() *DeviceStatsProcessor {
	return &DeviceStatsProcessor{
		rrqmps:   make(map[string]ISampleStats),
		wrqmps:   make(map[string]ISampleStats),
		rps:      make(map[string]ISampleStats),
		wps:      make(map[string]ISampleStats),
		rkbps:    make(map[string]ISampleStats),
		wkbps:    make(map[string]ISampleStats),
		avgrq_sz: make(map[string]ISampleStats),
		avgqu_sz: make(map[string]ISampleStats),
		await:    make(map[string]ISampleStats),
		r_await:  make(map[string]ISampleStats),
		w_await:  make(map[string]ISampleStats),
		svctm:    make(map[string]ISampleStats),
		util:     make(map[string]ISampleStats),
	}
}

func (p *DeviceStatsProcessor) GetCmdName() string {
	return "iostat"
}

func (p *DeviceStatsProcessor) GetCmdArgs() []string {
	return []string{"-xd", "10"}
}

func (d *DeviceStatsProcessor) AggregateSample(sample ISample) {
	d.lock.Lock()

	for key, item := range sample.GetMap() {
		ds := item.(*DeviceStats)

		if entry, has := d.rrqmps[key]; has {
			entry.Append(ds.rrqmps)
		} else {
			d.rrqmps[key] = NewStatSample(ds.rrqmps)
		}

		if entry, has := d.wrqmps[key]; has {
			entry.Append(ds.wrqmps)
		} else {
			d.wrqmps[key] = NewStatSample(ds.wrqmps)
		}

		if entry, has := d.rps[key]; has {
			entry.Append(ds.rps)
		} else {
			d.rps[key] = NewStatSample(ds.rps)
		}

		if entry, has := d.wps[key]; has {
			entry.Append(ds.wps)
		} else {
			d.wps[key] = NewStatSample(ds.wps)
		}

		if entry, has := d.rkbps[key]; has {
			entry.Append(ds.rkbps)
		} else {
			d.rkbps[key] = NewStatSample(ds.rkbps)
		}

		if entry, has := d.wkbps[key]; has {
			entry.Append(ds.wkbps)
		} else {
			d.wkbps[key] = NewStatSample(ds.wkbps)
		}

		if entry, has := d.avgrq_sz[key]; has {
			entry.Append(ds.avgrq_sz)
		} else {
			d.avgrq_sz[key] = NewStatSample(ds.avgrq_sz)
		}

		if entry, has := d.avgqu_sz[key]; has {
			entry.Append(ds.avgqu_sz)
		} else {
			d.avgqu_sz[key] = NewStatSample(ds.avgqu_sz)
		}

		if entry, has := d.await[key]; has {
			entry.Append(ds.await)
		} else {
			d.await[key] = NewStatSample(ds.await)
		}

		if entry, has := d.r_await[key]; has {
			entry.Append(ds.r_await)
		} else {
			d.r_await[key] = NewStatSample(ds.r_await)
		}

		if entry, has := d.w_await[key]; has {
			entry.Append(ds.w_await)
		} else {
			d.w_await[key] = NewStatSample(ds.w_await)
		}

		if entry, has := d.svctm[key]; has {
			entry.Append(ds.svctm)
		} else {
			d.svctm[key] = NewStatSample(ds.svctm)
		}

		if entry, has := d.util[key]; has {
			entry.Append(ds.util)
		} else {
			d.util[key] = NewStatSample(ds.util)
		}

	}

	d.lock.Unlock()
}

func (d *DeviceStatsProcessor) NewSample() ISample {
	return NewSample()
}

func (d *DeviceStatsProcessor) IsHeader(row string) bool {
	return DeviceStatsIsHeader(row)
}

func (d *DeviceStatsProcessor) ParseRow(row string) (IItem, error) {
	return DeviceStatsParseRow(row)
}

func (d *DeviceStatsProcessor) GetAndPurgeRrqmps() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.rrqmps
	d.rrqmps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeWrqmps() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.wrqmps
	d.wrqmps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeRps() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.rps
	d.rps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeWps() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.wps
	d.wps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeRkbps() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.rkbps
	d.rkbps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeWkbps() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.wkbps
	d.wkbps = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeAvgrq_sz() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.avgrq_sz
	d.avgrq_sz = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeAvgqu_sz() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.avgqu_sz
	d.avgqu_sz = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeAwait() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.await
	d.await = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeRawait() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.r_await
	d.r_await = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeWawait() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.w_await
	d.w_await = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeSvctm() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.svctm
	d.svctm = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

func (d *DeviceStatsProcessor) GetAndPurgeUtil() map[string]ISampleStats {
	d.lock.Lock()
	samples := d.util
	d.util = make(map[string]ISampleStats)
	d.lock.Unlock()

	return samples
}

// this could be a method on map[string]*StatSample
func (d *DeviceStatsProcessor) GetUniqKeys(data map[string]ISampleStats) []string {
	var sample_list []string

	for key := range data {
		sample_list = append(sample_list, key)
	}

	return sample_list
}
<|endoftext|>


<commit_before>package usermodel

import (
	"net/http"
	"time"
	"xorkevin.dev/governor"
	"xorkevin.dev/governor/service/db"
	"xorkevin.dev/governor/service/user/role/model"
	"xorkevin.dev/governor/util/rank"
	"xorkevin.dev/governor/util/uid"
	"xorkevin.dev/hunter2"
)

//go:generate forge model -m Model -t users -p user -o model_gen.go Model Info

const (
	uidSize     = 16
	passSaltLen = 32
	passHashLen = 32
	roleLimit   = 1024
)

const (
	// roleAdd indicates adding a role in diff
	roleAdd = iota
	// roleRemove indicates removing a role in diff
	roleRemove
)

type (
	// Repo is a user repository
	Repo interface {
		New(username, password, email, firstname, lastname string, ra rank.Rank) (*Model, error)
		NewEmpty() Model
		NewEmptyPtr() *Model
		ValidatePass(password string, m *Model) (bool, error)
		RehashPass(m *Model, password string) error
		GetRoles(m *Model) error
		GetGroup(limit, offset int) ([]Info, error)
		GetBulk(userids []string) ([]Info, error)
		GetByID(userid string) (*Model, error)
		GetByUsername(username string) (*Model, error)
		GetByEmail(email string) (*Model, error)
		Insert(m *Model) error
		UpdateRoles(m *Model, addRoles, rmRoles []string) error
		Update(m *Model) error
		Delete(m *Model) error
		Setup() error
	}

	repo struct {
		db       db.Database
		rolerepo rolemodel.Repo
		hasher   *hunter2.ScryptHasher
		verifier *hunter2.Verifier
	}

	// Model is the db User model
	Model struct {
		Userid       string `model:"userid,VARCHAR(31) PRIMARY KEY" query:"userid,getoneeq,userid;updeq,userid;deleq,userid"`
		Username     string `model:"username,VARCHAR(255) NOT NULL UNIQUE" query:"username,getoneeq,username"`
		AuthTags     rank.Rank
		PassHash     string `model:"pass_hash,VARCHAR(255) NOT NULL" query:"pass_hash"`
		Email        string `model:"email,VARCHAR(255) NOT NULL UNIQUE" query:"email,getoneeq,email"`
		FirstName    string `model:"first_name,VARCHAR(255) NOT NULL" query:"first_name"`
		LastName     string `model:"last_name,VARCHAR(255) NOT NULL" query:"last_name"`
		CreationTime int64  `model:"creation_time,BIGINT NOT NULL" query:"creation_time"`
	}

	// Info is the metadata of a user
	Info struct {
		Userid    string `query:"userid,getgroup;getgroupeq,userid|arr"`
		Username  string `query:"username"`
		Email     string `query:"email"`
		FirstName string `query:"first_name"`
		LastName  string `query:"last_name"`
	}
)

// New creates a new user repository
func New(database db.Database, rolerepo rolemodel.Repo) Repo {
	hasher := hunter2.NewScryptHasher(passHashLen, passSaltLen, hunter2.NewScryptDefaultConfig())
	verifier := hunter2.NewVerifier()
	verifier.RegisterHash(hasher)

	return &repo{
		db:       database,
		rolerepo: rolerepo,
		hasher:   hasher,
		verifier: verifier,
	}
}

// New creates a new User Model
func (r *repo) New(username, password, email, firstname, lastname string, ra rank.Rank) (*Model, error) {
	mUID, err := uid.New(uidSize)
	if err != nil {
		return nil, governor.NewError("Failed to create new uid", http.StatusInternalServerError, err)
	}

	mHash, err := r.hasher.Hash(password)
	if err != nil {
		return nil, governor.NewError("Failed to hash password", http.StatusInternalServerError, err)
	}

	return &Model{
		Userid:       mUID.Base64(),
		Username:     username,
		AuthTags:     ra,
		PassHash:     mHash,
		Email:        email,
		FirstName:    firstname,
		LastName:     lastname,
		CreationTime: time.Now().Round(0).Unix(),
	}, nil
}

func (r *repo) NewEmpty() Model {
	return Model{}
}

func (r *repo) NewEmptyPtr() *Model {
	return &Model{}
}

// ValidatePass validates the password against a hash
func (r *repo) ValidatePass(password string, m *Model) (bool, error) {
	ok, err := r.verifier.Verify(password, m.PassHash)
	if err != nil {
		return false, governor.NewError("Failed to verify password", http.StatusInternalServerError, err)
	}
	return ok, nil
}

// RehashPass updates the password with a new hash
func (r *repo) RehashPass(m *Model, password string) error {
	mHash, err := r.hasher.Hash(password)
	if err != nil {
		return governor.NewError("Failed to rehash password", http.StatusInternalServerError, err)
	}
	m.PassHash = mHash
	return nil
}

// GetRoles gets the roles of the user for the model
func (r *repo) GetRoles(m *Model) error {
	roles, err := r.rolerepo.GetUserRoles(m.Userid, roleLimit, 0)
	if err != nil {
		return governor.NewError("Failed to get roles of user", http.StatusInternalServerError, err)
	}
	m.AuthTags = rank.FromSlice(roles)
	return nil
}

// GetGroup gets information from each user
func (r *repo) GetGroup(limit, offset int) ([]Info, error) {
	m, err := userModelGetInfoOrdUserid(r.db.DB(), true, limit, offset)
	if err != nil {
		return nil, governor.NewError("Failed to get user info", http.StatusInternalServerError, err)
	}
	return m, nil
}

// GetBulk gets information from users
func (r *repo) GetBulk(userids []string) ([]Info, error) {
	m, err := userModelGetInfoHasUseridOrdUserid(r.db.DB(), userids, true, len(userids), 0)
	if err != nil {
		return nil, governor.NewError("Failed to get user info of userids", http.StatusInternalServerError, err)
	}
	return m, nil
}

func (r *repo) getApplyRoles(m *Model) (*Model, error) {
	if err := r.GetRoles(m); err != nil {
		return nil, err
	}
	return m, nil
}

// GetByID returns a user model with the given id
func (r *repo) GetByID(userid string) (*Model, error) {
	var m *Model
	if mUser, code, err := userModelGetModelEqUserid(r.db.DB(), userid); err != nil {
		if code == 2 {
			return nil, governor.NewError("No user found with that id", http.StatusNotFound, err)
		}
		return nil, governor.NewError("Failed to get user", http.StatusInternalServerError, err)
	} else {
		m = mUser
	}
	return r.getApplyRoles(m)
}

// GetByUsername returns a user model with the given username
func (r *repo) GetByUsername(username string) (*Model, error) {
	var m *Model
	if mUser, code, err := userModelGetModelEqUsername(r.db.DB(), username); err != nil {
		if code == 2 {
			return nil, governor.NewError("No user found with that username", http.StatusNotFound, err)
		}
		return nil, governor.NewError("Failed to get user by username", http.StatusInternalServerError, err)
	} else {
		m = mUser
	}
	return r.getApplyRoles(m)
}

// GetByEmail returns a user model with the given email
func (r *repo) GetByEmail(email string) (*Model, error) {
	var m *Model
	if mUser, code, err := userModelGetModelEqEmail(r.db.DB(), email); err != nil {
		if code == 2 {
			return nil, governor.NewError("No user found with that email", http.StatusNotFound, err)
		}
		return nil, governor.NewError("Failed to get user by email", http.StatusInternalServerError, err)
	} else {
		m = mUser
	}
	return r.getApplyRoles(m)
}

func (r *repo) insertRoles(m *Model) error {
	roles := make([]*rolemodel.Model, 0, len(m.AuthTags))
	for k := range m.AuthTags {
		roles = append(roles, r.rolerepo.New(m.Userid, k))
	}
	if err := r.rolerepo.InsertBulk(roles); err != nil {
		return governor.NewError("Failed to insert user roles", http.StatusInternalServerError, err)
	}
	return nil
}

// Insert inserts the model into the db
func (r *repo) Insert(m *Model) error {
	if code, err := userModelInsert(r.db.DB(), m); err != nil {
		if code == 3 {
			return governor.NewError("User id must be unique", http.StatusBadRequest, err)
		}
		return governor.NewError("Failed to insert user", http.StatusInternalServerError, err)
	}
	if err := r.insertRoles(m); err != nil {
		return err
	}
	return nil
}

// UpdateRoles updates the model's roles into the db
func (r *repo) UpdateRoles(m *Model, addRoles, rmRoles []string) error {
	addModels := make([]*rolemodel.Model, 0, len(addRoles))
	for _, i := range addRoles {
		addModels = append(addModels, r.rolerepo.New(m.Userid, i))
	}

	if len(rmRoles) > 0 {
		if err := r.rolerepo.DeleteRoles(m.Userid, rmRoles); err != nil {
			return governor.NewError("Failed to delete roles", http.StatusInternalServerError, err)
		}
	}
	if len(addModels) > 0 {
		if err := r.rolerepo.InsertBulk(addModels); err != nil {
			return governor.NewError("Failed to insert roles", http.StatusInternalServerError, err)
		}
	}
	return nil
}

// Update updates the model in the db
func (r *repo) Update(m *Model) error {
	if err := userModelUpdModelEqUserid(r.db.DB(), m, m.Userid); err != nil {
		return governor.NewError("Failed to update user", http.StatusInternalServerError, err)
	}
	return nil
}

// Delete deletes the model in the db
func (r *repo) Delete(m *Model) error {
	if err := r.rolerepo.DeleteUserRoles(m.Userid); err != nil {
		return governor.NewError("Failed to delete user roles", http.StatusInternalServerError, err)
	}

	if err := userModelDelEqUserid(r.db.DB(), m.Userid); err != nil {
		return governor.NewError("Failed to delete user", http.StatusInternalServerError, err)
	}
	return nil
}

// Setup creates a new User table
func (r *repo) Setup() error {
	if err := userModelSetup(r.db.DB()); err != nil {
		return governor.NewError("Failed to setup user model", http.StatusInternalServerError, err)
	}
	return nil
}
<commit_msg>Remove unused<commit_after>package usermodel

import (
	"net/http"
	"time"
	"xorkevin.dev/governor"
	"xorkevin.dev/governor/service/db"
	"xorkevin.dev/governor/service/user/role/model"
	"xorkevin.dev/governor/util/rank"
	"xorkevin.dev/governor/util/uid"
	"xorkevin.dev/hunter2"
)

//go:generate forge model -m Model -t users -p user -o model_gen.go Model Info

const (
	uidSize     = 16
	passSaltLen = 32
	passHashLen = 32
	roleLimit   = 1024
)

type (
	// Repo is a user repository
	Repo interface {
		New(username, password, email, firstname, lastname string, ra rank.Rank) (*Model, error)
		NewEmpty() Model
		NewEmptyPtr() *Model
		ValidatePass(password string, m *Model) (bool, error)
		RehashPass(m *Model, password string) error
		GetRoles(m *Model) error
		GetGroup(limit, offset int) ([]Info, error)
		GetBulk(userids []string) ([]Info, error)
		GetByID(userid string) (*Model, error)
		GetByUsername(username string) (*Model, error)
		GetByEmail(email string) (*Model, error)
		Insert(m *Model) error
		UpdateRoles(m *Model, addRoles, rmRoles []string) error
		Update(m *Model) error
		Delete(m *Model) error
		Setup() error
	}

	repo struct {
		db       db.Database
		rolerepo rolemodel.Repo
		hasher   *hunter2.ScryptHasher
		verifier *hunter2.Verifier
	}

	// Model is the db User model
	Model struct {
		Userid       string `model:"userid,VARCHAR(31) PRIMARY KEY" query:"userid,getoneeq,userid;updeq,userid;deleq,userid"`
		Username     string `model:"username,VARCHAR(255) NOT NULL UNIQUE" query:"username,getoneeq,username"`
		AuthTags     rank.Rank
		PassHash     string `model:"pass_hash,VARCHAR(255) NOT NULL" query:"pass_hash"`
		Email        string `model:"email,VARCHAR(255) NOT NULL UNIQUE" query:"email,getoneeq,email"`
		FirstName    string `model:"first_name,VARCHAR(255) NOT NULL" query:"first_name"`
		LastName     string `model:"last_name,VARCHAR(255) NOT NULL" query:"last_name"`
		CreationTime int64  `model:"creation_time,BIGINT NOT NULL" query:"creation_time"`
	}

	// Info is the metadata of a user
	Info struct {
		Userid    string `query:"userid,getgroup;getgroupeq,userid|arr"`
		Username  string `query:"username"`
		Email     string `query:"email"`
		FirstName string `query:"first_name"`
		LastName  string `query:"last_name"`
	}
)

// New creates a new user repository
func New(database db.Database, rolerepo rolemodel.Repo) Repo {
	hasher := hunter2.NewScryptHasher(passHashLen, passSaltLen, hunter2.NewScryptDefaultConfig())
	verifier := hunter2.NewVerifier()
	verifier.RegisterHash(hasher)

	return &repo{
		db:       database,
		rolerepo: rolerepo,
		hasher:   hasher,
		verifier: verifier,
	}
}

// New creates a new User Model
func (r *repo) New(username, password, email, firstname, lastname string, ra rank.Rank) (*Model, error) {
	mUID, err := uid.New(uidSize)
	if err != nil {
		return nil, governor.NewError("Failed to create new uid", http.StatusInternalServerError, err)
	}

	mHash, err := r.hasher.Hash(password)
	if err != nil {
		return nil, governor.NewError("Failed to hash password", http.StatusInternalServerError, err)
	}

	return &Model{
		Userid:       mUID.Base64(),
		Username:     username,
		AuthTags:     ra,
		PassHash:     mHash,
		Email:        email,
		FirstName:    firstname,
		LastName:     lastname,
		CreationTime: time.Now().Round(0).Unix(),
	}, nil
}

func (r *repo) NewEmpty() Model {
	return Model{}
}

func (r *repo) NewEmptyPtr() *Model {
	return &Model{}
}

// ValidatePass validates the password against a hash
func (r *repo) ValidatePass(password string, m *Model) (bool, error) {
	ok, err := r.verifier.Verify(password, m.PassHash)
	if err != nil {
		return false, governor.NewError("Failed to verify password", http.StatusInternalServerError, err)
	}
	return ok, nil
}

// RehashPass updates the password with a new hash
func (r *repo) RehashPass(m *Model, password string) error {
	mHash, err := r.hasher.Hash(password)
	if err != nil {
		return governor.NewError("Failed to rehash password", http.StatusInternalServerError, err)
	}
	m.PassHash = mHash
	return nil
}

// GetRoles gets the roles of the user for the model
func (r *repo) GetRoles(m *Model) error {
	roles, err := r.rolerepo.GetUserRoles(m.Userid, roleLimit, 0)
	if err != nil {
		return governor.NewError("Failed to get roles of user", http.StatusInternalServerError, err)
	}
	m.AuthTags = rank.FromSlice(roles)
	return nil
}

// GetGroup gets information from each user
func (r *repo) GetGroup(limit, offset int) ([]Info, error) {
	m, err := userModelGetInfoOrdUserid(r.db.DB(), true, limit, offset)
	if err != nil {
		return nil, governor.NewError("Failed to get user info", http.StatusInternalServerError, err)
	}
	return m, nil
}

// GetBulk gets information from users
func (r *repo) GetBulk(userids []string) ([]Info, error) {
	m, err := userModelGetInfoHasUseridOrdUserid(r.db.DB(), userids, true, len(userids), 0)
	if err != nil {
		return nil, governor.NewError("Failed to get user info of userids", http.StatusInternalServerError, err)
	}
	return m, nil
}

func (r *repo) getApplyRoles(m *Model) (*Model, error) {
	if err := r.GetRoles(m); err != nil {
		return nil, err
	}
	return m, nil
}

// GetByID returns a user model with the given id
func (r *repo) GetByID(userid string) (*Model, error) {
	var m *Model
	if mUser, code, err := userModelGetModelEqUserid(r.db.DB(), userid); err != nil {
		if code == 2 {
			return nil, governor.NewError("No user found with that id", http.StatusNotFound, err)
		}
		return nil, governor.NewError("Failed to get user", http.StatusInternalServerError, err)
	} else {
		m = mUser
	}
	return r.getApplyRoles(m)
}

// GetByUsername returns a user model with the given username
func (r *repo) GetByUsername(username string) (*Model, error) {
	var m *Model
	if mUser, code, err := userModelGetModelEqUsername(r.db.DB(), username); err != nil {
		if code == 2 {
			return nil, governor.NewError("No user found with that username", http.StatusNotFound, err)
		}
		return nil, governor.NewError("Failed to get user by username", http.StatusInternalServerError, err)
	} else {
		m = mUser
	}
	return r.getApplyRoles(m)
}

// GetByEmail returns a user model with the given email
func (r *repo) GetByEmail(email string) (*Model, error) {
	var m *Model
	if mUser, code, err := userModelGetModelEqEmail(r.db.DB(), email); err != nil {
		if code == 2 {
			return nil, governor.NewError("No user found with that email", http.StatusNotFound, err)
		}
		return nil, governor.NewError("Failed to get user by email", http.StatusInternalServerError, err)
	} else {
		m = mUser
	}
	return r.getApplyRoles(m)
}

func (r *repo) insertRoles(m *Model) error {
	roles := make([]*rolemodel.Model, 0, len(m.AuthTags))
	for k := range m.AuthTags {
		roles = append(roles, r.rolerepo.New(m.Userid, k))
	}
	if err := r.rolerepo.InsertBulk(roles); err != nil {
		return governor.NewError("Failed to insert user roles", http.StatusInternalServerError, err)
	}
	return nil
}

// Insert inserts the model into the db
func (r *repo) Insert(m *Model) error {
	if code, err := userModelInsert(r.db.DB(), m); err != nil {
		if code == 3 {
			return governor.NewError("User id must be unique", http.StatusBadRequest, err)
		}
		return governor.NewError("Failed to insert user", http.StatusInternalServerError, err)
	}
	if err := r.insertRoles(m); err != nil {
		return err
	}
	return nil
}

// UpdateRoles updates the model's roles into the db
func (r *repo) UpdateRoles(m *Model, addRoles, rmRoles []string) error {
	addModels := make([]*rolemodel.Model, 0, len(addRoles))
	for _, i := range addRoles {
		addModels = append(addModels, r.rolerepo.New(m.Userid, i))
	}

	if len(rmRoles) > 0 {
		if err := r.rolerepo.DeleteRoles(m.Userid, rmRoles); err != nil {
			return governor.NewError("Failed to delete roles", http.StatusInternalServerError, err)
		}
	}
	if len(addModels) > 0 {
		if err := r.rolerepo.InsertBulk(addModels); err != nil {
			return governor.NewError("Failed to insert roles", http.StatusInternalServerError, err)
		}
	}
	return nil
}

// Update updates the model in the db
func (r *repo) Update(m *Model) error {
	if err := userModelUpdModelEqUserid(r.db.DB(), m, m.Userid); err != nil {
		return governor.NewError("Failed to update user", http.StatusInternalServerError, err)
	}
	return nil
}

// Delete deletes the model in the db
func (r *repo) Delete(m *Model) error {
	if err := r.rolerepo.DeleteUserRoles(m.Userid); err != nil {
		return governor.NewError("Failed to delete user roles", http.StatusInternalServerError, err)
	}

	if err := userModelDelEqUserid(r.db.DB(), m.Userid); err != nil {
		return governor.NewError("Failed to delete user", http.StatusInternalServerError, err)
	}
	return nil
}

// Setup creates a new User table
func (r *repo) Setup() error {
	if err := userModelSetup(r.db.DB()); err != nil {
		return governor.NewError("Failed to setup user model", http.StatusInternalServerError, err)
	}
	return nil
}
<|endoftext|>