<|start_filename|>UDPush/UDPush.go<|end_filename|>
/*
** Observer.go
** Author: <NAME>
** Mail   <<EMAIL>>
** Started on  Mon Feb 09 14:36:00 2015 <NAME>
** Last update Thu Feb 19 15:45:08 2015 <NAME>
 */

package UDPush

import (
	"fmt"
	"net"
)

// Constants

// MAX NUMBER PER WATCHER ENGINE

const maxClients = 10

type update struct {
	status  bool
	ownerID int
}

// NotificationEngine interface for the notification system
// Defines the requirements to create a gobox
type NotificationEngine interface {
	Initialize(id string)
	Attach(Watcher) error
	Detach(Watcher) bool
	Notify()
}

// WatcherEngine Interface for watcher (Observer) system
// Defines the requirements to create a gobox
// notification watcher.
type WatcherEngine interface {
	Update()
}

//Pusher struct that satisfies the NotificationEngine interface
type Pusher struct {
	ServerID string
	BindedTo uint
	Watchers map[string]Watcher
	Pending  bool
}

// Watcher Struct that satisfies the WatcherEngine
// This type requires an auth mecanism in order
// to work in a safe way
type Watcher struct {
	OwnerID    int
	ClientID   int
	SessionKey string
	Action     bool
	Connection net.Conn
}

// Methods for struct to satisfy the notificationEngine interface

//Initialize is a 'constructor' for the pusher struct
func (e *Pusher) Initialize(id string) {
	e.ServerID = id
	e.Watchers = make(map[string]Watcher, maxClients)
}

//Attach Add a new Watcher to the notification slice
func (e *Pusher) Attach(w Watcher) (err error) {
	//Check if Watchers is full
	if len(e.Watchers) == maxClients {
		return fmt.Errorf("[!] Error: Not enough space for new client")
	}
	//Check if element already exists
	if _, k := e.Watchers[w.SessionKey]; k {
		return fmt.Errorf("[!] Warning: client already monitored, skipping addition")
	}
	fmt.Println("Client registered ", w.SessionKey)
	e.Watchers[w.SessionKey] = w
	return nil
}

//Detach Remove a watcher from the notification slice
func (e *Pusher) Detach(w Watcher) (err error) {
	//Check if element already exists
	if item, ok := e.Watchers[w.SessionKey]; ok {
		item.Connection.Close()
		delete(e.Watchers, w.SessionKey)
		return nil
	}
	return fmt.Errorf("[!] Error: client doesn't exist")
}

//Notify Tell the watcher {clientID} to update
func (e *Pusher) Notify(sessionkey string) {
	for _, k := range e.Watchers {
		if k.SessionKey != sessionkey {
			k.Update()
			k.Action = true
		}
	}
}

//Utilities for pusher

//ShowWatchers Print current watchers in pusher
func (e *Pusher) ShowWatchers() {
	for _, k := range e.Watchers {
		fmt.Println("Watcher: ", k)
	}
}

// Methods for satisfiying the interface

// Update Get update from pusher... Golint forces me to do this
// http://tinyurl.com/lhzjvmm
func (w *Watcher) Update() {
	w.Action = true
	fits := w.SessionKey[:2]
	fmt.Println("[!] Attempting to update watcher: %s", fits)
	writen, err := w.Connection.Write([]byte("Y"))
	if writen != len([]byte("Y")) {
		fmt.Println("[!]Error writting: unable to write")
	}
	if err != nil {
		fmt.Printf("%s", err)
	}

}

//Network related methods

func getPendingUpdates() update {
	return update{status: true,
		ownerID: 1}
}

//InitUDPush 'constructs' the UDP notification engine
//The e on the reciever stands for event
func (e *Pusher) InitUDPush() error {
	//Initialize the map
	e.Watchers = make(map[string]Watcher, maxClients)
	connectionString := fmt.Sprintf("%s:%d", e.ServerID, e.BindedTo)
	ln, err := net.Listen("tcp", connectionString)
	if err != nil {
		return fmt.Errorf("Error at initUDPush: %s", err)
	}
	fmt.Println("[+] UDP Listening on: ", connectionString)
	for {
		conn, err := ln.Accept()
		fmt.Println("Host connected: ", ln.Addr())
		if err != nil {
			return fmt.Errorf("Error at initUDPush: %s", err)
		}
		session := make([]byte, 64)
		conn.Read(session)
		e.Attach(Watcher{
			SessionKey: string(session),
			Connection: conn,
		})
	}
}


<|start_filename|>client/api/api_test.go<|end_filename|>
package api

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"testing"

	"github.com/golangbox/gobox/boxtools"
	server_api "github.com/golangbox/gobox/server/api"
	"github.com/golangbox/gobox/server/model"
	"github.com/golangbox/gobox/structs"
	"github.com/jinzhu/gorm"
)

var user structs.User
var client structs.Client
var apiClient Api

func init() {
	model.DB, _ = gorm.Open("postgres", "dbname=goboxtest sslmode=disable")

	model.DB.DropTableIfExists(&structs.User{})
	model.DB.DropTableIfExists(&structs.Client{})
	model.DB.DropTableIfExists(&structs.FileAction{})
	model.DB.DropTableIfExists(&structs.File{})
	model.DB.DropTableIfExists(&structs.FileSystemFile{})
	model.DB.AutoMigrate(&structs.User{}, &structs.Client{}, &structs.FileAction{}, &structs.File{}, &structs.FileSystemFile{})

	user, _ = boxtools.NewUser("<EMAIL>", "password")

	var err error
	client, err = boxtools.NewClient(user, "test", false)
	if err != nil {
		fmt.Println(err)
	}

	apiClient = New(client.SessionKey)

	go server_api.ServeServerRoutes("8000")
}

func TestSendFileActionsToServer(t *testing.T) {
	fileActions, _ := boxtools.GenerateSliceOfRandomFileActions(1, 1, 1)

	var hashesToUpload []string
	var err error
	hashesToUpload, err = apiClient.SendFileActionsToServer(fileActions)
	if err != nil {
		t.Error(err)
	}

	if fileActions[0].File.Hash != hashesToUpload[0] {
		t.Error(fmt.Errorf("Wrong hash returned"))
	}
}

func TestDownloadFileFromServer(t *testing.T) {
	testFile := structs.File{
		Hash:   "fc45acaffc35a3aa674f7c0d5a03d22350b4f2ff4bf45ccebad077e5af80e512",
		UserId: user.Id,
	}
	model.DB.Create(&testFile)

	url, err := apiClient.DownloadFileFromServer("fc45acaffc35a3aa674f7c0d5a03d22350b4f2ff4bf45ccebad077e5af80e512")
	if err != nil {
		t.Error(err)
	}
	_ = url
	// futher tested to confirm functioanlity in
	// TestUploadFileToServer
}

func TestUploadFileToServer(t *testing.T) {
	file := []byte("this is a file")
	err := apiClient.UploadFileToServer(file)
	if err != nil {
		t.Error(err)
	}

	expectedHash := "fc45acaffc35a3aa674f7c0d5a03d22350b4f2ff4bf45ccebad077e5af80e512"
	testFile := structs.File{
		Hash:   expectedHash,
		UserId: user.Id,
	}
	model.DB.Create(&testFile)

	s3_url, err := apiClient.DownloadFileFromServer(
		expectedHash,
	)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	resp, err := http.Get(s3_url)
	if err != nil {
		t.Error(err)
	}
	contents, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Error(err)
	}

	if string(contents) != "this is a file" {
		t.Error(fmt.Errorf("S3 file contents don't match"))
	}
}


<|start_filename|>server/s3/s3.go<|end_filename|>
package s3

import (
	"os"
	"time"

	"github.com/golangbox/goamz/aws"
	"github.com/golangbox/goamz/s3"
)

var client *s3.S3
var bucket *s3.Bucket

func init() {
	key := os.Getenv("GOBOX_AWS_ACCESS_KEY_ID")
	secret := os.Getenv("GOBOX_AWS_SECRET_ACCESS_KEY")
	auth := aws.Auth{AccessKey: key, SecretKey: secret}
	client = s3.New(auth, aws.Regions["us-west-2"])
	bucket = client.Bucket("gobox")
}

func TestKeyExistence(hash string) (exists bool, err error) {
	// this is expensive, both in terms of time and $
	// maybe store the s3 values in a db?
	exists, err = bucket.Exists(hash)
	return exists, err
}

func GenerateSignedUrl(hash string) (url string, err error) {
	return bucket.SignedURL(hash, time.Now().Add(time.Duration(time.Minute*10)))
}

func UploadFile(hash string, fileBody []byte) error {
	var options s3.Options
	return bucket.Put(hash, fileBody, "", s3.PublicRead, options)
}


<|start_filename|>client/client_test.go<|end_filename|>
package main

import (
	"fmt"
	"time"

	"github.com/golangbox/gobox/boxtools"
	"github.com/golangbox/gobox/structs"

	"path/filepath"
	"reflect"
	"testing"
)

const (
	sandboxDir = "../sandbox"
)

func TestStartWatcher(t *testing.T) {
	_, err := startWatcher("/billybob")
	if err == nil {
		t.Log("startWatcher must fail on invalid directory")
		t.FailNow()
	}

	ch, err := startWatcher(sandboxDir)
	if err != nil {
		t.Log("startWatcher didn't work on a valid directory")
		t.FailNow()
	}

	if reflect.ValueOf(ch).Kind() != reflect.Chan {
		t.Log("Return value from startWatcher must be a channel")
		t.FailNow()
	}
	go boxtools.SimulateFilesystemChanges(sandboxDir, 10, 5, 0)
	for i := 0; i < 18; i++ {
		fmt.Println(<-ch)
		fmt.Println(i)

	}
	ignores := make(map[string]bool)
	ignores[".Gobox"] = true
	abspath, err := filepath.Abs(sandboxDir)
	if err != nil {
		t.Log("Could not clean up properly")
		t.FailNow()
	}
	err = boxtools.CleanTestFolder(abspath, ignores, true)
	if err != nil {
		t.Log(err.Error())
		t.FailNow()
	}
	return
}

func TestServerActions(t *testing.T) {

}

func TestFanActionsIn(t *testing.T) {

	ch1, ch2 := make(chan structs.StateChange), make(chan structs.StateChange)
	out := fanActionsIn(ch1, ch2)
	numRead := 0
	go func() { ch1 <- structs.StateChange{} }()
	go func() { ch2 <- structs.StateChange{} }()
	timeout := time.Tick(1000 * time.Millisecond)
	timedOut := false
	for !timedOut {
		select {
		case <-out:
			numRead++
		case <-timeout:
			timedOut = true
		}
	}
	if numRead != 2 {
		t.FailNow()
	}

}

func TestArbitraryFanIn(t *testing.T) {
	validNums := make(map[int]bool)
	newChans := make(chan chan interface{})
	out := make(chan interface{})
	arbitraryFanIn(newChans, out, true)
	chans := make([]chan interface{}, 0)
	for i := 0; i < 10; i++ {
		chans = append(chans, make(chan interface{}, 1))
		newChans <- chans[i]
	}

	for i := 0; i < 5; i++ {
		chans[i] <- interface{}(i)
		validNums[i] = true
	}

	for i := 10; i < 20; i++ {
		chans = append(chans, make(chan interface{}, 1))
		newChans <- chans[i]
		chans[i] <- interface{}(i)
		validNums[i] = true
	}

	timeout := time.Tick(1000 * time.Microsecond)
	timedOut := false
	for !timedOut {
		select {
		case v := <-out:
			fmt.Println(v, v.(int))
			if _, found := validNums[v.(int)]; !found {
				fmt.Println(found)
				t.FailNow()
			}
		case <-timeout:
			timedOut = true
		}
	}
}

func TestStephen(t *testing.T) {
	run(sandboxDir)
	go boxtools.SimulateFilesystemChanges(sandboxDir, 10, 5, 0)
	for {
		time.Sleep(1000)
	}
}

func TestHasherQuitsProperly(t *testing.T) {

}


<|start_filename|>server/server.go<|end_filename|>
package server

import (
	"fmt"
	"log"

	"github.com/golangbox/gobox/UDPush"
	"github.com/golangbox/gobox/boxtools"
	"github.com/golangbox/gobox/server/api"
)

type services struct {
	s3     bool
	api    bool
	udpush bool
}

type server struct {
	services
	name        string
	ip          string
	port        uint
	clientLimit uint
	status      func() bool
	display     func() string
}

func (s *server) checkStatus() bool {
	if s.services.api == true &&
		s.services.s3 == true &&
		s.services.udpush == true {
		return true
	}
	return false
}

func createDummyUser() error {
	user, err := boxtools.NewUser("<EMAIL>", "password")
	if err != nil {
		return err
	}
	_ = user
	return nil
}

//Run creates all the structures to make or project work
func Run() {

	//Launch API

	s := server{
		name:        "Elvis",
		ip:          "127.0.0.1",
		port:        4242,
		clientLimit: 10,
	}

	// model.DB, _ = gorm.Open(
	// 	"postgres",
	// 	"dbname=gobox sslmode=disable",
	// )
	// model.DB.AutoMigrate(
	// 	&structs.User{},
	// 	&structs.Client{},
	// 	&structs.FileAction{},
	// 	&structs.File{},
	// 	&structs.FileSystemFile{},
	// )

	err := createDummyUser()
	if err != nil {
		log.Fatal(err)
	}
	////Launch UDP notification service
	////Define the Subject (The guy who is goin to hold all the clients)

	pusher := &UDPush.Pusher{
		ServerID: s.ip,
		BindedTo: s.port,
	}

	go func() {
		err = pusher.InitUDPush()
		if err != nil {
			fmt.Println(err)
		}
	}()
	api.ServeServerRoutes("8000", pusher)
}


<|start_filename|>backup/old_client.go<|end_filename|>
package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/golangbox/gobox/structs"
)

const (
	goBoxDirectory     = "."
	goBoxDataDirectory = ".GoBox"
	serverEndpoint     = "http://requestb.in/1mv9fa41"
	// serverEndpoint           = "http://www.google.com"
	filesystemCheckFrequency = 5
)

func main() {
	fmt.Println("Running GoBox...")

	createGoBoxLocalDirectory()

	go monitorFiles()

	select {}

}

func createGoBoxLocalDirectory() {

	_, err := os.Stat(goBoxDataDirectory)
	if err != nil {
		fmt.Println(err)
		fmt.Println("Making directory")
		err := os.Mkdir(goBoxDataDirectory, 0777)
		if err != nil {
			log.Fatal(err)
		}
	}
}

func monitorFiles() {

	var newfileInfos = make(map[string]structs.File)
	var fileInfos = make(map[string]structs.File)

	data, err := ioutil.ReadFile(goBoxDataDirectory + "/data")
	if err != nil {
		fmt.Println(err)
	}
	if data != nil {
		err = json.Unmarshal(data, &fileInfos)
		if err != nil {
			fmt.Println(err)
		}
	}
	for _ = range time.Tick(filesystemCheckFrequency * time.Second) {
		fmt.Println("Checking to see if there are any filesystem changes.")
		newfileInfos, err = findFilesInDirectory(goBoxDirectory)
		if err != nil {
			fmt.Println(err)
		}
		err := compareFileInfos(fileInfos, newfileInfos)
		if err != nil {
			fmt.Println(err)
			fmt.Println("Error uploading file changes, skipping this upload cycle")
		} else {
			err = writefileInfosToLocalFile(newfileInfos)
			if err != nil {
				fmt.Println(err)
			} else {
				// fmt.Println("Saved data locally")
			}
			fileInfos = newfileInfos
		}
	}
}

func writefileInfosToLocalFile(fileInfos map[string]structs.File) error {
	jsonBytes, err := json.Marshal(fileInfos)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(goBoxDataDirectory+"/data", jsonBytes, 0644)
	return err
}

func handleFileChange(isCreate bool, file structs.File) (err error) {
	infoToSend := structs.FileAction{
		IsCreate: isCreate,
		File:     file,
	}
	resp, err := uploadMetadata(infoToSend)
	if err != nil {
		return
	}
	if resp.StatusCode != 200 {
		err = fmt.Errorf("Error uploading metadata, status code: %d", resp.StatusCode)
		return
	}

	return
}

func uploadMetadata(uploadinfo structs.FileAction) (resp *http.Response, err error) {
	fmt.Println("Uploading metadata: (" + uploadinfo.File.Name + ")")
	jsonBytes, err := json.Marshal(uploadinfo)
	if err != nil {
		return
	}
	resp, err = http.Post(serverEndpoint+"/meta", "application/json", bytes.NewBuffer(jsonBytes))
	fmt.Println(resp)
	if err != nil {
		return resp, err
	}
	if uploadinfo.IsCreate == true {
		var contents []byte
		contents, err = ioutil.ReadAll(resp.Body)
		err := resp.Body.Close()
		if err != nil {
			return resp, err
		}
		fmt.Println(string(contents))
		if string(contents) == "true" {
			resp, err = uploadFile(uploadinfo.File.Path)
			if err != nil {
				return resp, err
			}
		} else {
			fmt.Println("no need for upload")
		}
	}
	if err != nil {
		return
	}
	return
}

func compareFileInfos(fileInfos map[string]structs.File,
	newfileInfos map[string]structs.File) (err error) {

	for key, value := range newfileInfos {
		// http://stackoverflow.com/questions/2050391/how-to-test-key-existence-in-a-map
		if _, exists := fileInfos[key]; !exists {
			fmt.Println("Need to Upload:", value.Name)
			err = handleFileChange(true, value)
			if err != nil {
				return err
			}
		}
	}
	for key, value := range fileInfos {
		if _, exists := newfileInfos[key]; !exists {
			fmt.Println("Need to delete:", key)
			err = handleFileChange(false, value)
			if err != nil {
				return err
			}
		}
	}
	return
}

// http://matt.aimonetti.net/posts/2013/07/01/golang-multipart-file-upload-example/
func newfileUploadRequest(uri string, params map[string]string,
	paramName, path string) (*http.Request, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(paramName, filepath.Base(path))
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, file)

	for key, val := range params {
		_ = writer.WriteField(key, val)
	}
	err = writer.Close()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", uri, body)
	req.Header.Add("Content-Type", writer.FormDataContentType())

	return req, err
}

func getSha256FromFilename(filename string) (sha256String string,
	err error) {
	file, err := ioutil.ReadFile(filename)
	if err != nil {
		return "", fmt.Errorf("Error reading file for sha256: %s", err)
	}
	h := sha256.New()
	_, err = h.Write(file)
	if err != nil {
		return "", fmt.Errorf("Error writing file to hash for sha256: %s", err)
	}
	byteString := h.Sum(nil)

	sha256String = hex.EncodeToString(byteString)

	return sha256String, nil
}

func uploadFile(name string) (*http.Response, error) {
	// filename := "main.go"
	file, _ := os.Stat(name)
	s, err := getSha256FromFilename(name)
	if err != nil {
		fmt.Println(err)
	}

	size := strconv.Itoa(int(file.Size()))
	extraParams := map[string]string{
		"Name": file.Name(),
		"Hash": s,
		"Size": size,
	}

	// http://requestb.in/19w82ne1
	//"http://10.0.7.205:4242/upload"
	request, err := newfileUploadRequest("http://10.0.7.205:4242/upload", extraParams, "FileName", name)
	if err != nil {
		return nil, err
	}
	client := &http.Client{}
	// resp, err := client.Do(request)
	return client.Do(request)
}

func mapKeyValue(path string, sha256 string) (key string) {
	return path + "-" + sha256
}

func findFilesInDirectoryHelper(directory string,
	fileInfos map[string]structs.File) (outputfileInfos map[string]structs.File, err error) {
	files, err := ioutil.ReadDir(directory)
	if err != nil {
		return nil, fmt.Errorf("Unable to read directory: %s", err)
	}

	for _, f := range files {
		name := f.Name()
		path := directory + "/" + name

		// fmt.Println("Scanning file: " + path)
		if f.IsDir() {
			if f.Name() != goBoxDataDirectory {
				fileInfos, err = findFilesInDirectoryHelper(path, fileInfos)
			}
		} else {
			sha256, err := getSha256FromFilename(path)
			if err != nil {
				fmt.Println(err)
			}

			fileInfos[mapKeyValue(path, sha256)] = structs.File{
				Name:     name,
				Hash:     sha256,
				Size:     f.Size(),
				Path:     path,
				Modified: f.ModTime(),
			}
		}
	}

	return fileInfos, err
}

func findFilesInDirectory(directory string) (outputfileInfos map[string]structs.File, err error) {
	emptyfileInfos := make(map[string]structs.File)
	return findFilesInDirectoryHelper(directory, emptyfileInfos)
}


<|start_filename|>structs/structs.go<|end_filename|>
package structs

import "time"

type StateChange struct {
	File         File
	IsCreate     bool
	IsLocal      bool
	Quit         <-chan bool
	Done         chan<- interface{}
	Error        chan<- interface{}
	PreviousHash string
}

type CurrentAction struct {
	Quit     chan<- bool
	IsCreate bool
	IsLocal  bool
}

type ClientFileActionsResponse struct {
	LastId      int64
	FileActions []FileAction
}

type ErrorMessage struct {
	Error    error
	File     File
	Function string
}

type FileSystemState struct {
	FileActionId int64
	State        map[string]File
}

type User struct {
	Id             int64
	Email          string `sql:"type:text;"`
	HashedPassword string
	CreatedAt      time.Time
	UpdatedAt      time.Time
	DeletedAt      time.Time
}

type Client struct {
	Id                      int64
	UserId                  int64
	SessionKey              string
	Name                    string
	IsServer                bool
	LastSynchedFileActionId int64
	CreatedAt               time.Time
	UpdatedAt               time.Time
	DeletedAt               time.Time
}

type FileAction struct {
	Id           int64
	ClientId     int64
	IsCreate     bool
	CreatedAt    time.Time
	PreviousHash string
	File         File
	FileId       int64
}

type File struct {
	Id        int64
	UserId    int64
	Name      string
	Hash      string
	Size      int64
	Modified  time.Time
	Path      string `sql:"type:text;"`
	CreatedAt time.Time
}

type FileSystemFile struct {
	Id     int64
	UserId int64
	FileId int64
	Path   string `sql:"type:text;"`
	File   File
}


<|start_filename|>client/watcher/watcher.go<|end_filename|>
package watcher

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/go-fsnotify/fsnotify"
	"github.com/golangbox/gobox/structs"
)

const (
	CREATE = 0
	MODIFY = 1
	DELETE = 2
)

type RecursiveWatcher struct {
	*fsnotify.Watcher
	Files   chan structs.StateChange
	Folders chan string
}

func NewRecursiveWatcher(path string) (*RecursiveWatcher, error) {
	folders := Subfolders(path)
	fmt.Println(folders)
	if len(folders) == 0 {
		return nil, errors.New("No folders to watch.")
	}

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}
	rw := &RecursiveWatcher{Watcher: watcher}

	rw.Files = make(chan structs.StateChange, 10)
	rw.Folders = make(chan string, len(folders))

	for _, folder := range folders {
		rw.AddFolder(folder)
	}
	return rw, nil
}

func (watcher *RecursiveWatcher) AddFolder(folder string) {
	err := watcher.Add(folder)
	if err != nil {
		log.Println("Error watching: ", folder, err)
	}
	// watcher.Folders <- folder
}

func CreateLocalStateChange(path string, eventType int) (change structs.StateChange, err error) {
	fi, err := os.Stat(path)
	if err != nil {
		if !(eventType == DELETE && os.IsNotExist(err)) {
			fmt.Println("returning")
			return
		}
		err = nil
	}
	change.IsCreate = (eventType != DELETE)
	change.IsLocal = true
	change.File.Path = path
	if eventType != DELETE {
		change.File.Name = fi.Name()
		change.File.Size = fi.Size()
		change.File.Modified = fi.ModTime()
		// hmmm, what do we do if the file wasn't created? os.Stat doesn't provide created
		if eventType == CREATE {
			change.File.CreatedAt = fi.ModTime()
		}
	}
	return
}

func (watcher *RecursiveWatcher) Run(initScanDone <-chan struct{}, debug bool) {
	go func() {
		<-initScanDone
		fmt.Println("recieved init scan done signal")
		for {
			select {
			case event := <-watcher.Events:
				if ext := filepath.Ext(event.Name); ext == ".tmp" {
					continue
				}
				if strings.HasPrefix(event.Name, ".Gobox") {
					continue
				}
				// absPath, err := filepath.Abs(event.Name)
				// if err == nil {
				// 	event.Name = absPath
				// }

				// create a file/directory
				if event.Op&fsnotify.Create == fsnotify.Create {
					fi, err := os.Stat(event.Name)
					if err != nil {
						// eg. stat .subl513.tmp : no such file or directory
						if debug {
							// DebugError(err)
						}
					} else if fi.IsDir() {
						if debug {
							// DebugMessage("Detected new directory %s", event.Name)
						}
						if !shouldIgnoreFile(filepath.Base(event.Name)) {
							fmt.Println("adding folder: ", event.Name)
							watcher.AddFolder(event.Name)
						}
					} else {
						if debug {
							// DebugMessage("Detected new file %s", event.Name)
						}

						change, err := CreateLocalStateChange(event.Name, CREATE)
						if err != nil {
							continue
						}
						watcher.Files <- change
					}
				}

				if event.Op&fsnotify.Write == fsnotify.Write {
					// modified a file, assuming that you don't modify folders
					if debug {
						// DebugMessage("Detected file modification %s", event.Name)
					}

					change, err := CreateLocalStateChange(event.Name, MODIFY)
					if err != nil {
						continue
					}
					watcher.Files <- change
				}
				if event.Op&fsnotify.Remove == fsnotify.Remove {
					fmt.Println("Got the remove")
					change, err := CreateLocalStateChange(event.Name, DELETE)
					if err != nil {
						fmt.Println("error")
						continue
					}
					watcher.Files <- change

				}

			case err := <-watcher.Errors:
				log.Println("error", err)
			}
		}
	}()
}

// Subfolders returns a slice of subfolders (recursive), including the folder provided.
func Subfolders(path string) (paths []string) {
	filepath.Walk(path, func(newPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			name := info.Name()
			// skip folders that begin with a dot
			if shouldIgnoreFile(name) && name != "." && name != ".." {
				return filepath.SkipDir
			}
			paths = append(paths, newPath)
		}
		return nil
	})
	return paths
}

// shouldIgnoreFile determines if a file should be ignored.
// File names that begin with "." or "_" are ignored by the go tool.
func shouldIgnoreFile(name string) bool {
	return strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_")
}


<|start_filename|>server/api/api_test.go<|end_filename|>
package api

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"testing"

	"github.com/golangbox/gobox/boxtools"
	"github.com/golangbox/gobox/server/model"
	"github.com/golangbox/gobox/server/s3"
	"github.com/golangbox/gobox/structs"
	"github.com/jinzhu/gorm"
)

var user structs.User
var client structs.Client

func init() {
	model.DB, _ = gorm.Open("postgres", "dbname=goboxtest sslmode=disable")

	model.DB.DropTableIfExists(&structs.User{})
	model.DB.DropTableIfExists(&structs.Client{})
	model.DB.DropTableIfExists(&structs.FileAction{})
	model.DB.DropTableIfExists(&structs.File{})
	model.DB.DropTableIfExists(&structs.FileSystemFile{})
	model.DB.AutoMigrate(
		&structs.User{},
		&structs.Client{},
		&structs.FileAction{},
		&structs.File{},
		&structs.FileSystemFile{},
	)

	user, _ = boxtools.NewUser("<EMAIL>", "password")

	var err error
	client, err = boxtools.NewClient(user, "test", false)
	if err != nil {
		fmt.Println(err)
	}

	go ServeServerRoutes("8000")
}

// func httpErrorCheck(err error, statusCode int, w http.ResponseWriter)

func TestClientsFileActionsHandler(t *testing.T) {
	_, _ = boxtools.NewClient(user, "test", false)
	fileActions, _ := boxtools.GenerateSliceOfRandomFileActions(1, 1, 10)
	for _, value := range fileActions {
		model.DB.Create(&value)
	}
	resp, _ := http.PostForm(
		"http://localhost:8000/clients/",
		url.Values{
			"sessionKey": {client.SessionKey},
			"lastId":     {"0"},
		},
	)
	contents, _ := ioutil.ReadAll(resp.Body)
	if resp.StatusCode != 200 {
		t.Fail()
	}
	var incomingFileActions structs.ClientFileActionsResponse
	json.Unmarshal(contents, &incomingFileActions)

	if len(incomingFileActions.FileActions) != 10 {
		t.Fail()
	}
}

func TestUploadHandler(t *testing.T) {
	file := []byte("These are the file contents")
	resp, _ := http.Post(
		"http://localhost:8000/upload/?sessionKey="+client.SessionKey,
		"",
		bytes.NewBuffer(file),
	)
	contents, _ := ioutil.ReadAll(resp.Body)
	if resp.StatusCode != 200 {
		err := fmt.Errorf(string(contents))
		t.Error(err)
	}
	h := sha256.New()
	h.Write(file)
	byteString := h.Sum(nil)
	sha256String := hex.EncodeToString(byteString)

	url, _ := s3.GenerateSignedUrl(sha256String)

	resp, _ = http.Get(url)

	contents, _ = ioutil.ReadAll(resp.Body)

	if string(contents) != string(file) {
		t.Fail()
	}
}

func TestApiCallWithWrongAndNoAuth(t *testing.T) {
	resp, err := http.PostForm(
		"http://localhost:8000/file-actions/",
		url.Values{"sessionKey": {"nope"}},
	)
	if err != nil {
		t.Error(err)
	}
	if resp.StatusCode != 401 {
		t.Error("Wrong status code")
	}

	resp, err = http.Post(
		"http://localhost:8000/file-actions/",
		"text",
		bytes.NewBuffer([]byte("wheee")),
	)
	if err != nil {
		t.Error(err)
	}
	if resp.StatusCode != 401 {
		t.Error("Wrong status code")
	}
}

func TestFileActionsHandler(t *testing.T) {
	fileActions, _ := boxtools.GenerateSliceOfRandomFileActions(1, 1, 2)
	var bothfileActions []structs.FileAction
	for _, value := range fileActions {
		bothfileActions = append(bothfileActions, value)
		bothfileActions = append(bothfileActions, value)
	}
	jsonBytes, _ := json.Marshal(bothfileActions)
	resp, _ := http.Post(
		"http://localhost:8000/file-actions/?sessionKey="+client.SessionKey,
		"application/json",
		bytes.NewBuffer(jsonBytes),
	)
	contents, _ := ioutil.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		fmt.Println(resp)
		t.Fail()
	}
	_ = contents
	// var responseSlice []string
	// json.Unmarshal(contents, &responseSlice)
	// if len(responseSlice) != 2 {
	// 	fmt.Println(len(responseSlice))
	// 	fmt.Println(responseSlice)
	// 	t.Fail()
	// }
}

func TestFileDownloadHandler(t *testing.T) {
	file, _ := boxtools.GenerateRandomFile(1)
	model.DB.Create(&file)
	resp, err := http.PostForm(
		"http://localhost:8000/download/",
		url.Values{"sessionKey": {client.SessionKey}, "fileHash": {file.Hash}},
	)
	if err != nil {
		t.Error(err)
	}
	contents, _ := ioutil.ReadAll(resp.Body)
	if resp.StatusCode != 200 {
		// t.Fail()
	}
	url := string(contents)
	_ = url
	//not sure how we check to see if the url is valid
}


<|start_filename|>server/s3/s3_test.go<|end_filename|>
package s3

import (
	"io/ioutil"
	"net/http"
	"testing"
)

const (
	validS3Key        = "test"
	validS3KeyContent = "<KEY>"
)

func TestTestKeyExistence(t *testing.T) {
	var exists bool
	var err error

	exists, err = TestKeyExistence(validS3Key)
	if exists != true {
		t.Fail()
	}
	if err != nil {
		t.Error(err)
	}
	exists, err = TestKeyExistence("notvalid")
	if exists == true {
		t.Fail()
	}
	if err != nil {
		t.Error(err)
	}
}

func TestGenerateSignedUrl(t *testing.T) {

	url, err := GenerateSignedUrl(validS3Key)
	_ = url
	if err != nil {
		t.Error(err)
	}

	resp, err := http.Get(url)
	contents, err := ioutil.ReadAll(resp.Body)
	if validS3KeyContent != string(contents) {
		t.Fail()
	}
}

func TestFileUpload(t *testing.T) {
	testString := "file thing"
	file := []byte(testString)
	err := UploadFile("test2", file)
	if err != nil {
		t.Error(err)
	}
	getByte, err := bucket.Get("test2")
	if err != nil {
		t.Error(err)
	}
	if string(file) != string(getByte) {
		t.Fail()
	}
}


<|start_filename|>main_test.go<|end_filename|>
package main

import (
	"os"
	"os/exec"
	"testing"
	"time"

	// "github.com/golangbox/gobox/boxtools"
	"github.com/golangbox/gobox/boxtools"
	"github.com/golangbox/gobox/server"
	"github.com/golangbox/gobox/server/model"
	"github.com/golangbox/gobox/structs"
	"github.com/jinzhu/gorm"
)

func init() {
	model.DB, _ = gorm.Open("postgres", "dbname=goboxtest sslmode=disable")

	model.DB.DropTableIfExists(&structs.User{})
	model.DB.DropTableIfExists(&structs.Client{})
	model.DB.DropTableIfExists(&structs.FileAction{})
	model.DB.DropTableIfExists(&structs.File{})
	model.DB.DropTableIfExists(&structs.FileSystemFile{})
	model.DB.AutoMigrate(
		&structs.User{},
		&structs.Client{},
		&structs.FileAction{},
		&structs.File{},
		&structs.FileSystemFile{},
	)

}
func TestEverything(t *testing.T) {
	go server.Run()
	time.Sleep(time.Second * 2)
	paths := []string{
		"sandbox/client1/",
		"sandbox/client2/",
	}
	ignores := make(map[string]bool)
	for _, value := range paths {
		err := boxtools.CleanTestFolder(value, ignores, true)
		if err != nil {
			panic("Could not delete folder contents")
		}
		go func(value string) {
			cmd := exec.Command(
				"go",
				"run",
				"client/client.go",
				value)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Run()
		}(value)
	}
	select {}
}


<|start_filename|>cmd/gobox-client/spec_test.go<|end_filename|>
package main

import (
	"testing"

	"github.com/ghthor/gospec"
)

func TestUnitSpecs(t *testing.T) {
	r := gospec.NewRunner()

	r.AddSpec(DescribeReaderWatcher)

	gospec.MainGoTest(r, t)
}


<|start_filename|>cmd/gobox-client/fs_watcher.go<|end_filename|>
package main

// Interface used by the Fs Watcher
type FsEventCollector interface {
	NewFsEvent(stateChange)
}

// Called by Watcher
func (s collector) NewFsEvent(c stateChange) {
	s.collect <- c
}

// TODO move to tests
var _ FsEventCollector = collector{}


<|start_filename|>server/api/api.go<|end_filename|>
package api

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"text/template"

	"github.com/golangbox/gobox/UDPush"
	"github.com/golangbox/gobox/boxtools"
	"github.com/golangbox/gobox/server/model"
	"github.com/golangbox/gobox/server/s3"
	"github.com/golangbox/gobox/structs"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
)

var T *template.Template

func RenderTemplate(w http.ResponseWriter, tmpl string, data interface{}) {
	T.ExecuteTemplate(w, tmpl+".html", data)
}

var Pusher *UDPush.Pusher

func ServeServerRoutes(port string, pusher *UDPush.Pusher) {
	Pusher = pusher
	var err error
	T, err = template.ParseGlob("server/templates/*")
	_ = err
	r := mux.NewRouter()
	r.StrictSlash(true)

	// public
	r.HandleFunc("/", IndexHandler)
	r.HandleFunc("/login/", LoginHandler).Methods("GET")
	r.HandleFunc("/sign-up/", SignUpHandler).Methods("POST")
	r.HandleFunc("/file-data/{email}", FilesHandler).Methods("POST")
	r.HandleFunc("/download/{id}/{filename}", DownloadHandler).Methods("GET")

	// require client authentication
	r.HandleFunc("/file-actions/", sessionValidate(FileActionsHandler)).Methods("POST")
	r.HandleFunc("/upload/", sessionValidate(UploadHandler)).Methods("POST")
	r.HandleFunc("/download/", sessionValidate(FileDownloadHandler)).Methods("POST")
	r.HandleFunc("/clients/", sessionValidate(ClientsFileActionsHandler)).Methods("POST")

	// static files? (css, js, etc...)
	// r.PathPrefix("/").Handler(http.FileServer(http.Dir("./public/")))

	http.Handle("/", r)

	fmt.Println("Serving api on port :" + port)
	http.ListenAndServe(":"+port, nil)
}

type httpError struct {
	err            error
	code           int
	responseWriter http.ResponseWriter
}

func (h *httpError) check() bool {
	if h.err != nil {
		h.responseWriter.WriteHeader(h.code)
		h.responseWriter.Write([]byte(h.err.Error()))
		log.Fatal(h.err)
		return true
	} else {
		return false
	}
}

func sessionValidate(fn func(http.ResponseWriter, *http.Request, structs.Client)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL)
		client, err := verifyAndReturnClient(r)
		if err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error()))
			return
		}
		fn(w, r, client)
	}
}

func verifyAndReturnClient(req *http.Request) (client structs.Client, err error) {
	sessionKey := req.FormValue("SessionKey")
	if sessionKey == "" {
		err = fmt.Errorf("No session key with request")
		return
	}
	query := model.DB.Where("session_key = ?", sessionKey).First(&client)
	if query.Error != nil {
		err = query.Error
		return
	}
	if client.Id == 0 {
		err = fmt.Errorf("No client matching this session key")
		return
	}
	return
}

func FileActionsHandler(w http.ResponseWriter, req *http.Request,
	client structs.Client) {
	httpError := httpError{responseWriter: w}

	var contents []byte
	contents, httpError.err = ioutil.ReadAll(req.Body)
	httpError.code = http.StatusInternalServerError
	if httpError.check() {
		return
	}

	var fileActions []structs.FileAction
	httpError.err = json.Unmarshal(contents, &fileActions)
	httpError.code = http.StatusNotAcceptable
	if httpError.check() {
		return
	}

	for _, value := range fileActions {
		value.ClientId = client.Id
	}

	fileActions, httpError.err = boxtools.WriteFileActionsToDatabase(fileActions, client)
	httpError.code = http.StatusInternalServerError
	if httpError.check() {
		return
	}

	var user structs.User
	query := model.DB.Model(&client).Related(&user)
	httpError.err = query.Error
	if httpError.check() {
		return
	}

	Pusher.Notify(client.SessionKey)

	errs := boxtools.ApplyFileActionsToFileSystemFileTable(fileActions, user)
	if len(errs) != 0 {
		fmt.Println(errs)
	}

	var hashesThatNeedToBeUploaded []string
	hashMap := make(map[string]bool)
	// write to a map to remove any duplicate hashes
	for _, value := range fileActions {
		if value.IsCreate == true {
			hashMap[value.File.Hash] = true
		}
	}
	for key, _ := range hashMap {
		var exists bool
		exists, httpError.err = s3.TestKeyExistence(key)
		httpError.code = http.StatusInternalServerError
		if httpError.check() {
			return
		}
		if exists == false {
			hashesThatNeedToBeUploaded = append(
				hashesThatNeedToBeUploaded,
				key,
			)
		}
	}

	var jsonBytes []byte
	jsonBytes, httpError.err = json.Marshal(hashesThatNeedToBeUploaded)
	httpError.code = http.StatusInternalServerError
	if httpError.check() {
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write(jsonBytes)
}
func UploadHandler(w http.ResponseWriter, req *http.Request,
	client structs.Client) {
	httpError := httpError{responseWriter: w}

	var contents []byte
	contents, httpError.err = ioutil.ReadAll(req.Body)
	httpError.code = http.StatusUnauthorized
	if httpError.check() {
		return
	}

	h := sha256.New()
	_, httpError.err = h.Write(contents)
	httpError.code = http.StatusInternalServerError
	if httpError.check() {
		return
	}
	byteString := h.Sum(nil)
	sha256String := hex.EncodeToString(byteString)

	// we have the hash, so we might as well check if it
	// exists again before we upload
	var exists bool
	exists, httpError.err = s3.TestKeyExistence(sha256String)
	if httpError.check() {
		return
	}
	if exists == false {
		httpError.err = s3.UploadFile(sha256String, contents)
		if httpError.check() {
			return
		}
	}
	w.WriteHeader(http.StatusOK)
}

func FileDownloadHandler(w http.ResponseWriter, req *http.Request,
	client structs.Client) {
	httpError := httpError{responseWriter: w}
	httpError.code = http.StatusInternalServerError

	var user structs.User
	query := model.DB.Model(&client).Related(&user)
	httpError.err = query.Error
	if httpError.check() {
		return
	}

	fileHash := req.FormValue("fileHash")
	if fileHash == "" {
		w.WriteHeader(http.StatusNotAcceptable)
		return
	}

	var file structs.File
	query = model.DB.Where(
		&structs.File{
			UserId: user.Id,
			Hash:   fileHash,
		},
	).First(&file)
	if query.Error != nil {
		if query.Error == gorm.RecordNotFound {
			w.WriteHeader(http.StatusUnauthorized)
		} else {
			w.WriteHeader(http.StatusInternalServerError)
			w.Write([]byte(query.Error.Error()))
		}
		return
	}

	var exists bool
	exists, httpError.err = s3.TestKeyExistence(fileHash)
	if httpError.check() {
		return
	}

	if exists != true {
		w.WriteHeader(http.StatusNoContent)
		return
	}

	var url string
	url, httpError.err = s3.GenerateSignedUrl(fileHash)
	if httpError.check() {
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(url))
}

func ClientsFileActionsHandler(w http.ResponseWriter, req *http.Request,
	client structs.Client) {
	httpError := httpError{responseWriter: w}
	httpError.code = http.StatusInternalServerError

	lastIdString := req.FormValue("lastId")
	if lastIdString == "" {
		httpError.err = fmt.Errorf("Need last fileAction Id.")
		if httpError.check() {
			return
		}
	}

	var user structs.User
	query := model.DB.Model(&client).Related(&user)
	httpError.err = query.Error
	if httpError.check() {
		return
	}

	var clients []structs.Client
	query = model.DB.Model(&user).Not("Id = ?", client.Id).Related(&clients, "clients")
	httpError.err = query.Error
	if httpError.check() {
		return
	}

	var clientIds []int64
	for _, value := range clients {
		clientIds = append(clientIds, value.Id)
	}

	var fileActions []structs.FileAction
	query = model.DB.Where("client_id in (?)", clientIds).
		Where("Id > ?", lastIdString).
		Find(&fileActions)
	httpError.err = query.Error
	if httpError.check() {
		return
	}

	var highestId int64
	for _, value := range fileActions {
		if value.Id > highestId {
			highestId = value.Id
		}
	}

	fileActions = boxtools.RemoveRedundancyFromFileActions(fileActions)

	for key, value := range fileActions {
		var file structs.File
		_ = model.DB.First(&file, value.FileId)
		fileActions[key].File = file
	}

	responseStruct := structs.ClientFileActionsResponse{
		LastId:      highestId,
		FileActions: fileActions,
	}

	var responseJsonBytes []byte
	responseJsonBytes, httpError.err = json.Marshal(responseStruct)
	if httpError.check() {
		return
	}
	w.Write(responseJsonBytes)

	model.DB.Save(&client)

}

func FilesHandler(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	email := vars["email"]

	var user structs.User
	query := model.DB.Where("email = ?", email).First(&user)

	var files []structs.FileSystemFile
	query = model.DB.Where("user_id = ?", user.Id).Find(&files)

	for i, value := range files {
		query = model.DB.First(&files[i].File, value.FileId)
	}

	if query.Error != nil {
		panic(query.Error)
	}
	jsonBytes, _ := json.Marshal(files)
	w.Write(jsonBytes)
}

func DownloadHandler(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	id := vars["id"]
	intId, _ := strconv.Atoi(id)
	int64Id := int64(intId)

	var file structs.File
	query := model.DB.First(&file, int64Id)
	_ = query
	url, err := s3.GenerateSignedUrl(file.Hash)
	resp, err := http.Get(url)
	// keyBytes, err := ioutil.ReadAll(resp.Body)
	w.Header().Add("Content-Type", "application/octet-stream")
	io.Copy(w, resp.Body)
	_ = err
	// w.Write(keyBytes)

	// w.Write()
	// fmt.Println(query.Error)
}

func IndexHandler(w http.ResponseWriter, req *http.Request) {
	RenderTemplate(w, "index", nil)
}

func SignUpHandler(w http.ResponseWriter, req *http.Request) {
	// wants username and pass1 and pass2 posted as a form?
	// returns 200 or  some sort of error to client?

}

func LoginHandler(w http.ResponseWriter, req *http.Request) {
	httpError := httpError{responseWriter: w}
	httpError.code = http.StatusInternalServerError

	var user structs.User
	model.DB.First(&user)
	client, err := boxtools.NewClient(user, "stephen", false)
	httpError.err = err
	if httpError.check() {
		return
	}

	w.Write([]byte(client.SessionKey))
}


<|start_filename|>cmd/gobox-client/udp_watcher.go<|end_filename|>
package main

import (
	"bufio"
	"io"
)

// Interface used by the UDP Listener
type RemoteEventCollector interface {
	NewRemoteEvent(stateChange)
}

// Called by Server Notifications
func (s collector) NewRemoteEvent(c stateChange) {
	s.collect <- c
}

// TODO move to tests
var _ RemoteEventCollector = collector{}

type readerWatcher struct {
	// TODO There should probly be some communication if
	//      watcher encounters an error while it's running

	// Stores the error that caused watcher to exit
	err error
}

func (w *readerWatcher) watchFrom(r io.Reader, sink RemoteEventCollector) {
	go func() {
		scanner := bufio.NewScanner(r)
		scanner.Split(bufio.ScanLines)

		for scanner.Scan() {
			sink.NewRemoteEvent(stateChange(scanner.Text()))
		}

		w.err = scanner.Err()
	}()
}


<|start_filename|>cmd/gobox-client/watcher_test.go<|end_filename|>
package main

import (
	"strings"

	"github.com/ghthor/gospec"
	. "github.com/ghthor/gospec"
)

type mockRemoteEventCollector struct {
	changes chan stateChange
}

func (c *mockRemoteEventCollector) NewRemoteEvent(sc stateChange) {
	c.changes <- sc
}

func DescribeReaderWatcher(c gospec.Context) {
	c.Specify("a io.Reader watcher", func() {
		c.Specify("generates state changes", func() {
			watcher := readerWatcher{}
			collector := mockRemoteEventCollector{make(chan stateChange)}

			watcher.watchFrom(strings.NewReader("update\n"), &collector)
			change := <-collector.changes

			c.Expect(change.(string), Equals, "update")
			c.Expect(watcher.err, IsNil)
		})
	})
}


<|start_filename|>server/model/model.go<|end_filename|>
package model

import (
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
)

var DB gorm.DB

func main() {
	// var err error
	// DB, err = gorm.Open("postgres", "dbname=gobox sslmode=disable")
	// if err != nil {
	// 	fmt.Println(err)
	// }
	// query := DB.AutoMigrate(&User{}, &Client{})
	// if query.Error != nil {
	// 	fmt.Println(query.Error)
	// }
	// fmt.Println(query)

	// meta, err := convertJsonStringToMetaStruct(testJsonString)
	// fmt.Println(meta)
	// file, err := convertMetaStructToFileStruct(meta)
	// fmt.Println(file)
}


<|start_filename|>boxtools/boxtools.go<|end_filename|>
package boxtools

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"path/filepath"
	"time"

	"github.com/golangbox/gobox/server/model"
	"github.com/golangbox/gobox/structs"
	"github.com/jinzhu/gorm"

	"code.google.com/p/go.crypto/bcrypt"
)

var salt []byte

func init() {
	f, err := os.Open("/dev/random")
	if err != nil {
		panic("Unable to open /dev/random")
	}
	salt = make([]byte, 8)
	n, err := f.Read(salt)
	if n != 8 || err != nil {
		panic("Couldn't read from dev/random")
	}
	f.Close()

	rand.Seed(time.Now().Unix())

}

func RandomString(n int) string {
	s := ""
	for i := 0; i < n; i++ {
		s += string('a' + rand.Intn(26))
	}
	return s
}

func GenerateFilePathFromRoot(root string, depth int) string {
	s := root
	for i := 0; i < depth; i++ {

		s += string(filepath.Separator)
		s += RandomString(rand.Intn(10) + 1)
	}
	return s
}

func GenerateRandomBytes(n int) []byte {
	slc := make([]byte, n)
	for i := 0; i < n; i++ {
		slc[i] = 'a' + byte(rand.Intn(26))
	}
	return slc
}

func WriteRandomFileContent(path string) (err error) {
	bytes := GenerateRandomBytes(rand.Intn(1000))
	f, err := os.Create(path)
	if err != nil {
		return
	}
	_, err = f.Write(bytes)
	return
}

func sortPair(a int, b int) (less int, more int) {
	if a < b {
		return a, b
	}
	return b, a
}

func changeRandomPartOfFile(path string) (err error) {
	fi, err := os.Stat(path)
	if err != nil {
		return
	}
	if fi.IsDir() {
		return errors.New("Cannot alter a dir")
	}
	flen := fi.Size()
	f, err := os.Open(path)
	if err != nil {
		return
	}
	bytes := make([]byte, flen)
	n, err := f.Read(bytes)
	if err != nil {
		return
	}
	start, stop := sortPair(rand.Intn(n), rand.Intn(n))
	for i := start; i < stop; i++ {
		bytes[i] = (((bytes[i] - 'a') + 1) % 26) + 'a'
	}
	_, err = f.Write(bytes)
	return
}

func SimulateFilesystemChanges(dir string, inserts int, alters int, deletes int) {
	counter := 0
	inserted := make([]string, 0)
	for i := 0; i < inserts; i++ {
		path := GenerateFilePathFromRoot(dir, 1)
		err := WriteRandomFileContent(path)
		if err == nil {
			counter++
			inserted = append(inserted, path)
		}
	}
	fmt.Println(len(inserted))
	for i := 0; i < alters; i++ {
		changeRandomPartOfFile(inserted[i])
		counter++
	}
	for i := 0; i < deletes; i++ {
		err := os.Remove(inserted[i])
		counter++
		if err != nil {
			fmt.Println("Failed to remove a file in simulateFilesystemChanges")
		}
	}
	fmt.Println(counter)
}

func CleanTestFolder(path string, ignores map[string]bool, rootDir bool) (err error) {
	ignored := 0
	infos, err := ioutil.ReadDir(path)
	if err != nil {
		return
	}
	for _, fi := range infos {
		if fi.Name() == "." || fi.Name() == ".." {
			continue
		}

		fileName := filepath.Join(path, fi.Name())
		fmt.Println(fileName)
		if _, found := ignores[fi.Name()]; found {
			ignored++
			continue
		}
		if fi.IsDir() {
			err = CleanTestFolder(fileName, ignores, false)
			if err != nil {
				return
			}
			err = os.Remove(fileName)
			continue
		}
		err = os.Remove(fileName)
		if err != nil {
			return
		}
	}
	if ignored != 0 && !rootDir {
		err = errors.New("Cannot delete a non-empty folder")
	}
	return
}

func GenerateRandomFile(user_id int) (file structs.File, err error) {
	path := GenerateFilePathFromRoot("/path", 3)
	basename := filepath.Base(path)
	h, err := GenerateRandomSha256()
	if err != nil {
		return file, err
	}
	return structs.File{
		UserId:    int64(user_id),
		Name:      basename,
		Hash:      h,
		Size:      rand.Int63(),
		Modified:  time.Now(),
		Path:      path,
		CreatedAt: time.Now(),
	}, err

}

func GenerateRandomFileAction(client_id int, user_id int, isCreate bool) (fileAction structs.FileAction, err error) {
	file, err := GenerateRandomFile(user_id)
	if err != nil {
		return fileAction, err
	}
	_ = isCreate
	return structs.FileAction{
		ClientId:  int64(client_id),
		IsCreate:  true,
		CreatedAt: time.Now(),
		File:      file,
	}, err
}

func GenerateSliceOfRandomFileActions(user_id int, clients int, actions int) (fileActions []structs.FileAction, err error) {
	fileActions = make([]structs.FileAction, actions)
	for i := 0; i < int(actions); i++ {
		isAction := rand.Intn(2) == 1
		action, err := GenerateRandomFileAction(rand.Intn(clients)+1, user_id, isAction)
		if err != nil {
			return fileActions, err
		}
		fileActions[i] = action
	}
	return fileActions, err
}

func GenerateNoisyAndNonNoisyFileActions(user_id int, clients int, totalNonNoisyActions int, createPairs int) (nonNoisyActions []structs.FileAction,
	noisyActions []structs.FileAction, err error) {
	numNoisyActions := totalNonNoisyActions + createPairs
	nonNoisyActions, err = GenerateSliceOfRandomFileActions(user_id, clients, totalNonNoisyActions)
	if err != nil {
		return
	}
	noisyActions = make([]structs.FileAction, numNoisyActions)
	copy(noisyActions, nonNoisyActions)
	offset := len(nonNoisyActions)
	for i := 0; i < createPairs; i++ {
		new := nonNoisyActions[i]
		new.IsCreate = !new.IsCreate
		noisyActions[i+offset] = new
	}
	return

}

func NewUser(email string, password string) (user structs.User, err error) {
	hash, err := hashPassword(password)
	if err != nil {
		return
	}
	user = structs.User{
		Email:          email,
		HashedPassword: hash,
	}
	query := model.DB.Create(&user)
	if query.Error != nil {
		return user, query.Error
	}
	client, err := NewClient(user, "Server", true)
	_ = client
	if err != nil {
		return
	}
	return
}

func NewClient(user structs.User, name string, isServer bool) (client structs.Client, err error) {
	// calculate key if we need a key?
	newKey, err := GenerateRandomSha256()
	if err != nil {
		return
	}
	client = structs.Client{
		UserId:     user.Id,
		SessionKey: newKey,
		IsServer:   isServer,
		Name:       name,
	}
	query := model.DB.Create(&client)
	if query.Error != nil {
		return client, query.Error
	}
	return
}

func GenerateRandomSha256() (s string, err error) {
	h := sha256.New()
	h.Write(salt)
	io.WriteString(h, time.Now().String())
	bytes := h.Sum(nil)
	s = hex.EncodeToString(bytes)
	return s, err
}

func ValidateUserPassword(email, password string) (user structs.User, err error) {
	model.DB.Where("email = ?", email).First(&user)
	bytePassword := []byte(password)
	byteHash := []byte(user.HashedPassword)
	err = bcrypt.CompareHashAndPassword(byteHash, bytePassword)
	return user, err
}

func clear(b []byte) {
	for i := 0; i < len(b); i++ {
		b[i] = 0
	}
}

func hashPassword(password string) (hash string, err error) {
	bytePassword := []byte(password)
	defer clear(bytePassword)
	byteHash, err := bcrypt.GenerateFromPassword(bytePassword, bcrypt.DefaultCost)
	return string(byteHash), err
}

func ConvertJsonStringToFileActionsStruct(jsonFileAction string, client structs.Client) (fileAction structs.FileAction, err error) {
	// {"Id":0,"ClientId":0,"IsCreate":true,"CreatedAt":"0001-01-01T00:00:00Z","File":{"Id":0,"UserId":0,"Name":"client.go","Hash":"f953d35b6d8067bf2bd9c46017c554b73aa28a549fac06ba747d673b2da5bfe0","Size":6622,"Modified":"2015-02-09T14:39:22-05:00","Path":"./client.go","CreatedAt":"0001-01-01T00:00:00Z"}}
	data := []byte(jsonFileAction)
	var unmarshalledFileAction structs.FileAction
	err = json.Unmarshal(data, &unmarshalledFileAction)
	if err != nil {
		err = fmt.Errorf("Error unmarshaling json to structs.FileAction: %s", err)
		return
	}
	return
}

func ConvertFileActionStructToJsonString(fileActionStruct structs.FileAction) (fileActionJson string, err error) {
	jsonBytes, err := json.Marshal(fileActionStruct)
	if err != nil {
		return
	}
	fileActionJson = string(jsonBytes)
	return
}

func RemoveRedundancyFromFileActions(fileActions []structs.FileAction) (simplifiedFileActions []structs.FileAction) {
	// removing redundancy
	// if a file is created, and then deleted remove

	var actionMap = make(map[string]int)

	var isCreateMap = make(map[bool]string)
	isCreateMap[true] = "1"
	isCreateMap[false] = "0"

	for _, action := range fileActions {
		// create a map of the fileaction
		// key values are task+path+hash
		// hash map value is the number of occurences
		actionKey := isCreateMap[action.IsCreate] + action.File.Path + action.File.Hash
		value, exists := actionMap[actionKey]
		if exists {
			actionMap[actionKey] = value + 1
		} else {
			actionMap[actionKey] = 1
		}
	}

	for _, action := range fileActions {
		// for each action value, check if a pair exists in the map
		// if it does remove one iteration of that value
		// if it doesn't write that to the simplified array

		// This ends up removing pairs twice, once for each matching value
		var opposingTask string
		if action.IsCreate == true {
			opposingTask = isCreateMap[false]
		} else {
			opposingTask = isCreateMap[true]
		}
		opposingMapKey := opposingTask + action.File.Path + action.File.Hash
		value, exists := actionMap[opposingMapKey]
		if exists == true && value > 0 {
			actionMap[opposingMapKey] = value - 1
		} else {
			simplifiedFileActions = append(simplifiedFileActions, action)
		}
	}
	return
}

func ComputeFilesFromFileActions(fileActions []structs.FileAction) (files []structs.File) {
	simplifiedFileActions := RemoveRedundancyFromFileActions(fileActions)
	for _, value := range simplifiedFileActions {
		files = append(files, value.File)
	}
	return
}

func WriteFileActionsToDatabase(fileActions []structs.FileAction,
	client structs.Client) (outPutFileActions []structs.FileAction,
	err error) {
	var user structs.User
	query := model.DB.Model(&client).Related(&user)
	if query.Error != nil {
		err = query.Error
		return outPutFileActions, err
	}
	for _, fileAction := range fileActions {
		fileAction.ClientId = client.Id
		fileAction.File.UserId = user.Id
		file, err := FindFile(fileAction.File.Hash, fileAction.File.Path, user)
		if err != nil {
			return outPutFileActions, err
		}
		if file.Id != 0 {
			// if the file exists, assign an id
			// otherwise GORM automatically creates
			// the file, so make sure to clear the File
			// struct
			fileAction.FileId = file.Id
			fileAction.File = structs.File{}
		}
		query = model.DB.Create(&fileAction)
		outPutFileActions = append(
			outPutFileActions,
			fileAction,
		)
		if query.Error != nil {
			return outPutFileActions, query.Error
		}
	}
	return outPutFileActions, nil
}

func FindFile(hash string, path string, user structs.User) (file structs.File, err error) {
	query := model.DB.Where(&structs.File{
		UserId: user.Id,
		Path:   path,
		Hash:   hash,
	}).First(&file)
	if query.Error != nil {
		if query.Error != gorm.RecordNotFound {
			return file, query.Error
		} else {
			return
		}
	}
	return //this should never happen
}

func ApplyFileActionsToFileSystemFileTable(fileActions []structs.FileAction, user structs.User) (errs []error) {
	for _, fileAction := range fileActions {
		// get file for fileaction
		var file structs.File
		model.DB.First(&file, fileAction.FileId)
		if fileAction.IsCreate == true {
			err := deleteFileSystemFileAtPath(file.Path, user)
			if err != nil {
				log.Fatal(err)
				errs = append(errs, err)
			}
			newFileSystemFile := structs.FileSystemFile{
				UserId: user.Id,
				FileId: fileAction.FileId,
				Path:   file.Path,
			}
			query := model.DB.Create(&newFileSystemFile)
			if query.Error != nil {
				fmt.Println(query.Error)
			}
		} else {
			err := deleteFileSystemFileAtPath(file.Path, user)
			if err != nil {
				log.Fatal(err)
				errs = append(errs, err)
			}
		}
	}
	return
}

func deleteFileSystemFileAtPath(path string, user structs.User) (err error) {
	query := model.DB.
		Where("path = ?", path).
		Where("user_id = ?", user.Id).
		Delete(structs.FileSystemFile{})
	if query.Error != nil {
		return query.Error
	}
	return
}


<|start_filename|>client/client.go<|end_filename|>
package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"time"

	"github.com/golangbox/gobox/client/api"
	"github.com/golangbox/gobox/client/watcher"
	"github.com/golangbox/gobox/structs"
)

// TODO
// get client/server sessions set up
// sync a damn file
// work on download branch

// PROBLEMS: No way to tell if a remove event was dir or a file because it can't be os.Stat'ed
//           Can't remove that dir from a watch because Watcher.watches isn't exposed

var client api.Api

const (
	dataDirectoryBasename = ".Gobox"
	serverEndpoint        = "http://requestb.in/1mv9fa41"
)

func writeError(err error, change structs.StateChange, function string) {
	change.Error <- structs.ErrorMessage{
		Error:    err,
		File:     change.File,
		Function: function,
	}
	close(change.Done)
}

// it may be easier to just have this function make a FileAction, but ignore for now
func writeDone(change structs.StateChange, fa structs.FileAction) {
	change.Done <- fa
	close(change.Error)
}

func findChangedFilesOnInit(
	fileSystemStateFile string,
	goboxDirectoryPath string,
	watcherInitScanDone chan<- struct{},
	serverActionsInitScanDone chan<- struct{}) (
	out chan structs.StateChange, err error) {

	out = make(chan structs.StateChange)
	fileSystemState, err := fetchFileSystemState(fileSystemStateFile)
	if err != nil {
		return
	}
	go func() {
		err = filepath.Walk(goboxDirectoryPath, func(fp string, fi os.FileInfo, errIn error) (errOut error) {
			if errIn != nil {
				return errIn
			}
			if filepath.HasPrefix(fp, ".") || filepath.HasPrefix(fp, "..") {
				return
			}
			matched, errOut := regexp.MatchString(".Gobox(/.*)*", fp)
			if errOut != nil {
				return
			}
			if matched {
				return
			}
			f, found := fileSystemState[fp]
			if !found {
				change, err := watcher.CreateLocalStateChange(fp,
					watcher.CREATE)
				if err != nil {
					return
				}
				out <- change
				return
			}
			h, err := getSha256FromFilename(fp)
			if err != nil {
				return
			}
			if f.Hash != h {
				change, err := watcher.CreateLocalStateChange(fp, watcher.MODIFY)
				if err != nil {
					return
				}
				out <- change
				delete(fileSystemState, fp)
				return
			}
			return
		})
		if err != nil {
			return
		}
		for fp := range fileSystemState {
			// may need to change structs so that PreviousHash is in the File struct
			change, err := watcher.CreateLocalStateChange(fp, watcher.DELETE)
			if err != nil {
				return
			}
			out <- change
		}
		watcherInitScanDone <- struct{}{}
		serverActionsInitScanDone <- struct{}{}
		return
	}()
	return
}

func startWatcher(dir string, initScanDone <-chan struct{}) (
	out chan structs.StateChange, err error) {
	rw, err := watcher.NewRecursiveWatcher(dir)
	if err != nil {
		return out, err

	}
	rw.Run(initScanDone, false)
	return rw.Files, err
}

func createServerStateChange(fa structs.FileAction) (change structs.StateChange) {
	change.File = fa.File
	change.IsCreate = fa.IsCreate
	change.IsLocal = false
	change.PreviousHash = fa.PreviousHash
	return
}

// ah shoot! is this the only function that needs to know about the last fileaction ID state?
// the following function is hypothetical and non-functional as of now

func serverActions(UDPing <-chan bool, fileActionIdPath string,
	initScanDone <-chan struct{}) (out chan structs.StateChange,
	errorChan chan interface{}, err error) {
	out = make(chan structs.StateChange)
	fileActionId, err := fetchFileActionID(fileActionIdPath)
	if err != nil {
		panic("Couldn't properly read fil eActionId from given path")
	}

	go func() {
		<-initScanDone
		for {
			<-UDPing
			fmt.Println("-----------------PING RECIEVED--------------------")
			// these return values are obviously wrong right now
			fmt.Println("File aCTION ID", fileActionId)
			clientFileActionResponse, err := client.DownloadClientFileActions(
				fileActionId)
			// need to rethink errors, assumption of a statechange is invalid
			if err != nil {
				fmt.Println(err)
				writeError(err, structs.StateChange{}, "serverActions")
			}
			fileActionId = clientFileActionResponse.LastId
			for _, fileAction := range clientFileActionResponse.FileActions {
				change := createServerStateChange(fileAction)
				fmt.Println("In server actions hash: ", change.File.Hash)
				out <- change
				fmt.Println("loop")
			}
			err = writeFileActionIDToLocalFile(fileActionId, fileActionIdPath)
			if err != nil {
				fmt.Println("Couldn't write fileActionId to path : ",
					fileActionIdPath)
			}
		}
	}()
	return
}

func initUDPush(sessionKey string) (notification chan bool, err error) {
	notification = make(chan bool)
	go func() {
		conn, err := net.Dial("tcp", api.UDPEndpoint)
		// defer conn.Close()
		if err != nil {
			fmt.Println(fmt.Errorf("%s", err))
			return
		}
		sessionKeyBytes := []byte(sessionKey)
		_, err = conn.Write(sessionKeyBytes)
		if err != nil {
			return
		}
		response := make([]byte, 21)
		for {
			read, err := conn.Read(response)
			if err != nil {
				fmt.Println(err)
			}
			fmt.Println("Message read from socket: ", read, string(response))
			notification <- true
			fmt.Println("YO")
		}
	}()
	return

}

func fanActionsIn(initActions <-chan structs.StateChange,
	watcherActions <-chan structs.StateChange,
	serverActions <-chan structs.StateChange) chan structs.StateChange {
	out := make(chan structs.StateChange)
	go func() {
		for {
			fmt.Println("FanActions")
			select {
			case stateChange := <-initActions:
				fmt.Println("fanActions inside the stateChange := <-initActions")
				out <- stateChange
			case stateChange := <-watcherActions:
				fmt.Println("fanActions inside the stateChange := <-WatcherActions")
				out <- stateChange
			case stateChange := <-serverActions:
				fmt.Println("fanActions inside the stateChange := <-serverActions")
				fmt.Println("Fanin: ", stateChange.File.Hash)
				out <- stateChange
			}
		}
	}()
	return out
}

func createGoboxLocalDirectory(path string) {
	if _, err := os.Stat(path); err != nil {
		fmt.Println(err.Error())
		if os.IsNotExist(err) {
			fmt.Println("Making directory")
			err := os.Mkdir(path, 0777)
			if err != nil {
				log.Fatal(err)
			}
		}
	}
}

func writeFileSystemStateToLocalFile(fileSystemState map[string]structs.File, path string) error {
	jsonBytes, err := json.Marshal(fileSystemState)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(path, jsonBytes, 0644)
	return err
}

func fetchFileSystemState(path string) (fileSystemState map[string]structs.File, err error) {
	if _, err := os.Stat(path); err != nil {
		emptyState := make(map[string]structs.File)
		writeFileSystemStateToLocalFile(emptyState, path)
	}

	data, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	if data != nil {
		err = json.Unmarshal(data, &fileSystemState)
	}
	return

}

func writeFileActionIDToLocalFile(id int64, path string) (err error) {
	jsonBytes, err := json.Marshal(id)
	if err != nil {
		return
	}
	err = ioutil.WriteFile(path, jsonBytes, 0644)
	return
}

func fetchFileActionID(path string) (id int64, err error) {
	if _, err := os.Stat(path); err != nil {
		defaultId := int64(1)
		err = writeFileActionIDToLocalFile(defaultId, path)
		if err != nil {
			return id, err
		}
	}
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}
	if data != nil {
		err = json.Unmarshal(data, &id)
	}
	return
}

func getSha256FromFilename(filename string) (sha256String string,
	err error) {
	file, err := ioutil.ReadFile(filename)
	if err != nil {
		return "", fmt.Errorf("Error reading file for sha256: %s", err)
	}
	h := sha256.New()
	_, err = h.Write(file)
	if err != nil {
		return "", fmt.Errorf("Error writing file to hash for sha256: %s", err)
	}
	byteString := h.Sum(nil)

	sha256String = hex.EncodeToString(byteString)

	return sha256String, nil
}

func makeFileAction(change structs.StateChange) (fa structs.FileAction) {
	fa.IsCreate = change.IsCreate
	fa.CreatedAt = change.File.CreatedAt
	fa.FileId = change.File.Id
	fa.File = change.File
	fa.PreviousHash = change.PreviousHash
	return
}

func fileActionSender(change structs.StateChange) {
	select {
	case <-change.Quit:
		gracefulQuit(change)
		return
	default:
		fileActions := make([]structs.FileAction, 1)
		fileActions[0] = makeFileAction(change)

		needed, err := client.SendFileActionsToServer(fileActions)
		if err != nil {
			writeError(err, change, "fileActionSender")
			return
		}
		// if len needed 0, need to do cleanup by signaling done
		if len(needed) == 0 {
			writeDone(change, fileActions[0])
			return
		}
		// need to fix this to just get responses for one file
		go uploader(change.File.Path, change, fileActions[0])

	}
	return
}

func uploader(path string, change structs.StateChange, fa structs.FileAction) {
	select {
	case <-change.Quit:
		gracefulQuit(change)
		return
	default:
		buf, err := ioutil.ReadFile(path)
		if err != nil {
			writeError(err, change, "uploader")
			return
		}
		err = client.UploadFileToServer(buf)
		if err != nil {
			writeError(err, change, "uploader")
			return
		}
		change.Done <- fa
		close(change.Error)
	}
	return
}

func gracefulQuit(change structs.StateChange) {
	close(change.Done)
	close(change.Error)
}

func hasher(change structs.StateChange) {
	select {
	case <-change.Quit:
		gracefulQuit(change)
		return
	default:
		h, err := getSha256FromFilename(change.File.Path)
		if err != nil {
			writeError(err, change, "hasher")
			return
		}
		change.File.Hash = h
		go fileActionSender(change)
	}
	return
}

// potential for deadly embrace when stephen tries to send a new channel while this func
// blocks on sending on out

// fix was to add a go func to write to out. I don't love this solution becuase it makes order
// of stuff coming out of the out channel non-deterministic, so any other ideas are invited.
func arbitraryFanIn(newChannels <-chan chan interface{}, out chan<- interface{}, removeOnEvent bool) {
	go func() {
		var ch <-chan interface{}
		chans := make([]reflect.SelectCase, 0)
		timeout := time.Tick(10 * time.Millisecond)
		chans = append(chans, reflect.SelectCase{
			Dir:  reflect.SelectRecv,
			Chan: reflect.ValueOf(timeout),
		})
		for {
			select {
			case ch = <-newChannels:
				chans = append(chans, reflect.SelectCase{
					Dir:  reflect.SelectRecv,
					Chan: reflect.ValueOf(ch),
				})
			default:
				chosen, value, ok := reflect.Select(chans)
				if chosen == 0 {
					continue
				}
				if removeOnEvent || !ok {
					lastIndex := len(chans) - 1
					chans[chosen] = chans[lastIndex]
					chans = chans[:lastIndex]
				}
				if ok {
					go func() { out <- value.Interface() }()
				}
			}
		}
	}()
}

func serverDeleter(change structs.StateChange) {
	_, err := os.Stat(change.File.Path)
	if err != nil {
		if os.IsNotExist(err) {
			writeDone(change, makeFileAction(change))
			return
		}
		writeError(err, change, "deleter")
		return
	}
	err = os.Remove(change.File.Path)
	if err != nil {
		writeError(err, change, "deleter")
		return
	}
	writeDone(change, makeFileAction(change))
}

func downloader(change structs.StateChange) {
	fmt.Println("Change: ", change.File.Hash)
	select {
	case <-change.Quit:
		gracefulQuit(change)
		return
	default:
		// this could take a long time
		s3_url, err := client.DownloadFileFromServer(change.File.Hash)
		if err != nil {
			writeError(err, change, "downloader")
		}
		resp, err := http.Get(s3_url)
		if err != nil {
			writeError(err, change, "downloader")
		}
		contents, err := ioutil.ReadAll(resp.Body)
		fmt.Println(contents)
		if err != nil {
			writeError(err, change, "downloader")
		}
		//tmpFilename := filepath.Join(".Gobox/tmp/", change.File.Hash)
		err = ioutil.WriteFile(change.File.Path, contents, 0644)
		if err != nil {
			writeError(err, change, "downloader")
		}
		//select {
		//case <-change.Quit:
		//gracefulQuit(change)
		//return
		//default:
		//err = os.Rename(tmpFilename, change.File.Path)
		//if err != nil {
		//writeError(err, change, "downloader")
		//}
		//}
	}
	fmt.Println("look")
	return
}

func localDeleter(change structs.StateChange) {
	go fileActionSender(change)
}

func stephen(goboxFileSystemStateFile string, stateChanges <-chan structs.StateChange,
	inputErrChans []chan interface{}) {
	// spin up a goroutine that will fan in error messages using reflect.select
	// hand it an error channel, and add this to the main select statement
	// do the same thing for done, so I can write a generic fan-n-in function
	fileSystemState, err := fetchFileSystemState(goboxFileSystemStateFile)
	if err != nil {
		panic("Could not properly retrieve fileSystemState")
	}

	quitChannels := make(map[string]structs.CurrentAction)

	newErrors := make((chan (chan interface{})))
	errors := make(chan interface{})
	go arbitraryFanIn(newErrors, errors, true)

	newDones := make((chan (chan interface{})))
	dones := make(chan interface{})
	go arbitraryFanIn(newDones, dones, true)

	for _, ch := range inputErrChans {
		newErrors <- ch
	}

	writeFileSystemStateCounter := 0
	for {
		if writeFileSystemStateCounter > 5 {
			writeFileSystemStateToLocalFile(
				fileSystemState,
				goboxFileSystemStateFile,
			)
			if err != nil {
				fmt.Println(
					"Error while writing fileSystemState to: ",
					goboxFileSystemStateFile)
			}
			writeFileSystemStateCounter = 0

		}
		// maybe it would be better to combine errors and dones into the same multiplexor?
		select {
		case e := <-errors:
			msg := e.(structs.ErrorMessage)
			fmt.Println("Experienced an error with ", msg.File.Path)
			fmt.Println("In function: ", msg.Function)
			fmt.Println("Error: ", msg.Error)
			fmt.Println("File: ", msg.File)
			delete(quitChannels, msg.File.Path)
		case d := <-dones:
			fa := d.(structs.FileAction)
			delete(quitChannels, fa.File.Path)
			fileSystemState[fa.File.Path] = fa.File
		case change := <-stateChanges:
			if currentAction, found := quitChannels[change.File.Path]; found {
				// tell goroutine branch to quit
				if currentAction.IsLocal == false {
					continue
				}
				currentAction.Quit <- true
				close(currentAction.Quit)
			}
			f, found := fileSystemState[change.File.Path]
			if change.IsLocal {
				if found {
					change.PreviousHash = f.Hash
				} else {
					change.PreviousHash = ""
				}
			}

			fmt.Println(change)
			quitChan := make(chan bool, 1)
			doneChan := make(chan interface{}, 1)
			newDones <- doneChan
			errChan := make(chan interface{}, 1)
			newErrors <- errChan
			quitChannels[change.File.Path] = structs.CurrentAction{
				Quit:     quitChan,
				IsCreate: change.IsCreate,
				IsLocal:  change.IsLocal,
			}
			change.Quit = quitChan
			change.Done = doneChan
			change.Error = errChan
			if change.IsCreate {
				if change.IsLocal {
					go hasher(change)
				} else {
					fmt.Println("[!]Attempting download...")
					go downloader(change)
				}
			} else {
				if found {
					if change.IsLocal {
						go localDeleter(change)
					} else {
						if f.Hash == change.PreviousHash {
							go serverDeleter(change)
							delete(fileSystemState, change.File.Path)
						}

					}
				}

			}
		}
		writeFileSystemStateCounter++
	}
}
func run(path string) {
	errChans := make([]chan interface{}, 0)
	watcherInitScanDone := make(chan struct{})
	serverActionsInitScanDone := make(chan struct{})
	client = api.New("")
	err := os.Chdir(path)
	if err != nil {
		fmt.Println("unable to change dir, quitting")
		return
	}
	goboxDirectory := "."
	goboxDataDirectory := filepath.Join(goboxDirectory, dataDirectoryBasename)
	goboxFileSystemStateFile := filepath.Join(goboxDataDirectory, "fileSystemState")
	goboxFileActionIdFile := filepath.Join(goboxDataDirectory, "fileActionId")

	createGoboxLocalDirectory(goboxDataDirectory)
	initActions, err := findChangedFilesOnInit(
		goboxFileSystemStateFile,
		goboxDirectory,
		watcherInitScanDone,
		serverActionsInitScanDone,
	)
	if err != nil {
		panic("Could not start init scan")
	}
	watcherActions, err := startWatcher(goboxDirectory, watcherInitScanDone)
	if err != nil {
		panic("Could not start watcher")
	}
	UDPNotification, err := initUDPush(client.SessionKey)
	if err != nil {
		panic("Could not start UDP socket")
	}
	// fix this to get correct fileActionID
	remoteActions, errChan, err := serverActions(UDPNotification,
		goboxFileActionIdFile, serverActionsInitScanDone)
	errChans = append(errChans, errChan)
	if err != nil {
		panic("Could not properly start remote actions")
	}

	actions := fanActionsIn(initActions, watcherActions, remoteActions)
	stephen(goboxFileSystemStateFile, actions, errChans)
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("usage: ./gobox_client PATH_TO_GOBOX_DIRECTORY")
		return
	}
	fi, err := os.Stat(os.Args[1])
	if err != nil {
		fmt.Println("Error reading gobox directory")
		return
	}
	if !fi.IsDir() {
		fmt.Println("Provided path is not a directory")
		return
	}

	fmt.Println("Running : ", os.Args[1])
	run(os.Args[1])
}


<|start_filename|>boxtools/boxtools_test.go<|end_filename|>
package boxtools

import (
	"fmt"
	"testing"

	"github.com/golangbox/gobox/server/model"
	"github.com/golangbox/gobox/structs"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
)

const (
	email    = "<EMAIL>"
	password = "password"
)

var user structs.User
var client structs.Client

func init() {
	var err error

	model.DB, err = gorm.Open("postgres", "dbname=goboxtest sslmode=disable")

	model.DB.DropTableIfExists(&structs.User{})
	model.DB.DropTableIfExists(&structs.Client{})
	model.DB.DropTableIfExists(&structs.FileAction{})
	model.DB.DropTableIfExists(&structs.File{})
	model.DB.DropTableIfExists(&structs.FileSystemFile{})
	model.DB.AutoMigrate(&structs.User{}, &structs.Client{}, &structs.FileAction{}, &structs.File{}, &structs.FileSystemFile{})

	if err != nil {
		fmt.Println(err)
	}
}

func TestUserCreation(t *testing.T) {
	var err error
	user, err = NewUser(email, password)
	if err != nil {
		t.Error(err)
	}
	if user.Email != email {
		t.Fail()
	}
	if user.HashedPassword == "" {
		t.Fail()
	}
}

func TestClientCreation(t *testing.T) {
	var user structs.User
	model.DB.Where("email = ?", email).Find(&user)

	client, err := NewClient(user, "test", false)

	if err != nil {
		t.Error(err)
	}
	user = structs.User{} //nil user

	//testing relation
	model.DB.Model(&client).Related(&user)
	if user.Email != email {
		t.Fail()
	}
}

func TestPasswordValidation(t *testing.T) {
	user, err := ValidateUserPassword(email, password)
	if err != nil {
		t.Error(err)
	}
	if user.Email != email {
		t.Fail()
	}
	// clean up created user
	model.DB.Where("email = ?", email).Delete(structs.User{})
}

func TestJsonMetaConversion(t *testing.T) {
}

func TestRemoveRedundancy(t *testing.T) {
	_, noisy, err := GenerateNoisyAndNonNoisyFileActions(1, 4, 10, 10)
	if err != nil {
		t.Log("Could not generate file actions successfully")
		t.FailNow()
	}
	result := RemoveRedundancyFromFileActions(noisy)
	if 0 != len(result) {
		t.Log("Result should be empty")
		t.FailNow()
	}
	_, noisy, err = GenerateNoisyAndNonNoisyFileActions(1, 4, 10, 5)
	result = RemoveRedundancyFromFileActions(noisy)
	if 5 != len(result) {
		t.Log("Result of cleaning should be length 5")
		t.FailNow()
	}
}


<|start_filename|>client/api/api.go<|end_filename|>
package api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/golangbox/gobox/structs"
)

const (
	ApiEndpoint = "http://127.0.0.1:8000/"
	UDPEndpoint = "127.0.0.1:4242"
)

type Api struct {
	SessionKey string
}

func New(SessionKey string) (c Api) {
	resp, err := http.Get(ApiEndpoint + "login/")
	if err != nil {
		log.Fatal(err)
	}
	keyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	c.SessionKey = string(keyBytes)
	return
}

func (c *Api) apiRequest(endpoint string, body []byte,
	fileType string) (*http.Response, error) {
	return http.Post(
		ApiEndpoint+endpoint+"/?SessionKey="+c.SessionKey,
		"application/json",
		bytes.NewBuffer(body),
	)
}

func (c *Api) SendFileActionsToServer(
	fileActions []structs.FileAction) (
	filesToUpload []string, err error) {

	jsonBytes, err := json.Marshal(fileActions)
	if err != nil {
		fmt.Println("[+]")
		return
	}

	resp, err := c.apiRequest(
		"file-actions",
		jsonBytes,
		"application/json",
	)

	//if err != nil {
	//fmt.Println("[!] API")
	//return
	//}

	contents, _ := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	if resp.StatusCode != http.StatusOK {
		return
	}
	err = json.Unmarshal(contents, &filesToUpload)
	if err != nil {
		return
	}
	return
}

func (c *Api) UploadFileToServer(fileBody []byte) (err error) {
	resp, err := c.apiRequest(
		"upload",
		fileBody,
		"",
	)
	if err != nil {
		return
	}
	if resp.StatusCode != http.StatusOK {
		contents, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		err = fmt.Errorf(string(contents))
		return err
	}
	return
}

func (c *Api) DownloadFileFromServer(
	hash string) (s3_url string, err error) {
	for {
		resp, err := http.PostForm(
			ApiEndpoint+"download/",
			url.Values{
				"SessionKey": {c.SessionKey},
				"fileHash":   {hash},
			},
		)
		if err != nil {
			return "", err
		}
		fmt.Println("HASH: ", hash)
		contents, err := ioutil.ReadAll(resp.Body)
		fmt.Println(string(contents))
		if resp.StatusCode == http.StatusInternalServerError {
			err = fmt.Errorf(string(contents))
			return "", err
		} else if resp.StatusCode == http.StatusOK {
			s3_url = string(contents)
			return s3_url, err
		} else {
			time.Sleep(time.Second * 10)
		}
	}
	return
}

func (c *Api) DownloadClientFileActions(lastId int64) (
	clientFileActionsResponse structs.ClientFileActionsResponse, err error) {
	var lastIdString string
	lastIdString = strconv.FormatInt(lastId, 32)
	resp, err := http.PostForm(
		ApiEndpoint+"clients/",
		url.Values{
			"SessionKey": {c.SessionKey},
			"lastId":     {lastIdString},
		},
	)
	if err != nil {
		fmt.Println("[!]")
		return
	}
	contents, err := ioutil.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf(string(contents))
		fmt.Println(resp.StatusCode)
		return
	}

	err = json.Unmarshal(contents, &clientFileActionsResponse)
	if err != nil {
		fmt.Println("[+]")
		return
	}
	return
}


<|start_filename|>cmd/gobox-client/muxer.go<|end_filename|>
package main

import "log"

type stateChange interface{}

type collectHaltRequest struct {
	hasHalted <-chan struct{}
}

type collector struct {
	// Changes sent to this channel are collected
	collect chan<- stateChange

	// TODO needs to stop
}

// Starts a go routine that will collect state changes
// and send them out the channel returned.
func (s *collector) startCollecting() (changes <-chan stateChange) {
	collectCh := make(chan stateChange)
	s.collect = collectCh
	var newChange <-chan stateChange = collectCh

	changesOutCh := make(chan stateChange)
	changes = changesOutCh
	var fifo chan<- stateChange

	go func() {
		// buffer to collect changes in
		var changes []stateChange

		// first change to be sent out
		var change stateChange

		for {
			if change == nil && len(changes) > 0 {
				change = changes[0]
				changes = changes[1:]
			}

			if change == nil {
				select {
				case c := <-newChange:
					changes = append(changes, c)
				}

			} else {
				select {
				case c := <-newChange:
					changes = append(changes, c)

				case fifo <- change:
					change = nil
				}
			}
		}
	}()

	return changes
}

type changeId int

type executionError struct {
	change executableChange
	err    error
}

type executableChange interface {
	Id() changeId
	Exec(errCh chan<- executionError, doneCh chan<- executableChange)
	Halt()
}

type changeHaltRequest struct {
	hasHalted <-chan executableChange
}

type upload struct {
	id changeId
	stateChange

	requestHalt <-chan changeHaltRequest
}

func (c upload) Id() changeId { return c.id }
func (c upload) Exec(errCh chan<- executionError, doneCh chan<- executableChange) {
	go func() {
		// TODO Do upload
		doneCh <- c
	}()
}
func (upload) Halt() {
}

type download struct {
	id changeId
	stateChange

	requestHalt <-chan changeHaltRequest
}

func (c download) Id() changeId { return c.id }
func (c download) Exec(errCh chan<- executionError, doneCh chan<- executableChange) {
	go func() {
		// TODO Do download
		doneCh <- c
	}()
}
func (download) Halt() {
}

func newExecutableChange(id changeId, sc stateChange) executableChange {
	// TODO Process change, sc
	//      - Is it an upload?
	//      - Is it a download?

	// TODO return the correct type of exec change
	return upload{id, sc, nil}
}

type executorHaltRequest struct {
	hasHalted chan<- struct{}
}

type executor struct {
	// Used by the Halt() method to stop the executor
	requestHalt chan<- executorHaltRequest
}

func (e *executor) executeFrom(changesIn <-chan stateChange) {
	haltCh := make(chan executorHaltRequest)
	e.requestHalt = haltCh
	var haltRequested <-chan executorHaltRequest = haltCh

	go func() {
		errCh := make(chan executionError)
		var executionErr <-chan executionError = errCh

		doneCh := make(chan executableChange)
		var executionComplete <-chan executableChange = doneCh

		nextId := func() func() changeId {
			var nextId changeId = 0
			return func() changeId {
				defer func() { nextId++ }()
				id := nextId
				return id
			}
		}()

		runningChanges := make(map[changeId]executableChange)

		for {
			select {
			case c := <-changesIn:
				ec := newExecutableChange(nextId(), c)
				// TODO Should I cancel any running changes because of ec?

				// Non blocking
				ec.Exec(errCh, doneCh)

				runningChanges[ec.Id()] = ec

			case e := <-executionErr:
				log.Println(e.err)

			case c := <-executionComplete:
				delete(runningChanges, c.Id())

			case r := <-haltRequested:
				// TODO Cleanup
				r.hasHalted <- struct{}{}
			}
		}
	}()
}

func (e executor) halt() {
	hasHalted := make(chan struct{})
	e.requestHalt <- executorHaltRequest{hasHalted}
	<-hasHalted
}
