<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

func secretHandler(next http.Handler) http.Handler {
	secret := os.Getenv("SECRET_KEY")
	signature_ip := os.Getenv("SIGNATURE_IP")

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// step 1: query string is required
		if r.URL.RawQuery == "" {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		// step 2: query string show be valid
		values, err := url.ParseQuery(r.URL.RawQuery)
		if err != nil {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		// step 3: expires and signature are required
		expires := values.Get("e")
		signature := values.Get("s")
		if expires == "" || signature == "" {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		// step 4: expires is int
		expires_at, err := strconv.ParseInt(expires, 10, 64)
		if err != nil {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		// step 5: not expired
		now := int64(time.Now().Unix())
		if now > expires_at {
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("Expired"))
			return
		}

		// secret + url + expires + ip
		var base string
		if signature_ip == "" {
			base = secret + r.URL.Path + expires
		} else {
			base = secret + r.URL.Path + expires + r.RemoteAddr
		}
		sig := fmt.Sprintf("%x", md5.Sum([]byte(base)))

		if sig[8:16] == signature {
			next.ServeHTTP(w, r)
		} else {
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("Invalid signature"))
		}
	})
}

func main() {
	var root string
	if len(os.Args) > 1 {
		root = os.Args[1]
	} else {
		root = "."
	}

	log.Println("Serve files at " + root)

	handler := http.FileServer(http.Dir(root))
	http.Handle("/", secretHandler(handler))

	port := os.Getenv("PORT")
	if port == "" {
		port = "8700"
	}

	log.Println("Listening on port " + port)
	log.Fatal(http.ListenAndServe("localhost:"+port, nil))
}
<commit_msg>Use encoding/hex to hexdigest md5 signature<commit_after>package main

import (
	"crypto/md5"
	"encoding/hex"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

func secretHandler(next http.Handler) http.Handler {
	secret := os.Getenv("SECRET_KEY")
	signature_ip := os.Getenv("SIGNATURE_IP")

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// step 1: query string is required
		if r.URL.RawQuery == "" {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		// step 2: query string show be valid
		values, err := url.ParseQuery(r.URL.RawQuery)
		if err != nil {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		// step 3: expires and signature are required
		expires := values.Get("e")
		signature := values.Get("s")
		if expires == "" || signature == "" {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		// step 4: expires is int
		expires_at, err := strconv.ParseInt(expires, 10, 64)
		if err != nil {
			w.WriteHeader(http.StatusForbidden)
			return
		}

		// step 5: not expired
		now := int64(time.Now().Unix())
		if now > expires_at {
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("Expired"))
			return
		}

		// secret + url + expires + ip
		var base string
		if signature_ip == "" {
			base = secret + r.URL.Path + expires
		} else {
			base = secret + r.URL.Path + expires + r.RemoteAddr
		}

		hasher := md5.New()
		hasher.Write([]byte(base))
		sig := hex.EncodeToString(hasher.Sum(nil))

		if sig[8:16] == signature {
			next.ServeHTTP(w, r)
		} else {
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("Invalid signature"))
		}
	})
}

func main() {
	var root string
	if len(os.Args) > 1 {
		root = os.Args[1]
	} else {
		root = "."
	}

	log.Println("Serve files at " + root)

	handler := http.FileServer(http.Dir(root))
	http.Handle("/", secretHandler(handler))

	port := os.Getenv("PORT")
	if port == "" {
		port = "8700"
	}

	log.Println("Listening on port " + port)
	log.Fatal(http.ListenAndServe("localhost:"+port, nil))
}
<|endoftext|>


<commit_before>package main

import "github.com/go-martini/martini"
import "github.com/zph/go-whois/whois"

func main() {

    m := martini.Classic()

    m.Get("/", func(params martini.Params) string {
        return "Serving up jwhois data at /:domain"
    })
    m.Get("/whois/:domain", func(params martini.Params) string {
        rec := whois.RetrieveJSON(params["domain"])
        return rec
    })
    m.Get("/favicon.ico", func() (int) {
        return 418
    })
    m.NotFound(func() (int, string) {
        return 418, "yolo"
    })
    m.Run()
}
<commit_msg>2014-06-04 00:15:39<commit_after>package main

import "github.com/go-martini/martini"
import "github.com/zph/go-whois/whois"
import "strings"

func main() {

    m := martini.Classic()

    m.Get("/", func(params martini.Params) string {
        return "Serving up jwhois data at /:domain"
    })
    m.Get("/whois/:domain", func(params martini.Params) string {
        rec := whois.RetrieveJSON(params["domain"])
        return rec
    })
    m.Get("/whois/email/:domain", func(params martini.Params) string {
        rec, _ := whois.Retrieve(params["domain"])
        return strings.Join(rec.Emails, ", ")
    })
    m.Get("/favicon.ico", func() (int) {
        return 418
    })
    m.NotFound(func() (int, string) {
        return 418, "yolo"
    })
    m.Run()
}
<|endoftext|>


<commit_before>// Copyright 2014 Jack Wakefield
//
// 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 main

type Server struct {
}

const (
	defaultServerPort = 5674
)

var serverAddress string

func initServer() error {
	return nil
}
<commit_msg>chore(server): add server initialisation and connection handling<commit_after>// Copyright 2014 Jack Wakefield
//
// 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 main

import (
	"fmt"
	"net/http"
)

type Server struct {
	Error            chan error
	Address          string
	Port             int
	Clients          map[*Client]bool
	Broadcast        chan []byte
	Register         chan *Client
	Unregister       chan *Client
	MessageProcessor *MessageProcessor
}

const (
	defaultServerPort = 5674
)

func initServer(address string, port int) (err error) {
	console.Infof("Initialising server on address %s:%d...", address, port)

	if address == "" {
		console.Debug("Resolving the local network addresss...")

		if address, err = externalIpAddress(); err != nil {
			return
		}

		console.Debugf("Address resolved to %s", address)
	}

	console.Debugf("Validating port %d...", port)

	if err = isValidPort(port); err != nil {
		return
	}

	console.Debug("Port deemed valid")
	console.Debug("Creating server...")

	server := newServer(address, port)
	go server.listen()
	go server.run()

	select {
	case err = <-server.Error:
		return
	}

	return nil
}

func newServer(address string, port int) (server *Server) {
	server = &Server{
		Address:    address,
		Port:       port,
		Error:      make(chan error),
		Broadcast:  make(chan []byte),
		Register:   make(chan *Client),
		Unregister: make(chan *Client),
		Clients:    make(map[*Client]bool),
	}

	server.MessageProcessor = NewMessageProcessor(server)

	return
}

func (server *Server) listen() {
	http.HandleFunc("/ws", server.handleWebSocket)

	address := fmt.Sprintf("%s:%d", server.Address, server.Port)
	console.Infof("Server listening on address %s", address)

	if err := http.ListenAndServe(address, nil); err != nil {
		server.Error <- err
	}
}

func (server *Server) run() {
	console.Info("Awaiting client connections...")

	for {
		select {
		case client := <-server.Register:
			server.Clients[client] = true
		case client := <-server.Unregister:
			if _, ok := server.Clients[client]; ok {
				delete(server.Clients, client)
				close(client.Send)
			}
		case message := <-server.Broadcast:
			for client := range server.Clients {
				select {
				case client.Send <- message:
				default:
					close(client.Send)
					delete(server.Clients, client)
				}
			}
		}
	}
}

func (server *Server) handleWebSocket(writer http.ResponseWriter, request *http.Request) {
	console.Debugf("Connection received from %s", request.RemoteAddr)

	if request.Method != "GET" {
		http.Error(writer, "Method not allowed", 405)
		return
	}

	webSocket, err := upgrader.Upgrade(writer, request, nil)

	if err != nil {
		console.Errorf("Failed to upgrade connection (%s)", err)
		return
	}

	client := &Client{
		Send:      make(chan []byte, 256),
		WebSocket: webSocket,
		Server:    server,
	}

	server.Register <- client
	go client.WritePump()
	go server.MessageProcessor.clientConnected(client)
	client.ReadPump()
}
<|endoftext|>


<commit_before>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"sort"
	"time"
)

// Event stores the relevant information on events or updates to the storage layer.
type Event struct {
	Source      string
	Name        string
	Message     string
	Time        time.Time
	IsDirectory bool
}

var events = make([]Event, 0, 100)

// FileEvent stores the nodeid, string, and time of file related events
type FileEvent struct {
	NodeID int32
	Name   string
	Time   time.Time
}

// SendEvent gets events that have happened off to the peer servers so they can replicate the same change
func SendEvent(event Event, fullPath string) {
	// sendEvent to manager
	sendEvent(&event, fullPath, globalSettings.ManagerAddress, globalSettings.ManagerCredentials)

	log.Println("We are NodeA send to our peers")
	// SendEvent to all peers
	for k, v := range serverMap {
		fmt.Printf("Considering sending to: %s\n", k)
		if k != globalSettings.Name {
			fmt.Printf("sending to peer %s at %s\n", k, v.Address)
			sendEvent(&event, fullPath, v.Address, globalSettings.ManagerCredentials)
		}
	}
}

func sendEvent(event *Event, fullPath string, address string, credentials string) {
	url := "http://" + address + "/event/"
	fmt.Printf("target url: %s\n", url)

	// Set the event source  (server name)
	event.Source = globalSettings.Name

	jsonStr, _ := json.Marshal(event)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Set("Content-Type", "application/json")

	data := []byte(credentials)
	authHash := base64.StdEncoding.EncodeToString(data)
	req.Header.Add("Authorization", "Basic "+authHash)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
		return
	}

	defer resp.Body.Close()

	fmt.Println("response Status:", resp.Status)
	fmt.Println("response Headers:", resp.Header)
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("response Body:", string(body))

	if event.Name == "notify.Write" {
		fmt.Printf("source: notify.Write => %s\n", fullPath)
		url := "http://" + address + "/upload/"
		postFile(event.Message, fullPath, url, credentials)
	}
}

func eventHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		json.NewEncoder(w).Encode(events)
	case "POST":
		log.Println("Got POST: ", r.Body)
		decoder := json.NewDecoder(r.Body)
		var event Event
		err := decoder.Decode(&event)
		if err != nil {
			panic("bad json body")
		}

		log.Println(event.Name + ", path: " + event.Message)
		log.Printf("Event info: %v\n", event)

		pathName := globalSettings.Directory + "/" + event.Message
		relativePath := event.Message

		//todo remember these events and skip the logging on the other side. Possibly use nodeID?

		switch event.Name {
		case "notify.Create":
			fmt.Printf("notify.Create: %s\n", pathName)

			server, exists := serverMap[globalSettings.Name]
			if !exists {
				panic("Unable to find server definition")
			}

			fmt.Println("eventHandler->CreatePath")
			server.storage.CreatePath(relativePath, event.IsDirectory)
			fmt.Println("eventHandler->/CreatePath")
		case "notify.Remove":
			err = os.Remove(pathName)
			if err != nil && !os.IsNotExist(err) {
				panic(fmt.Sprintf("Error deleting folder %s: %v\n", pathName, err))
			}
			fmt.Printf("notify.Remove: %s\n", pathName)
		// todo fix this to handle the two rename events to be one event
		case "notify.Rename":
			fmt.Printf("notify.Rename: %s\n", pathName)

			server, exists := serverMap[globalSettings.Name]
			if !exists {
				panic("Unable to find server definition")
			}

			fmt.Println("eventHandler->CreatePath")
			server.storage.CreatePath(relativePath, event.IsDirectory)
			fmt.Println("eventHandler->/CreatePath")

		default:
			fmt.Printf("Unknown event found, doing nothing. Event: %v\n", event)
		}

		events = append([]Event{event}, events...)
	}
}

/*
Send the folder tree from this node to another node for comparison
*/
func sendFolderTree(initialTree DirTreeMap) {
	var tree = make(DirTreeMap)
	prefixLength := 1 // the slash prefix
	for key, value := range initialTree {
		if key == "" {
			continue
		}
		fmt.Printf("key is: '%s', length is: %d\n", key, prefixLength)
		key = key[prefixLength:]
		tree[key] = value
	}

	// package up the tree
	jsonStr, err := json.Marshal(tree)
	if err != nil {
		panic(err)
	}
	buffer := bytes.NewBuffer(jsonStr)
	client := &http.Client{}

	data := []byte(globalSettings.ManagerCredentials)
	authHash := base64.StdEncoding.EncodeToString(data)

	for k, v := range serverMap {
		// don't send an update to ourselves
		if k == globalSettings.Name {
			continue
		}

		url := "http://" + v.Address + "/tree/"
		fmt.Printf("Posting folder tree to node: %s at URL: %s\n", k, url)

		req, err := http.NewRequest("POST", url, buffer)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "Basic "+authHash)

		resp, err := client.Do(req)
		if err != nil {
			fmt.Printf("we encountered an error!\n%s\n", err)
			//panic(err)
			continue
		}

		//fmt.Println("response Status:", resp.Status)
		//fmt.Println("response Headers:", resp.Header)
		_, _ = ioutil.ReadAll(resp.Body)
		//body, _ := ioutil.ReadAll(resp.Body)
		//fmt.Println("response Body:", string(body))
		resp.Body.Close()
	}
}

func getListOfFolders(w http.ResponseWriter) {
	listOfFileInfo, err := scanDirectoryContents()
	if err != nil {
		log.Fatal(err)
	}
	json.NewEncoder(w).Encode(listOfFileInfo)
	fmt.Printf("Sending tree of size %d back to client\n", len(listOfFileInfo))
}

func deletePaths(deletedPaths []string) {
	if globalSettings.Directory == "" {
		panic("globalSettings.Directory is not configured correctly. Aborting")
	}

	fmt.Println("Paths were deleted on the other side. Delete them here")

	fmt.Printf("Paths to delete\nBefore sort:\n%v\n", deletedPaths)
	// Reverse sort the paths so the most specific is first. This allows us to get away without a recursive delete
	sort.Sort(sort.Reverse(sort.StringSlice(deletedPaths)))
	fmt.Printf("Paths to delete after sort\nAfter sort:\n%v\n", deletedPaths)
	for _, relativePath := range deletedPaths {
		if relativePath == "" || relativePath == "/" {
			fmt.Printf("We had a request to delete the base path. Skipping: %s\n", relativePath)
			continue
		}
		fullPath := globalSettings.Directory + relativePath
		fmt.Printf("Full path is: %s\n", fullPath)

		fmt.Printf("%s: about to remove\n", fullPath)

		// stop on any error except for not exist. We are trying to delete it anyway (or rather, it should have been deleted already)
		err := os.Remove(fullPath)
		if err != nil && !os.IsNotExist(err) {
			panic(err)
		}
		fmt.Printf("%s: done removing (err = %v)\n", fullPath, err)
	}
}

func addPaths(newPaths []string) {
	fmt.Println("Paths were added on the other side. Create them here")
	for _, newPathName := range newPaths {
		fmt.Printf("pathname is: %s\n", newPathName)
		err := os.Mkdir(newPathName, os.ModeDir+os.ModePerm)
		//todo figure out how to catch a path exists error.
		if err != nil && !os.IsExist(err) {
			panic(err)
		}
	}
}

func folderTreeHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		getListOfFolders(w)
	case "POST":
		decoder := json.NewDecoder(r.Body)

		var remoteTreePreTranslate DirTreeMap
		err := decoder.Decode(&remoteTreePreTranslate)
		if err != nil {
			panic("bad json body")
		}

		var remoteTree = make(DirTreeMap)
		for key, value := range remoteTreePreTranslate {
			key = fmt.Sprintf("%s/%s", globalSettings.Directory, key)
			remoteTree[key] = value
		}

		// todo get the node name in here
		fmt.Printf("Received a tree map from: %s\n%s\n", r.RemoteAddr, remoteTree)

		// let's compare to the current one we have.
		_, _, newPaths, deletedPaths, _ := checkForChanges(remoteTree, nil)

		// update the paths to be consistent. Remember, everything is backwards since this is comparing the other side to us instead of two successive states
		// deletion will show up as a new path.

		fmt.Printf("about to check for deletions: %s\n", newPaths)
		// delete folders that were deleted. We delete first, then add to make sure that the old ones will not be in the way
		if len(newPaths) > 0 {
			deletePaths(newPaths)
		}

		// add folders that were created
		fmt.Printf("about to check for new folders: %s\n", deletedPaths)
		// add folders that were added.
		if len(deletedPaths) > 0 {
			addPaths(deletedPaths)
		}
	}
}

func postFile(filename string, fullPath string, targetUrl string, credentials string) error {
	body := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(body)
	fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
	if err != nil {
		fmt.Println("error writing to buffer")
		return err
	}

	file, err := os.Open(fullPath)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = io.Copy(fileWriter, file)
	if err != nil {
		fmt.Println("error copying file")
		return err
	}

	myHash, err := fileMd5Hash(fullPath)
	if err != nil {
		fmt.Printf("failed to calculate MD5 Hash for %s\n", fullPath)
		return err
	}

	bodyWriter.WriteField("HASH", myHash)
	contentType := bodyWriter.FormDataContentType()
	err = bodyWriter.Close()
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", targetUrl, body)
	req.Header.Set("Content-Type", contentType)

	data := []byte(credentials)
	authHash := base64.StdEncoding.EncodeToString(data)
	req.Header.Add("Authorization", "Basic "+authHash)

	client := &http.Client{}
	resp, err := client.Do(req)
	defer resp.Body.Close()

	if err != nil {
		return err
	}

	return nil
}

func fileMd5Hash(filePath string) (string, error) {
	var returnMD5String string
	file, err := os.Open(filePath)
	if err != nil {
		return returnMD5String, err
	}
	defer file.Close()

	hash := md5.New()
	_, err = io.Copy(hash, file)
	if err != nil {
		return returnMD5String, err
	}

	hashInBytes := hash.Sum(nil)[:16]

	returnMD5String = hex.EncodeToString(hashInBytes)

	return returnMD5String, nil

}
<commit_msg>remove completed todo<commit_after>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"sort"
	"time"
)

// Event stores the relevant information on events or updates to the storage layer.
type Event struct {
	Source      string
	Name        string
	Message     string
	Time        time.Time
	IsDirectory bool
}

var events = make([]Event, 0, 100)

// FileEvent stores the nodeid, string, and time of file related events
type FileEvent struct {
	NodeID int32
	Name   string
	Time   time.Time
}

// SendEvent gets events that have happened off to the peer servers so they can replicate the same change
func SendEvent(event Event, fullPath string) {
	// sendEvent to manager
	sendEvent(&event, fullPath, globalSettings.ManagerAddress, globalSettings.ManagerCredentials)

	log.Println("We are NodeA send to our peers")
	// SendEvent to all peers
	for k, v := range serverMap {
		fmt.Printf("Considering sending to: %s\n", k)
		if k != globalSettings.Name {
			fmt.Printf("sending to peer %s at %s\n", k, v.Address)
			sendEvent(&event, fullPath, v.Address, globalSettings.ManagerCredentials)
		}
	}
}

func sendEvent(event *Event, fullPath string, address string, credentials string) {
	url := "http://" + address + "/event/"
	fmt.Printf("target url: %s\n", url)

	// Set the event source  (server name)
	event.Source = globalSettings.Name

	jsonStr, _ := json.Marshal(event)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Set("Content-Type", "application/json")

	data := []byte(credentials)
	authHash := base64.StdEncoding.EncodeToString(data)
	req.Header.Add("Authorization", "Basic "+authHash)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
		return
	}

	defer resp.Body.Close()

	fmt.Println("response Status:", resp.Status)
	fmt.Println("response Headers:", resp.Header)
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("response Body:", string(body))

	if event.Name == "notify.Write" {
		fmt.Printf("source: notify.Write => %s\n", fullPath)
		url := "http://" + address + "/upload/"
		postFile(event.Message, fullPath, url, credentials)
	}
}

func eventHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		json.NewEncoder(w).Encode(events)
	case "POST":
		log.Println("Got POST: ", r.Body)
		decoder := json.NewDecoder(r.Body)
		var event Event
		err := decoder.Decode(&event)
		if err != nil {
			panic("bad json body")
		}

		log.Println(event.Name + ", path: " + event.Message)
		log.Printf("Event info: %v\n", event)

		pathName := globalSettings.Directory + "/" + event.Message
		relativePath := event.Message

		//todo remember these events and skip the logging on the other side. Possibly use nodeID?

		switch event.Name {
		case "notify.Create":
			fmt.Printf("notify.Create: %s\n", pathName)

			server, exists := serverMap[globalSettings.Name]
			if !exists {
				panic("Unable to find server definition")
			}

			fmt.Println("eventHandler->CreatePath")
			server.storage.CreatePath(relativePath, event.IsDirectory)
			fmt.Println("eventHandler->/CreatePath")
		case "notify.Remove":
			err = os.Remove(pathName)
			if err != nil && !os.IsNotExist(err) {
				panic(fmt.Sprintf("Error deleting folder %s: %v\n", pathName, err))
			}
			fmt.Printf("notify.Remove: %s\n", pathName)
		// todo fix this to handle the two rename events to be one event
		case "notify.Rename":
			fmt.Printf("notify.Rename: %s\n", pathName)

			server, exists := serverMap[globalSettings.Name]
			if !exists {
				panic("Unable to find server definition")
			}

			fmt.Println("eventHandler->CreatePath")
			server.storage.CreatePath(relativePath, event.IsDirectory)
			fmt.Println("eventHandler->/CreatePath")

		default:
			fmt.Printf("Unknown event found, doing nothing. Event: %v\n", event)
		}

		events = append([]Event{event}, events...)
	}
}

/*
Send the folder tree from this node to another node for comparison
*/
func sendFolderTree(initialTree DirTreeMap) {
	var tree = make(DirTreeMap)
	prefixLength := 1 // the slash prefix
	for key, value := range initialTree {
		if key == "" {
			continue
		}
		fmt.Printf("key is: '%s', length is: %d\n", key, prefixLength)
		key = key[prefixLength:]
		tree[key] = value
	}

	// package up the tree
	jsonStr, err := json.Marshal(tree)
	if err != nil {
		panic(err)
	}
	buffer := bytes.NewBuffer(jsonStr)
	client := &http.Client{}

	data := []byte(globalSettings.ManagerCredentials)
	authHash := base64.StdEncoding.EncodeToString(data)

	for k, v := range serverMap {
		// don't send an update to ourselves
		if k == globalSettings.Name {
			continue
		}

		url := "http://" + v.Address + "/tree/"
		fmt.Printf("Posting folder tree to node: %s at URL: %s\n", k, url)

		req, err := http.NewRequest("POST", url, buffer)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "Basic "+authHash)

		resp, err := client.Do(req)
		if err != nil {
			fmt.Printf("we encountered an error!\n%s\n", err)
			//panic(err)
			continue
		}

		//fmt.Println("response Status:", resp.Status)
		//fmt.Println("response Headers:", resp.Header)
		_, _ = ioutil.ReadAll(resp.Body)
		//body, _ := ioutil.ReadAll(resp.Body)
		//fmt.Println("response Body:", string(body))
		resp.Body.Close()
	}
}

func getListOfFolders(w http.ResponseWriter) {
	listOfFileInfo, err := scanDirectoryContents()
	if err != nil {
		log.Fatal(err)
	}
	json.NewEncoder(w).Encode(listOfFileInfo)
	fmt.Printf("Sending tree of size %d back to client\n", len(listOfFileInfo))
}

func deletePaths(deletedPaths []string) {
	if globalSettings.Directory == "" {
		panic("globalSettings.Directory is not configured correctly. Aborting")
	}

	fmt.Println("Paths were deleted on the other side. Delete them here")

	fmt.Printf("Paths to delete\nBefore sort:\n%v\n", deletedPaths)
	// Reverse sort the paths so the most specific is first. This allows us to get away without a recursive delete
	sort.Sort(sort.Reverse(sort.StringSlice(deletedPaths)))
	fmt.Printf("Paths to delete after sort\nAfter sort:\n%v\n", deletedPaths)
	for _, relativePath := range deletedPaths {
		if relativePath == "" || relativePath == "/" {
			fmt.Printf("We had a request to delete the base path. Skipping: %s\n", relativePath)
			continue
		}
		fullPath := globalSettings.Directory + relativePath
		fmt.Printf("Full path is: %s\n", fullPath)

		fmt.Printf("%s: about to remove\n", fullPath)

		// stop on any error except for not exist. We are trying to delete it anyway (or rather, it should have been deleted already)
		err := os.Remove(fullPath)
		if err != nil && !os.IsNotExist(err) {
			panic(err)
		}
		fmt.Printf("%s: done removing (err = %v)\n", fullPath, err)
	}
}

func addPaths(newPaths []string) {
	fmt.Println("Paths were added on the other side. Create them here")
	for _, newPathName := range newPaths {
		fmt.Printf("pathname is: %s\n", newPathName)
		err := os.Mkdir(newPathName, os.ModeDir+os.ModePerm)
		if err != nil && !os.IsExist(err) {
			panic(err)
		}
	}
}

func folderTreeHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		getListOfFolders(w)
	case "POST":
		decoder := json.NewDecoder(r.Body)

		var remoteTreePreTranslate DirTreeMap
		err := decoder.Decode(&remoteTreePreTranslate)
		if err != nil {
			panic("bad json body")
		}

		var remoteTree = make(DirTreeMap)
		for key, value := range remoteTreePreTranslate {
			key = fmt.Sprintf("%s/%s", globalSettings.Directory, key)
			remoteTree[key] = value
		}

		// todo get the node name in here
		fmt.Printf("Received a tree map from: %s\n%s\n", r.RemoteAddr, remoteTree)

		// let's compare to the current one we have.
		_, _, newPaths, deletedPaths, _ := checkForChanges(remoteTree, nil)

		// update the paths to be consistent. Remember, everything is backwards since this is comparing the other side to us instead of two successive states
		// deletion will show up as a new path.

		fmt.Printf("about to check for deletions: %s\n", newPaths)
		// delete folders that were deleted. We delete first, then add to make sure that the old ones will not be in the way
		if len(newPaths) > 0 {
			deletePaths(newPaths)
		}

		// add folders that were created
		fmt.Printf("about to check for new folders: %s\n", deletedPaths)
		// add folders that were added.
		if len(deletedPaths) > 0 {
			addPaths(deletedPaths)
		}
	}
}

func postFile(filename string, fullPath string, targetUrl string, credentials string) error {
	body := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(body)
	fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
	if err != nil {
		fmt.Println("error writing to buffer")
		return err
	}

	file, err := os.Open(fullPath)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = io.Copy(fileWriter, file)
	if err != nil {
		fmt.Println("error copying file")
		return err
	}

	myHash, err := fileMd5Hash(fullPath)
	if err != nil {
		fmt.Printf("failed to calculate MD5 Hash for %s\n", fullPath)
		return err
	}

	bodyWriter.WriteField("HASH", myHash)
	contentType := bodyWriter.FormDataContentType()
	err = bodyWriter.Close()
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", targetUrl, body)
	req.Header.Set("Content-Type", contentType)

	data := []byte(credentials)
	authHash := base64.StdEncoding.EncodeToString(data)
	req.Header.Add("Authorization", "Basic "+authHash)

	client := &http.Client{}
	resp, err := client.Do(req)
	defer resp.Body.Close()

	if err != nil {
		return err
	}

	return nil
}

func fileMd5Hash(filePath string) (string, error) {
	var returnMD5String string
	file, err := os.Open(filePath)
	if err != nil {
		return returnMD5String, err
	}
	defer file.Close()

	hash := md5.New()
	_, err = io.Copy(hash, file)
	if err != nil {
		return returnMD5String, err
	}

	hashInBytes := hash.Sum(nil)[:16]

	returnMD5String = hex.EncodeToString(hashInBytes)

	return returnMD5String, nil

}
<|endoftext|>


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

import (
	"fmt"
	"net/http"
	"text/template"

	"github.com/gorilla/mux"
)

type Route struct {
	path       string
	handleFunc http.HandlerFunc
}

func NewRoute(p string, hf http.HandlerFunc) *Route {

	r := &Route{path: p, handleFunc: hf}
	return r

}

type ServerError struct {
	Msg string
}

func NewError(msg string) *ServerError {
	s := &ServerError{Msg: msg}
	return s
}

type HttpServer struct {
	port             string
	address          string
	errTemplate      *template.Template
	notFoundTemplate *template.Template
	router           *mux.Router
}

func NewHttpServer(a string, p string) *HttpServer {
	r := mux.NewRouter()
	s := &HttpServer{router: r, address: a, port: p}
	return s
}

type RouteHandler interface {
	GetRoutes() []*Route
}

type ErrHandler func(http.HandlerFunc) http.HandlerFunc

func (s *HttpServer) SetErrTemplate(t *template.Template) {
	s.errTemplate = t
}

func (s *HttpServer) SetNotFoundTemplate(t *template.Template) {
	s.notFoundTemplate = t
}

func (s *HttpServer) errorHandler(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if recoverErr := recover(); recoverErr != nil {
				error := NewError(fmt.Sprintf("\"%v\"", recoverErr))
				w.WriteHeader(500)
				if s.errTemplate != nil {
					s.errTemplate.Execute(w, error)
				} else {
					fmt.Fprintf(w, fmt.Sprintf("\"%v\"", recoverErr))
				}
			}
		}()
		fn(w, r)
	}
}
func (s *HttpServer) NotFound(w http.ResponseWriter, r *http.Request) {

	w.WriteHeader(404)
	if s.notFoundTemplate != nil {
		s.notFoundTemplate.Execute(w, nil)
	} else {
		fmt.Fprintf(w, "Not found")
	}

}

func (s *HttpServer) DeployAtBase(h RouteHandler) {
	s.Deploy("", h)
}

func (s *HttpServer) Deploy(context string, h RouteHandler) {
	routes := h.GetRoutes()
	for _, r := range routes {
		fmt.Println(fmt.Sprintf("%s/%s", context, r.path))
		s.router.HandleFunc(fmt.Sprintf("%s/%s", context, r.path), r.handleFunc)
	}
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
	http.Handle("/", s.router)
	s.router.NotFoundHandler = http.HandlerFunc(s.NotFound)
}

func (s *HttpServer) Start() error {
	return http.ListenAndServe(fmt.Sprintf("%s:%s", s.address, s.port), nil)
}
<commit_msg>traces removed<commit_after>// Copyright 2015 The httpserver Authors. All rights reserved.  Use of this
// source code is governed by a MIT-style license that can be found in the
// LICENSE file.
package httpserver

import (
	"fmt"
	"net/http"
	"text/template"

	"github.com/gorilla/mux"
)

type Route struct {
	path       string
	handleFunc http.HandlerFunc
}

func NewRoute(p string, hf http.HandlerFunc) *Route {

	r := &Route{path: p, handleFunc: hf}
	return r

}

type ServerError struct {
	Msg string
}

func NewError(msg string) *ServerError {
	s := &ServerError{Msg: msg}
	return s
}

type HttpServer struct {
	port             string
	address          string
	errTemplate      *template.Template
	notFoundTemplate *template.Template
	router           *mux.Router
}

func NewHttpServer(a string, p string) *HttpServer {
	r := mux.NewRouter()
	s := &HttpServer{router: r, address: a, port: p}
	return s
}

type RouteHandler interface {
	GetRoutes() []*Route
}

type ErrHandler func(http.HandlerFunc) http.HandlerFunc

func (s *HttpServer) SetErrTemplate(t *template.Template) {
	s.errTemplate = t
}

func (s *HttpServer) SetNotFoundTemplate(t *template.Template) {
	s.notFoundTemplate = t
}

func (s *HttpServer) errorHandler(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if recoverErr := recover(); recoverErr != nil {
				error := NewError(fmt.Sprintf("\"%v\"", recoverErr))
				w.WriteHeader(500)
				if s.errTemplate != nil {
					s.errTemplate.Execute(w, error)
				} else {
					fmt.Fprintf(w, fmt.Sprintf("\"%v\"", recoverErr))
				}
			}
		}()
		fn(w, r)
	}
}
func (s *HttpServer) NotFound(w http.ResponseWriter, r *http.Request) {

	w.WriteHeader(404)
	if s.notFoundTemplate != nil {
		s.notFoundTemplate.Execute(w, nil)
	} else {
		fmt.Fprintf(w, "Not found")
	}

}

func (s *HttpServer) DeployAtBase(h RouteHandler) {
	s.Deploy("", h)
}

func (s *HttpServer) Deploy(context string, h RouteHandler) {
	routes := h.GetRoutes()
	for _, r := range routes {
		s.router.HandleFunc(fmt.Sprintf("%s/%s", context, r.path), r.handleFunc)
	}
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
	http.Handle("/", s.router)
	s.router.NotFoundHandler = http.HandlerFunc(s.NotFound)
}

func (s *HttpServer) Start() error {
	return http.ListenAndServe(fmt.Sprintf("%s:%s", s.address, s.port), nil)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh"
)

const (
	maxNameLength        = 32
	historyLength        = 20
	systemMessageFormat  = "\033[1;3;90m"
	privateMessageFormat = "\033[3m"
	beep                 = "\007"
)

var (
	reStripText = regexp.MustCompile("[^0-9A-Za-z_.-]")
)

// Clients is a map of clients
type Clients map[string]*Client

// Server holds all the fields used by a server
type Server struct {
	sshConfig *ssh.ServerConfig
	done      chan struct{}
	clients   Clients
	count     int
	history   *History
	motd      string
	whitelist map[string]struct{}   // fingerprint lookup
	admins    map[string]struct{}   // fingerprint lookup
	bannedPk  map[string]*time.Time // fingerprint lookup
	bannedIP  map[net.Addr]*time.Time
	started   time.Time
	sync.Mutex
}

// NewServer constructs a new server
func NewServer(privateKey []byte) (*Server, error) {
	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	server := Server{
		done:      make(chan struct{}),
		clients:   Clients{},
		count:     0,
		history:   NewHistory(historyLength),
		motd:      "Message of the Day! Modify with /motd",
		whitelist: map[string]struct{}{},
		admins:    map[string]struct{}{},
		bannedPk:  map[string]*time.Time{},
		bannedIP:  map[net.Addr]*time.Time{},
		started:   time.Now(),
	}

	config := ssh.ServerConfig{
		NoClientAuth: false,
		// Auth-related things should be constant-time to avoid timing attacks.
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			fingerprint := Fingerprint(key)
			if server.IsBanned(fingerprint) {
				return nil, fmt.Errorf("Banned.")
			}
			if !server.IsWhitelisted(fingerprint) {
				return nil, fmt.Errorf("Not Whitelisted.")
			}
			perm := &ssh.Permissions{Extensions: map[string]string{"fingerprint": fingerprint}}
			return perm, nil
		},
	}
	config.AddHostKey(signer)

	server.sshConfig = &config

	return &server, nil
}

// Len returns the number of clients
func (s *Server) Len() int {
	return len(s.clients)
}

// SysMsg broadcasts the given message to everyone
func (s *Server) SysMsg(msg string, args ...interface{}) {
	s.Broadcast(ContinuousFormat(systemMessageFormat, " * "+fmt.Sprintf(msg, args...)), nil)
}

// Broadcast broadcasts the given message to everyone except for the given client
func (s *Server) Broadcast(msg string, except *Client) {
	logger.Debugf("Broadcast to %d: %s", s.Len(), msg)
	s.history.Add(msg)

	for _, client := range s.clients {
		if except != nil && client == except {
			continue
		}

		if strings.Contains(msg, client.Name) {
			// Turn message red if client's name is mentioned, and send BEL if they have enabled beeping
			tmpMsg := strings.Split(msg, Reset)
			if client.beepMe {
				tmpMsg[0] += beep
			}
			client.Send(strings.Join(tmpMsg, Reset+Bold+"\033[31m") + Reset)
		} else {
			client.Send(msg)
		}
	}
}

// Privmsg sends a message to a particular nick, if it exists
func (s *Server) Privmsg(nick, message string, sender *Client) error {
	// Get the recipient
	target, ok := s.clients[nick]
	if !ok {
		return fmt.Errorf("no client with that nick")
	}
	// Send the message
	target.Msg <- fmt.Sprintf(beep+"[PM from %v] %s%v%s", sender.ColoredName(), privateMessageFormat, message, Reset)
	logger.Debugf("PM from %v to %v: %v", sender.Name, nick, message)
	return nil
}

// SetMotd sets the Message of the Day (MOTD)
func (s *Server) SetMotd(motd string) {
	s.Lock()
	s.motd = motd
	s.Unlock()
}

// MotdUnicast sends the MOTD as a SysMsg
func (s *Server) MotdUnicast(client *Client) {
	client.SysMsg("MOTD:\r\n" + ColorString("36", s.motd)) /* a nice cyan color */
}

// MotdBroadcast broadcasts the MOTD
func (s *Server) MotdBroadcast(client *Client) {
	s.Broadcast(ContinuousFormat(systemMessageFormat, fmt.Sprintf(" * New MOTD set by %s.", client.ColoredName())), client)
	s.Broadcast(ColorString("36", s.motd), client)
}

// Add adds the client to the list of clients
func (s *Server) Add(client *Client) {
	go func() {
		s.MotdUnicast(client)
		client.SendLines(s.history.Get(10))
		client.SysMsg("Welcome to ssh-chat. Enter /help for more.")
	}()

	s.Lock()
	s.count++

	newName, err := s.proposeName(client.Name)
	if err != nil {
		client.SysMsg("Your name '%s' is not available, renamed to '%s'. Use /nick <name> to change it.", client.ColoredName(), ColorString(client.Color, newName))
	}

	client.Rename(newName)
	s.clients[client.Name] = client
	num := len(s.clients)
	s.Unlock()

	s.Broadcast(ContinuousFormat(systemMessageFormat, fmt.Sprintf(" * %s joined. (Total connected: %d)", client.ColoredName(), num)), client)
}

// Remove removes the given client from the list of clients
func (s *Server) Remove(client *Client) {
	s.Lock()
	delete(s.clients, client.Name)
	s.Unlock()

	s.SysMsg("%s left.", client.ColoredName())
}

func (s *Server) proposeName(name string) (string, error) {
	// Assumes caller holds lock.
	var err error
	name = reStripText.ReplaceAllString(name, "")

	if len(name) > maxNameLength {
		name = name[:maxNameLength]
	} else if len(name) == 0 {
		name = fmt.Sprintf("Guest%d", s.count)
	}

	_, collision := s.clients[name]
	if collision {
		err = fmt.Errorf("%s is not available", name)
		name = fmt.Sprintf("Guest%d", s.count)
	}

	return name, err
}

// Rename renames the given client (user)
func (s *Server) Rename(client *Client, newName string) {
	s.Lock()

	newName, err := s.proposeName(newName)
	if err != nil {
		client.SysMsg("%s", err)
		s.Unlock()
		return
	}

	// TODO: Use a channel/goroutine for adding clients, rather than locks?
	delete(s.clients, client.Name)
	oldName := client.Name
	client.Rename(newName)
	s.clients[client.Name] = client
	s.Unlock()

	s.SysMsg("%s is now known as %s.", ColorString(client.Color, oldName), ColorString(client.Color, newName))
}

// List lists the clients with the given prefix
func (s *Server) List(prefix *string) []string {
	r := []string{}

	for name := range s.clients {
		if prefix != nil && !strings.HasPrefix(name, *prefix) {
			continue
		}
		r = append(r, name)
	}

	return r
}

// Who returns the client with a given name
func (s *Server) Who(name string) *Client {
	return s.clients[name]
}

// Op adds the given fingerprint to the list of admins
func (s *Server) Op(fingerprint string) {
	logger.Infof("Adding admin: %s", fingerprint)
	s.Lock()
	s.admins[fingerprint] = struct{}{}
	s.Unlock()
}

// Whitelist adds the given fingerprint to the whitelist
func (s *Server) Whitelist(fingerprint string) {
	logger.Infof("Adding whitelist: %s", fingerprint)
	s.Lock()
	s.whitelist[fingerprint] = struct{}{}
	s.Unlock()
}

// Uptime returns the time since the server was started
func (s *Server) Uptime() string {
	return time.Now().Sub(s.started).String()
}

// IsOp checks if the given client is Op
func (s *Server) IsOp(client *Client) bool {
	_, r := s.admins[client.Fingerprint()]
	return r
}

// IsWhitelisted checks if the given fingerprint is whitelisted
func (s *Server) IsWhitelisted(fingerprint string) bool {
	/* if no whitelist, anyone is welcome */
	if len(s.whitelist) == 0 {
		return true
	}

	/* otherwise, check for whitelist presence */
	_, r := s.whitelist[fingerprint]
	return r
}

// IsBanned checks if the given fingerprint is banned
func (s *Server) IsBanned(fingerprint string) bool {
	ban, hasBan := s.bannedPk[fingerprint]
	if !hasBan {
		return false
	}
	if ban == nil {
		return true
	}
	if ban.Before(time.Now()) {
		s.Unban(fingerprint)
		return false
	}
	return true
}

// Ban bans a fingerprint for the given duration
func (s *Server) Ban(fingerprint string, duration *time.Duration) {
	var until *time.Time
	s.Lock()
	if duration != nil {
		when := time.Now().Add(*duration)
		until = &when
	}
	s.bannedPk[fingerprint] = until
	s.Unlock()
}

// Unban unbans a banned fingerprint
func (s *Server) Unban(fingerprint string) {
	s.Lock()
	delete(s.bannedPk, fingerprint)
	s.Unlock()
}

// Start starts the server
func (s *Server) Start(laddr string) error {
	// Once a ServerConfig has been configured, connections can be
	// accepted.
	socket, err := net.Listen("tcp", laddr)
	if err != nil {
		return err
	}

	logger.Infof("Listening on %s", laddr)

	go func() {
		defer socket.Close()
		for {
			conn, err := socket.Accept()

			if err != nil {
				logger.Errorf("Failed to accept connection: %v", err)
				if err == syscall.EINVAL {
					// TODO: Handle shutdown more gracefully?
					return
				}
			}

			// Goroutineify to resume accepting sockets early.
			go func() {
				// From a standard TCP connection to an encrypted SSH connection
				sshConn, channels, requests, err := ssh.NewServerConn(conn, s.sshConfig)
				if err != nil {
					logger.Errorf("Failed to handshake: %v", err)
					return
				}

				version := reStripText.ReplaceAllString(string(sshConn.ClientVersion()), "")
				if len(version) > 100 {
					version = "Evil Jerk with a superlong string"
				}
				logger.Infof("Connection #%d from: %s, %s, %s", s.count+1, sshConn.RemoteAddr(), sshConn.User(), version)

				go ssh.DiscardRequests(requests)

				client := NewClient(s, sshConn)
				go client.handleChannels(channels)
			}()
		}
	}()

	go func() {
		<-s.done
		socket.Close()
	}()

	return nil
}

// AutoCompleteFunction handles auto completion of nicks
func (s *Server) AutoCompleteFunction(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
	if key == 9 {
		shortLine := strings.Split(line[:pos], " ")
		partialNick := shortLine[len(shortLine)-1]

		nicks := s.List(&partialNick)
		if len(nicks) > 0 {
			nick := nicks[len(nicks)-1]
			posPartialNick := pos - len(partialNick)
			if len(shortLine) < 2 {
				nick += ": "
			} else {
				nick += " "
			}
			newLine = strings.Replace(line[posPartialNick:],
				partialNick, nick, 1)
			newLine = line[:posPartialNick] + newLine
			newPos = pos + (len(nick) - len(partialNick))
			ok = true
		}
	} else {
		ok = false
	}
	return
}

// Stop stops the server
func (s *Server) Stop() {
	for _, client := range s.clients {
		client.Conn.Close()
	}

	close(s.done)
}

// Fingerprint returns the fingerprint based on a public key
func Fingerprint(k ssh.PublicKey) string {
	hash := md5.Sum(k.Marshal())
	r := fmt.Sprintf("% x", hash)
	return strings.Replace(r, " ", ":", -1)
}
<commit_msg>Changed BannedPk to BannedPK, removed bannedIP<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"net"
	"regexp"
	"strings"
	"sync"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh"
)

const (
	maxNameLength        = 32
	historyLength        = 20
	systemMessageFormat  = "\033[1;3;90m"
	privateMessageFormat = "\033[3m"
	beep                 = "\007"
)

var (
	reStripText = regexp.MustCompile("[^0-9A-Za-z_.-]")
)

// Clients is a map of clients
type Clients map[string]*Client

// Server holds all the fields used by a server
type Server struct {
	sshConfig *ssh.ServerConfig
	done      chan struct{}
	clients   Clients
	count     int
	history   *History
	motd      string
	whitelist map[string]struct{}   // fingerprint lookup
	admins    map[string]struct{}   // fingerprint lookup
	bannedPK  map[string]*time.Time // fingerprint lookup
	started   time.Time
	sync.Mutex
}

// NewServer constructs a new server
func NewServer(privateKey []byte) (*Server, error) {
	signer, err := ssh.ParsePrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	server := Server{
		done:      make(chan struct{}),
		clients:   Clients{},
		count:     0,
		history:   NewHistory(historyLength),
		motd:      "Message of the Day! Modify with /motd",
		whitelist: map[string]struct{}{},
		admins:    map[string]struct{}{},
		bannedPK:  map[string]*time.Time{},
		started:   time.Now(),
	}

	config := ssh.ServerConfig{
		NoClientAuth: false,
		// Auth-related things should be constant-time to avoid timing attacks.
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			fingerprint := Fingerprint(key)
			if server.IsBanned(fingerprint) {
				return nil, fmt.Errorf("Banned.")
			}
			if !server.IsWhitelisted(fingerprint) {
				return nil, fmt.Errorf("Not Whitelisted.")
			}
			perm := &ssh.Permissions{Extensions: map[string]string{"fingerprint": fingerprint}}
			return perm, nil
		},
	}
	config.AddHostKey(signer)

	server.sshConfig = &config

	return &server, nil
}

// Len returns the number of clients
func (s *Server) Len() int {
	return len(s.clients)
}

// SysMsg broadcasts the given message to everyone
func (s *Server) SysMsg(msg string, args ...interface{}) {
	s.Broadcast(ContinuousFormat(systemMessageFormat, " * "+fmt.Sprintf(msg, args...)), nil)
}

// Broadcast broadcasts the given message to everyone except for the given client
func (s *Server) Broadcast(msg string, except *Client) {
	logger.Debugf("Broadcast to %d: %s", s.Len(), msg)
	s.history.Add(msg)

	for _, client := range s.clients {
		if except != nil && client == except {
			continue
		}

		if strings.Contains(msg, client.Name) {
			// Turn message red if client's name is mentioned, and send BEL if they have enabled beeping
			tmpMsg := strings.Split(msg, Reset)
			if client.beepMe {
				tmpMsg[0] += beep
			}
			client.Send(strings.Join(tmpMsg, Reset+Bold+"\033[31m") + Reset)
		} else {
			client.Send(msg)
		}
	}
}

// Privmsg sends a message to a particular nick, if it exists
func (s *Server) Privmsg(nick, message string, sender *Client) error {
	// Get the recipient
	target, ok := s.clients[nick]
	if !ok {
		return fmt.Errorf("no client with that nick")
	}
	// Send the message
	target.Msg <- fmt.Sprintf(beep+"[PM from %v] %s%v%s", sender.ColoredName(), privateMessageFormat, message, Reset)
	logger.Debugf("PM from %v to %v: %v", sender.Name, nick, message)
	return nil
}

// SetMotd sets the Message of the Day (MOTD)
func (s *Server) SetMotd(motd string) {
	s.Lock()
	s.motd = motd
	s.Unlock()
}

// MotdUnicast sends the MOTD as a SysMsg
func (s *Server) MotdUnicast(client *Client) {
	client.SysMsg("MOTD:\r\n" + ColorString("36", s.motd)) /* a nice cyan color */
}

// MotdBroadcast broadcasts the MOTD
func (s *Server) MotdBroadcast(client *Client) {
	s.Broadcast(ContinuousFormat(systemMessageFormat, fmt.Sprintf(" * New MOTD set by %s.", client.ColoredName())), client)
	s.Broadcast(ColorString("36", s.motd), client)
}

// Add adds the client to the list of clients
func (s *Server) Add(client *Client) {
	go func() {
		s.MotdUnicast(client)
		client.SendLines(s.history.Get(10))
		client.SysMsg("Welcome to ssh-chat. Enter /help for more.")
	}()

	s.Lock()
	s.count++

	newName, err := s.proposeName(client.Name)
	if err != nil {
		client.SysMsg("Your name '%s' is not available, renamed to '%s'. Use /nick <name> to change it.", client.ColoredName(), ColorString(client.Color, newName))
	}

	client.Rename(newName)
	s.clients[client.Name] = client
	num := len(s.clients)
	s.Unlock()

	s.Broadcast(ContinuousFormat(systemMessageFormat, fmt.Sprintf(" * %s joined. (Total connected: %d)", client.ColoredName(), num)), client)
}

// Remove removes the given client from the list of clients
func (s *Server) Remove(client *Client) {
	s.Lock()
	delete(s.clients, client.Name)
	s.Unlock()

	s.SysMsg("%s left.", client.ColoredName())
}

func (s *Server) proposeName(name string) (string, error) {
	// Assumes caller holds lock.
	var err error
	name = reStripText.ReplaceAllString(name, "")

	if len(name) > maxNameLength {
		name = name[:maxNameLength]
	} else if len(name) == 0 {
		name = fmt.Sprintf("Guest%d", s.count)
	}

	_, collision := s.clients[name]
	if collision {
		err = fmt.Errorf("%s is not available", name)
		name = fmt.Sprintf("Guest%d", s.count)
	}

	return name, err
}

// Rename renames the given client (user)
func (s *Server) Rename(client *Client, newName string) {
	s.Lock()

	newName, err := s.proposeName(newName)
	if err != nil {
		client.SysMsg("%s", err)
		s.Unlock()
		return
	}

	// TODO: Use a channel/goroutine for adding clients, rather than locks?
	delete(s.clients, client.Name)
	oldName := client.Name
	client.Rename(newName)
	s.clients[client.Name] = client
	s.Unlock()

	s.SysMsg("%s is now known as %s.", ColorString(client.Color, oldName), ColorString(client.Color, newName))
}

// List lists the clients with the given prefix
func (s *Server) List(prefix *string) []string {
	r := []string{}

	for name := range s.clients {
		if prefix != nil && !strings.HasPrefix(name, *prefix) {
			continue
		}
		r = append(r, name)
	}

	return r
}

// Who returns the client with a given name
func (s *Server) Who(name string) *Client {
	return s.clients[name]
}

// Op adds the given fingerprint to the list of admins
func (s *Server) Op(fingerprint string) {
	logger.Infof("Adding admin: %s", fingerprint)
	s.Lock()
	s.admins[fingerprint] = struct{}{}
	s.Unlock()
}

// Whitelist adds the given fingerprint to the whitelist
func (s *Server) Whitelist(fingerprint string) {
	logger.Infof("Adding whitelist: %s", fingerprint)
	s.Lock()
	s.whitelist[fingerprint] = struct{}{}
	s.Unlock()
}

// Uptime returns the time since the server was started
func (s *Server) Uptime() string {
	return time.Now().Sub(s.started).String()
}

// IsOp checks if the given client is Op
func (s *Server) IsOp(client *Client) bool {
	_, r := s.admins[client.Fingerprint()]
	return r
}

// IsWhitelisted checks if the given fingerprint is whitelisted
func (s *Server) IsWhitelisted(fingerprint string) bool {
	/* if no whitelist, anyone is welcome */
	if len(s.whitelist) == 0 {
		return true
	}

	/* otherwise, check for whitelist presence */
	_, r := s.whitelist[fingerprint]
	return r
}

// IsBanned checks if the given fingerprint is banned
func (s *Server) IsBanned(fingerprint string) bool {
	ban, hasBan := s.bannedPK[fingerprint]
	if !hasBan {
		return false
	}
	if ban == nil {
		return true
	}
	if ban.Before(time.Now()) {
		s.Unban(fingerprint)
		return false
	}
	return true
}

// Ban bans a fingerprint for the given duration
func (s *Server) Ban(fingerprint string, duration *time.Duration) {
	var until *time.Time
	s.Lock()
	if duration != nil {
		when := time.Now().Add(*duration)
		until = &when
	}
	s.bannedPK[fingerprint] = until
	s.Unlock()
}

// Unban unbans a banned fingerprint
func (s *Server) Unban(fingerprint string) {
	s.Lock()
	delete(s.bannedPK, fingerprint)
	s.Unlock()
}

// Start starts the server
func (s *Server) Start(laddr string) error {
	// Once a ServerConfig has been configured, connections can be
	// accepted.
	socket, err := net.Listen("tcp", laddr)
	if err != nil {
		return err
	}

	logger.Infof("Listening on %s", laddr)

	go func() {
		defer socket.Close()
		for {
			conn, err := socket.Accept()

			if err != nil {
				logger.Errorf("Failed to accept connection: %v", err)
				if err == syscall.EINVAL {
					// TODO: Handle shutdown more gracefully?
					return
				}
			}

			// Goroutineify to resume accepting sockets early.
			go func() {
				// From a standard TCP connection to an encrypted SSH connection
				sshConn, channels, requests, err := ssh.NewServerConn(conn, s.sshConfig)
				if err != nil {
					logger.Errorf("Failed to handshake: %v", err)
					return
				}

				version := reStripText.ReplaceAllString(string(sshConn.ClientVersion()), "")
				if len(version) > 100 {
					version = "Evil Jerk with a superlong string"
				}
				logger.Infof("Connection #%d from: %s, %s, %s", s.count+1, sshConn.RemoteAddr(), sshConn.User(), version)

				go ssh.DiscardRequests(requests)

				client := NewClient(s, sshConn)
				go client.handleChannels(channels)
			}()
		}
	}()

	go func() {
		<-s.done
		socket.Close()
	}()

	return nil
}

// AutoCompleteFunction handles auto completion of nicks
func (s *Server) AutoCompleteFunction(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
	if key == 9 {
		shortLine := strings.Split(line[:pos], " ")
		partialNick := shortLine[len(shortLine)-1]

		nicks := s.List(&partialNick)
		if len(nicks) > 0 {
			nick := nicks[len(nicks)-1]
			posPartialNick := pos - len(partialNick)
			if len(shortLine) < 2 {
				nick += ": "
			} else {
				nick += " "
			}
			newLine = strings.Replace(line[posPartialNick:],
				partialNick, nick, 1)
			newLine = line[:posPartialNick] + newLine
			newPos = pos + (len(nick) - len(partialNick))
			ok = true
		}
	} else {
		ok = false
	}
	return
}

// Stop stops the server
func (s *Server) Stop() {
	for _, client := range s.clients {
		client.Conn.Close()
	}

	close(s.done)
}

// Fingerprint returns the fingerprint based on a public key
func Fingerprint(k ssh.PublicKey) string {
	hash := md5.Sum(k.Marshal())
	r := fmt.Sprintf("% x", hash)
	return strings.Replace(r, " ", ":", -1)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/digitalocean/godo"
	"golang.org/x/oauth2"
)

type accessTokenSource struct {
	AccessToken string
}

func (t *accessTokenSource) Token() (*oauth2.Token, error) {
	token := &oauth2.Token{
		AccessToken: t.AccessToken,
	}
	return token, nil
}

func NewDoAPIClient(accessToken string) *godo.Client {
	accessTokenSource := &accessTokenSource{
		AccessToken: accessToken,
	}

	oauthClient := oauth2.NewClient(oauth2.NoContext, accessTokenSource)
	client := godo.NewClient(oauthClient)

	return client
}
<commit_msg>Use the built-in OAuth2 static token source<commit_after>package main

import (
	"github.com/digitalocean/godo"
	"golang.org/x/oauth2"
)

func NewDoAPIClient(accessToken string) *godo.Client {
	accessTokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken})

	oauthClient := oauth2.NewClient(oauth2.NoContext, accessTokenSource)
	client := godo.NewClient(oauthClient)

	return client
}
<|endoftext|>


<commit_before>package lapack

import (
	"testing"

	"github.com/ready-steady/assert"
	"github.com/ready-steady/fixture"
)

func BenchmarkDSYEV(bench *testing.B) {
	n := 1000
	a := fixture.MakeSymmetricMatrix(uint(n))
	w := make([]float64, n)
	lwork := 4 * n
	work := make([]float64, lwork)
	info := 0

	bench.ResetTimer()

	for i := 0; i < bench.N; i++ {
		DSYEV('V', 'U', n, a, n, w, work, lwork, &info)
	}
}

func TestDGTSV(t *testing.T) {
	n := 10
	nrhs := 2

	dl := []float64{
		4.1702200470257400e-01, 7.2032449344215810e-01, 1.1437481734488664e-04,
		3.0233257263183977e-01, 1.4675589081711304e-01, 9.2338594768797799e-02,
		1.8626021137767090e-01, 3.4556072704304774e-01, 3.9676747423066994e-01,
	}
	d := []float64{
		1.4191945144032947e+00, 1.6852195003967596e+00, 1.2044522497315175e+00,
		1.8781174363909454e+00, 1.0273875931979262e+00, 1.6704675101784021e+00,
		1.4173048023671271e+00, 1.5586898284457518e+00, 1.1403869385952339e+00,
		1.1981014890848787e+00,
	}
	du := []float64{
		9.6826157571939753e-01, 3.1342417815924284e-01, 6.9232261566931408e-01,
		8.7638915229603831e-01, 8.9460666350384732e-01, 8.5044211369777911e-02,
		3.9054783232882362e-02, 1.6983041956456890e-01, 8.7814250342941313e-01,
	}
	b := []float64{
		9.8886108890649471e-01, 7.4816565437983940e-01, 2.8044399206440518e-01,
		7.8927932845148852e-01, 1.0322600657764203e-01, 4.4789352617590517e-01,
		9.0859550309309556e-01, 2.9361414837367950e-01, 2.8777533858634874e-01,
		1.3002857211827767e-01, 1.9366957870297075e-02, 6.7883553293989096e-01,
		2.1162811600005904e-01, 2.6554665937222621e-01, 4.9157315928033829e-01,
		5.3362545117080384e-02, 5.7411760549201307e-01, 1.4672857490581015e-01,
		5.8930553690328424e-01, 6.9975836002093117e-01,
	}

	info := 0

	DGTSV(n, nrhs, dl, d, du, b, n, &info)

	assert.Equal(info, 0, t)

	expectedB := []float64{
		+4.2413660994635355e-01, +3.9961178716148082e-01, -3.2589132682920774e-01,
		+5.5626432959857730e-01, -2.9143803729175111e-01, +2.6209172590772878e-01,
		+6.2141868670421718e-01, +9.3577077457946675e-02, +1.8848763199672364e-01,
		+4.6108623476807969e-02, -3.2256530997806154e-01, +4.9279026276907484e-01,
		-5.4582244963076967e-02, -1.1208513329599634e-01, +5.4320839567983115e-01,
		-3.6469011007728362e-02, +4.0642167843987542e-01, +3.7427069391596034e-02,
		+7.4728631269099247e-02, +5.5930860268891813e-01,
	}

	assert.EqualWithin(b, expectedB, 1e-15, t)
}

func TestDSYEV(t *testing.T) {
	n := 5

	a := []float64{
		0.162182308193243, 0.601981941401637, 0.450541598502498,
		0.825816977489547, 0.106652770180584, 0.601981941401637,
		0.262971284540144, 0.083821377996933, 0.538342435260057,
		0.961898080855054, 0.450541598502498, 0.083821377996933,
		0.228976968716819, 0.996134716626885, 0.004634224134067,
		0.825816977489547, 0.538342435260057, 0.996134716626885,
		0.078175528753184, 0.774910464711502, 0.106652770180584,
		0.961898080855054, 0.004634224134067, 0.774910464711502,
		0.817303220653433,
	}

	w := make([]float64, n)
	work := []float64{0}
	lwork := -1
	info := 0

	DSYEV('V', 'U', n, a, n, w, work, lwork, &info)

	lwork = int(work[0])
	work = make([]float64, lwork)

	DSYEV('V', 'U', n, a, n, w, work, lwork, &info)

	assert.Equal(info, 0, t)

	expectedA := []float64{
		-0.350512137830478, +0.116468084895727, -0.435005782872646,
		+0.750503447417042, -0.333303121372602, +0.462361750400701,
		-0.693041256027589, -0.409079614137348, +0.219801690292016,
		+0.300427221556423, -0.638529696902280, -0.450088584675982,
		+0.439292414730346, +0.201686466663592, +0.395025107611391,
		-0.330279135877552, +0.306798908225146, -0.590447291483772,
		-0.272545278020086, +0.611458248553625, +0.382829428829298,
		+0.457628341015560, +0.319089342511548, +0.522946873930437,
		+0.518388356797788,
	}

	expectedW := []float64{
		-1.145487871954612, -0.676875725405419, -0.050275996742486,
		+0.892450858666551, +2.529798046292787,
	}

	assert.EqualWithin(a, expectedA, 2e-15, t)
	assert.EqualWithin(w, expectedW, 2e-15, t)
}
<commit_msg>Add a test for DGETRF and DGETRI<commit_after>package lapack

import (
	"testing"

	"github.com/ready-steady/assert"
	"github.com/ready-steady/fixture"
)

func BenchmarkDSYEV(bench *testing.B) {
	n := 1000
	a := fixture.MakeSymmetricMatrix(uint(n))
	w := make([]float64, n)
	lwork := 4 * n
	work := make([]float64, lwork)
	info := 0

	bench.ResetTimer()

	for i := 0; i < bench.N; i++ {
		DSYEV('V', 'U', n, a, n, w, work, lwork, &info)
	}
}

func TestDGETRI(t *testing.T) {
	n := 3
	a := []float64{1, -1, 0, 0, 5, 3, 2, 0, -9}
	ipiv := make([]int, n+1)
	info := 0

	DGETRF(n, n, a, n, ipiv, &info)
	assert.Equal(info, 0, t)

	lwork := n * n
	work := make([]float64, lwork)

	DGETRI(n, a, n, ipiv, work, lwork, &info)
	assert.Equal(info, 0, t)

	expectedA := []float64{
		+8.823529411764706e-01, +1.764705882352941e-01, +5.882352941176472e-02,
		-1.176470588235294e-01, +1.764705882352941e-01, +5.882352941176472e-02,
		+1.960784313725490e-01, +3.921568627450981e-02, -9.803921568627452e-02,
	}

	assert.EqualWithin(a, expectedA, 1e-15, t)
}

func TestDGTSV(t *testing.T) {
	n := 10
	nrhs := 2

	dl := []float64{
		4.1702200470257400e-01, 7.2032449344215810e-01, 1.1437481734488664e-04,
		3.0233257263183977e-01, 1.4675589081711304e-01, 9.2338594768797799e-02,
		1.8626021137767090e-01, 3.4556072704304774e-01, 3.9676747423066994e-01,
	}
	d := []float64{
		1.4191945144032947e+00, 1.6852195003967596e+00, 1.2044522497315175e+00,
		1.8781174363909454e+00, 1.0273875931979262e+00, 1.6704675101784021e+00,
		1.4173048023671271e+00, 1.5586898284457518e+00, 1.1403869385952339e+00,
		1.1981014890848787e+00,
	}
	du := []float64{
		9.6826157571939753e-01, 3.1342417815924284e-01, 6.9232261566931408e-01,
		8.7638915229603831e-01, 8.9460666350384732e-01, 8.5044211369777911e-02,
		3.9054783232882362e-02, 1.6983041956456890e-01, 8.7814250342941313e-01,
	}
	b := []float64{
		9.8886108890649471e-01, 7.4816565437983940e-01, 2.8044399206440518e-01,
		7.8927932845148852e-01, 1.0322600657764203e-01, 4.4789352617590517e-01,
		9.0859550309309556e-01, 2.9361414837367950e-01, 2.8777533858634874e-01,
		1.3002857211827767e-01, 1.9366957870297075e-02, 6.7883553293989096e-01,
		2.1162811600005904e-01, 2.6554665937222621e-01, 4.9157315928033829e-01,
		5.3362545117080384e-02, 5.7411760549201307e-01, 1.4672857490581015e-01,
		5.8930553690328424e-01, 6.9975836002093117e-01,
	}

	info := 0

	DGTSV(n, nrhs, dl, d, du, b, n, &info)
	assert.Equal(info, 0, t)

	expectedB := []float64{
		+4.2413660994635355e-01, +3.9961178716148082e-01, -3.2589132682920774e-01,
		+5.5626432959857730e-01, -2.9143803729175111e-01, +2.6209172590772878e-01,
		+6.2141868670421718e-01, +9.3577077457946675e-02, +1.8848763199672364e-01,
		+4.6108623476807969e-02, -3.2256530997806154e-01, +4.9279026276907484e-01,
		-5.4582244963076967e-02, -1.1208513329599634e-01, +5.4320839567983115e-01,
		-3.6469011007728362e-02, +4.0642167843987542e-01, +3.7427069391596034e-02,
		+7.4728631269099247e-02, +5.5930860268891813e-01,
	}

	assert.EqualWithin(b, expectedB, 1e-15, t)
}

func TestDSYEV(t *testing.T) {
	n := 5

	a := []float64{
		0.162182308193243, 0.601981941401637, 0.450541598502498,
		0.825816977489547, 0.106652770180584, 0.601981941401637,
		0.262971284540144, 0.083821377996933, 0.538342435260057,
		0.961898080855054, 0.450541598502498, 0.083821377996933,
		0.228976968716819, 0.996134716626885, 0.004634224134067,
		0.825816977489547, 0.538342435260057, 0.996134716626885,
		0.078175528753184, 0.774910464711502, 0.106652770180584,
		0.961898080855054, 0.004634224134067, 0.774910464711502,
		0.817303220653433,
	}

	w := make([]float64, n)
	work := []float64{0}
	lwork := -1
	info := 0

	DSYEV('V', 'U', n, a, n, w, work, lwork, &info)
	assert.Equal(info, 0, t)

	lwork = int(work[0])
	work = make([]float64, lwork)

	DSYEV('V', 'U', n, a, n, w, work, lwork, &info)
	assert.Equal(info, 0, t)

	expectedA := []float64{
		-0.350512137830478, +0.116468084895727, -0.435005782872646,
		+0.750503447417042, -0.333303121372602, +0.462361750400701,
		-0.693041256027589, -0.409079614137348, +0.219801690292016,
		+0.300427221556423, -0.638529696902280, -0.450088584675982,
		+0.439292414730346, +0.201686466663592, +0.395025107611391,
		-0.330279135877552, +0.306798908225146, -0.590447291483772,
		-0.272545278020086, +0.611458248553625, +0.382829428829298,
		+0.457628341015560, +0.319089342511548, +0.522946873930437,
		+0.518388356797788,
	}

	expectedW := []float64{
		-1.145487871954612, -0.676875725405419, -0.050275996742486,
		+0.892450858666551, +2.529798046292787,
	}

	assert.EqualWithin(a, expectedA, 2e-15, t)
	assert.EqualWithin(w, expectedW, 2e-15, t)
}
<|endoftext|>


<commit_before>// Package layer is package for managing read-only
// and read-write mounts on the union file system
// driver. Read-only mounts are referenced using a
// content hash and are protected from mutation in
// the exposed interface. The tar format is used
// to create read-only layers and export both
// read-only and writable layers. The exported
// tar data for a read-only layer should match
// the tar used to create the layer.
package layer // import "github.com/docker/docker/layer"

import (
	"errors"
	"io"

	"github.com/docker/distribution"
	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/containerfs"
	digest "github.com/opencontainers/go-digest"
	"github.com/sirupsen/logrus"
)

var (
	// ErrLayerDoesNotExist is used when an operation is
	// attempted on a layer which does not exist.
	ErrLayerDoesNotExist = errors.New("layer does not exist")

	// ErrLayerNotRetained is used when a release is
	// attempted on a layer which is not retained.
	ErrLayerNotRetained = errors.New("layer not retained")

	// ErrMountDoesNotExist is used when an operation is
	// attempted on a mount layer which does not exist.
	ErrMountDoesNotExist = errors.New("mount does not exist")

	// ErrMountNameConflict is used when a mount is attempted
	// to be created but there is already a mount with the name
	// used for creation.
	ErrMountNameConflict = errors.New("mount already exists with name")

	// ErrActiveMount is used when an operation on a
	// mount is attempted but the layer is still
	// mounted and the operation cannot be performed.
	ErrActiveMount = errors.New("mount still active")

	// ErrNotMounted is used when requesting an active
	// mount but the layer is not mounted.
	ErrNotMounted = errors.New("not mounted")

	// ErrMaxDepthExceeded is used when a layer is attempted
	// to be created which would result in a layer depth
	// greater than the 125 max.
	ErrMaxDepthExceeded = errors.New("max depth exceeded")

	// ErrNotSupported is used when the action is not supported
	// on the current host operating system.
	ErrNotSupported = errors.New("not support on this host operating system")
)

// ChainID is the content-addressable ID of a layer.
type ChainID digest.Digest

// String returns a string rendition of a layer ID
func (id ChainID) String() string {
	return string(id)
}

// DiffID is the hash of an individual layer tar.
type DiffID digest.Digest

// String returns a string rendition of a layer DiffID
func (diffID DiffID) String() string {
	return string(diffID)
}

// TarStreamer represents an object which may
// have its contents exported as a tar stream.
type TarStreamer interface {
	// TarStream returns a tar archive stream
	// for the contents of a layer.
	TarStream() (io.ReadCloser, error)
}

// Layer represents a read-only layer
type Layer interface {
	TarStreamer

	// TarStreamFrom returns a tar archive stream for all the layer chain with
	// arbitrary depth.
	TarStreamFrom(ChainID) (io.ReadCloser, error)

	// ChainID returns the content hash of the entire layer chain. The hash
	// chain is made up of DiffID of top layer and all of its parents.
	ChainID() ChainID

	// DiffID returns the content hash of the layer
	// tar stream used to create this layer.
	DiffID() DiffID

	// Parent returns the next layer in the layer chain.
	Parent() Layer

	// Size returns the size of the entire layer chain. The size
	// is calculated from the total size of all files in the layers.
	Size() (int64, error)

	// DiffSize returns the size difference of the top layer
	// from parent layer.
	DiffSize() (int64, error)

	// Metadata returns the low level storage metadata associated
	// with layer.
	Metadata() (map[string]string, error)
}

// RWLayer represents a layer which is
// read and writable
type RWLayer interface {
	TarStreamer

	// Name of mounted layer
	Name() string

	// Parent returns the layer which the writable
	// layer was created from.
	Parent() Layer

	// Mount mounts the RWLayer and returns the filesystem path
	// the to the writable layer.
	Mount(mountLabel string) (containerfs.ContainerFS, error)

	// Unmount unmounts the RWLayer. This should be called
	// for every mount. If there are multiple mount calls
	// this operation will only decrement the internal mount counter.
	Unmount() error

	// Size represents the size of the writable layer
	// as calculated by the total size of the files
	// changed in the mutable layer.
	Size() (int64, error)

	// Changes returns the set of changes for the mutable layer
	// from the base layer.
	Changes() ([]archive.Change, error)

	// Metadata returns the low level metadata for the mutable layer
	Metadata() (map[string]string, error)

	// ApplyDiff applies the diff to the RW layer
	ApplyDiff(diff io.Reader) (int64, error)
}

// Metadata holds information about a
// read-only layer
type Metadata struct {
	// ChainID is the content hash of the layer
	ChainID ChainID

	// DiffID is the hash of the tar data used to
	// create the layer
	DiffID DiffID

	// Size is the size of the layer and all parents
	Size int64

	// DiffSize is the size of the top layer
	DiffSize int64
}

// MountInit is a function to initialize a
// writable mount. Changes made here will
// not be included in the Tar stream of the
// RWLayer.
type MountInit func(root containerfs.ContainerFS) error

// CreateRWLayerOpts contains optional arguments to be passed to CreateRWLayer
type CreateRWLayerOpts struct {
	MountLabel string
	InitFunc   MountInit
	StorageOpt map[string]string
}

// Store represents a backend for managing both
// read-only and read-write layers.
type Store interface {
	Register(io.Reader, ChainID) (Layer, error)
	Get(ChainID) (Layer, error)
	Map() map[ChainID]Layer
	Release(Layer) ([]Metadata, error)

	CreateRWLayer(id string, parent ChainID, opts *CreateRWLayerOpts) (RWLayer, error)
	GetRWLayer(id string) (RWLayer, error)
	GetMountID(id string) (string, error)
	ReleaseRWLayer(RWLayer) ([]Metadata, error)

	Cleanup() error
	DriverStatus() [][2]string
	DriverName() string
}

// DescribableStore represents a layer store capable of storing
// descriptors for layers.
type DescribableStore interface {
	RegisterWithDescriptor(io.Reader, ChainID, distribution.Descriptor) (Layer, error)
}

// CreateChainID returns ID for a layerDigest slice
func CreateChainID(dgsts []DiffID) ChainID {
	return createChainIDFromParent("", dgsts...)
}

func createChainIDFromParent(parent ChainID, dgsts ...DiffID) ChainID {
	if len(dgsts) == 0 {
		return parent
	}
	if parent == "" {
		return createChainIDFromParent(ChainID(dgsts[0]), dgsts[1:]...)
	}
	// H = "H(n-1) SHA256(n)"
	dgst := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0])))
	return createChainIDFromParent(ChainID(dgst), dgsts[1:]...)
}

// ReleaseAndLog releases the provided layer from the given layer
// store, logging any error and release metadata
func ReleaseAndLog(ls Store, l Layer) {
	metadata, err := ls.Release(l)
	if err != nil {
		logrus.Errorf("Error releasing layer %s: %v", l.ChainID(), err)
	}
	LogReleaseMetadata(metadata)
}

// LogReleaseMetadata logs a metadata array, uses this to
// ensure consistent logging for release metadata
func LogReleaseMetadata(metadatas []Metadata) {
	for _, metadata := range metadatas {
		logrus.Infof("Layer %s cleaned up", metadata.ChainID)
	}
}
<commit_msg>fix typo in comments<commit_after>// Package layer is package for managing read-only
// and read-write mounts on the union file system
// driver. Read-only mounts are referenced using a
// content hash and are protected from mutation in
// the exposed interface. The tar format is used
// to create read-only layers and export both
// read-only and writable layers. The exported
// tar data for a read-only layer should match
// the tar used to create the layer.
package layer // import "github.com/docker/docker/layer"

import (
	"errors"
	"io"

	"github.com/docker/distribution"
	"github.com/docker/docker/pkg/archive"
	"github.com/docker/docker/pkg/containerfs"
	digest "github.com/opencontainers/go-digest"
	"github.com/sirupsen/logrus"
)

var (
	// ErrLayerDoesNotExist is used when an operation is
	// attempted on a layer which does not exist.
	ErrLayerDoesNotExist = errors.New("layer does not exist")

	// ErrLayerNotRetained is used when a release is
	// attempted on a layer which is not retained.
	ErrLayerNotRetained = errors.New("layer not retained")

	// ErrMountDoesNotExist is used when an operation is
	// attempted on a mount layer which does not exist.
	ErrMountDoesNotExist = errors.New("mount does not exist")

	// ErrMountNameConflict is used when a mount is attempted
	// to be created but there is already a mount with the name
	// used for creation.
	ErrMountNameConflict = errors.New("mount already exists with name")

	// ErrActiveMount is used when an operation on a
	// mount is attempted but the layer is still
	// mounted and the operation cannot be performed.
	ErrActiveMount = errors.New("mount still active")

	// ErrNotMounted is used when requesting an active
	// mount but the layer is not mounted.
	ErrNotMounted = errors.New("not mounted")

	// ErrMaxDepthExceeded is used when a layer is attempted
	// to be created which would result in a layer depth
	// greater than the 125 max.
	ErrMaxDepthExceeded = errors.New("max depth exceeded")

	// ErrNotSupported is used when the action is not supported
	// on the current host operating system.
	ErrNotSupported = errors.New("not support on this host operating system")
)

// ChainID is the content-addressable ID of a layer.
type ChainID digest.Digest

// String returns a string rendition of a layer ID
func (id ChainID) String() string {
	return string(id)
}

// DiffID is the hash of an individual layer tar.
type DiffID digest.Digest

// String returns a string rendition of a layer DiffID
func (diffID DiffID) String() string {
	return string(diffID)
}

// TarStreamer represents an object which may
// have its contents exported as a tar stream.
type TarStreamer interface {
	// TarStream returns a tar archive stream
	// for the contents of a layer.
	TarStream() (io.ReadCloser, error)
}

// Layer represents a read-only layer
type Layer interface {
	TarStreamer

	// TarStreamFrom returns a tar archive stream for all the layer chain with
	// arbitrary depth.
	TarStreamFrom(ChainID) (io.ReadCloser, error)

	// ChainID returns the content hash of the entire layer chain. The hash
	// chain is made up of DiffID of top layer and all of its parents.
	ChainID() ChainID

	// DiffID returns the content hash of the layer
	// tar stream used to create this layer.
	DiffID() DiffID

	// Parent returns the next layer in the layer chain.
	Parent() Layer

	// Size returns the size of the entire layer chain. The size
	// is calculated from the total size of all files in the layers.
	Size() (int64, error)

	// DiffSize returns the size difference of the top layer
	// from parent layer.
	DiffSize() (int64, error)

	// Metadata returns the low level storage metadata associated
	// with layer.
	Metadata() (map[string]string, error)
}

// RWLayer represents a layer which is
// read and writable
type RWLayer interface {
	TarStreamer

	// Name of mounted layer
	Name() string

	// Parent returns the layer which the writable
	// layer was created from.
	Parent() Layer

	// Mount mounts the RWLayer and returns the filesystem path
	// to the writable layer.
	Mount(mountLabel string) (containerfs.ContainerFS, error)

	// Unmount unmounts the RWLayer. This should be called
	// for every mount. If there are multiple mount calls
	// this operation will only decrement the internal mount counter.
	Unmount() error

	// Size represents the size of the writable layer
	// as calculated by the total size of the files
	// changed in the mutable layer.
	Size() (int64, error)

	// Changes returns the set of changes for the mutable layer
	// from the base layer.
	Changes() ([]archive.Change, error)

	// Metadata returns the low level metadata for the mutable layer
	Metadata() (map[string]string, error)

	// ApplyDiff applies the diff to the RW layer
	ApplyDiff(diff io.Reader) (int64, error)
}

// Metadata holds information about a
// read-only layer
type Metadata struct {
	// ChainID is the content hash of the layer
	ChainID ChainID

	// DiffID is the hash of the tar data used to
	// create the layer
	DiffID DiffID

	// Size is the size of the layer and all parents
	Size int64

	// DiffSize is the size of the top layer
	DiffSize int64
}

// MountInit is a function to initialize a
// writable mount. Changes made here will
// not be included in the Tar stream of the
// RWLayer.
type MountInit func(root containerfs.ContainerFS) error

// CreateRWLayerOpts contains optional arguments to be passed to CreateRWLayer
type CreateRWLayerOpts struct {
	MountLabel string
	InitFunc   MountInit
	StorageOpt map[string]string
}

// Store represents a backend for managing both
// read-only and read-write layers.
type Store interface {
	Register(io.Reader, ChainID) (Layer, error)
	Get(ChainID) (Layer, error)
	Map() map[ChainID]Layer
	Release(Layer) ([]Metadata, error)

	CreateRWLayer(id string, parent ChainID, opts *CreateRWLayerOpts) (RWLayer, error)
	GetRWLayer(id string) (RWLayer, error)
	GetMountID(id string) (string, error)
	ReleaseRWLayer(RWLayer) ([]Metadata, error)

	Cleanup() error
	DriverStatus() [][2]string
	DriverName() string
}

// DescribableStore represents a layer store capable of storing
// descriptors for layers.
type DescribableStore interface {
	RegisterWithDescriptor(io.Reader, ChainID, distribution.Descriptor) (Layer, error)
}

// CreateChainID returns ID for a layerDigest slice
func CreateChainID(dgsts []DiffID) ChainID {
	return createChainIDFromParent("", dgsts...)
}

func createChainIDFromParent(parent ChainID, dgsts ...DiffID) ChainID {
	if len(dgsts) == 0 {
		return parent
	}
	if parent == "" {
		return createChainIDFromParent(ChainID(dgsts[0]), dgsts[1:]...)
	}
	// H = "H(n-1) SHA256(n)"
	dgst := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0])))
	return createChainIDFromParent(ChainID(dgst), dgsts[1:]...)
}

// ReleaseAndLog releases the provided layer from the given layer
// store, logging any error and release metadata
func ReleaseAndLog(ls Store, l Layer) {
	metadata, err := ls.Release(l)
	if err != nil {
		logrus.Errorf("Error releasing layer %s: %v", l.ChainID(), err)
	}
	LogReleaseMetadata(metadata)
}

// LogReleaseMetadata logs a metadata array, uses this to
// ensure consistent logging for release metadata
func LogReleaseMetadata(metadatas []Metadata) {
	for _, metadata := range metadatas {
		logrus.Infof("Layer %s cleaned up", metadata.ChainID)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The Ebiten Authors
//
// 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.

// +build example

package twenty48

import (
	"errors"
	"image/color"
	"math/rand"
	"sort"
	"strconv"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/examples/common"
)

type TileData struct {
	value int
	x     int
	y     int
}

type Tile struct {
	current           TileData
	next              TileData
	movingCount       int
	startPoppingCount int
	poppingCount      int
}

func NewTile(value int, x, y int) *Tile {
	return &Tile{
		current: TileData{
			value: value,
			x:     x,
			y:     y,
		},
		startPoppingCount: maxPoppingCount,
	}
}

func (t *Tile) Value() int {
	return t.current.value
}

func (t *Tile) Pos() (int, int) {
	return t.current.x, t.current.y
}

func (t *Tile) NextValue() int {
	return t.next.value
}

func (t *Tile) NextPos() (int, int) {
	return t.next.x, t.next.y
}

func (t *Tile) IsMoving() bool {
	return 0 < t.movingCount
}

func (t *Tile) stopAnimation() {
	if 0 < t.movingCount {
		t.current = t.next
		t.next = TileData{}
	}
	t.movingCount = 0
	t.startPoppingCount = 0
	t.poppingCount = 0
}

func tileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
	var result *Tile
	for t := range tiles {
		if t.current.x != x || t.current.y != y {
			continue
		}
		if result != nil {
			panic("not reach")
		}
		result = t
	}
	return result
}

func nextTileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
	var result *Tile
	for t := range tiles {
		if 0 < t.movingCount {
			if t.next.x != x || t.next.y != y || t.next.value == 0 {
				continue
			}
		} else {
			if t.current.x != x || t.current.y != y {
				continue
			}
		}
		if result != nil {
			panic("not reach")
		}
		result = t
	}
	return result
}

const (
	maxMovingCount  = 5
	maxPoppingCount = 6
)

func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) bool {
	vx, vy := dir.Vector()
	tx := []int{}
	ty := []int{}
	for i := 0; i < size; i++ {
		tx = append(tx, i)
		ty = append(ty, i)
	}
	if vx > 0 {
		sort.Sort(sort.Reverse(sort.IntSlice(tx)))
	}
	if vy > 0 {
		sort.Sort(sort.Reverse(sort.IntSlice(ty)))
	}

	moved := false
	for _, j := range ty {
		for _, i := range tx {
			t := tileAt(tiles, i, j)
			if t == nil {
				continue
			}
			if t.next != (TileData{}) {
				panic("not reach")
			}
			if t.IsMoving() {
				panic("not reach")
			}
			ii := i
			jj := j
			for {
				ni := ii + vx
				nj := jj + vy
				if ni < 0 || ni >= size || nj < 0 || nj >= size {
					break
				}
				tt := nextTileAt(tiles, ni, nj)
				if tt == nil {
					ii = ni
					jj = nj
					moved = true
					continue
				}
				if t.current.value != tt.current.value {
					break
				}
				if 0 < tt.movingCount && tt.current.value != tt.next.value {
					// already merged
					break
				}
				ii = ni
				jj = nj
				moved = true
				break
			}
			next := TileData{}
			next.value = t.current.value
			if tt := nextTileAt(tiles, ii, jj); tt != t && tt != nil {
				next.value = t.current.value + tt.current.value
				tt.next.value = 0
				tt.next.x = ii
				tt.next.y = jj
				tt.movingCount = maxMovingCount
			}
			next.x = ii
			next.y = jj
			if t.current != next {
				t.next = next
				t.movingCount = maxMovingCount
			}
		}
	}
	if !moved {
		for t := range tiles {
			t.next = TileData{}
			t.movingCount = 0
		}
	}
	return moved
}

func addRandomTile(tiles map[*Tile]struct{}, size int) error {
	cells := make([]bool, size*size)
	for t := range tiles {
		if t.IsMoving() {
			panic("not reach")
		}
		i := t.current.x + t.current.y*size
		cells[i] = true
	}
	availableCells := []int{}
	for i, b := range cells {
		if b {
			continue
		}
		availableCells = append(availableCells, i)
	}
	if len(availableCells) == 0 {
		return errors.New("twenty48: there is no space to add a new tile")
	}
	c := availableCells[rand.Intn(len(availableCells))]
	v := 2
	if rand.Intn(10) == 0 {
		v = 4
	}
	x := c % size
	y := c / size
	t := NewTile(v, x, y)
	tiles[t] = struct{}{}
	return nil
}

func (t *Tile) Update() error {
	switch {
	case 0 < t.movingCount:
		t.movingCount--
		if t.movingCount == 0 {
			if t.current.value != t.next.value && 0 < t.next.value {
				t.poppingCount = maxPoppingCount
			}
			t.current = t.next
			t.next = TileData{}
		}
	case 0 < t.startPoppingCount:
		t.startPoppingCount--
	case 0 < t.poppingCount:
		t.poppingCount--
	}
	return nil
}

func colorToScale(clr color.Color) (float64, float64, float64, float64) {
	r, g, b, a := clr.RGBA()
	rf := float64(r) / 0xffff
	gf := float64(g) / 0xffff
	bf := float64(b) / 0xffff
	af := float64(a) / 0xffff
	// Convert to non-premultiplied alpha components.
	if 0 < af {
		rf /= af
		gf /= af
		bf /= af
	}
	return rf, gf, bf, af
}

func mean(a, b int, rate float64) int {
	return int(float64(a)*(1-rate) + float64(b)*rate)
}

func meanF(a, b float64, rate float64) float64 {
	return a*(1-rate) + b*rate
}

const (
	tileSize   = 40
	tileMargin = 2
)

var (
	tileImage *ebiten.Image
)

func init() {
	tileImage, _ = ebiten.NewImage(tileSize, tileSize, ebiten.FilterNearest)
	tileImage.Fill(color.White)
}

func (t *Tile) Draw(boardImage *ebiten.Image) {
	i, j := t.current.x, t.current.y
	ni, nj := t.next.x, t.next.y
	v := t.current.value
	if v == 0 {
		return
	}
	op := &ebiten.DrawImageOptions{}
	x := i*tileSize + (i+1)*tileMargin
	y := j*tileSize + (j+1)*tileMargin
	nx := ni*tileSize + (ni+1)*tileMargin
	ny := nj*tileSize + (nj+1)*tileMargin
	switch {
	case 0 < t.movingCount:
		rate := 1 - float64(t.movingCount)/maxMovingCount
		x = mean(x, nx, rate)
		y = mean(y, ny, rate)
	case 0 < t.startPoppingCount:
		rate := 1 - float64(t.startPoppingCount)/float64(maxPoppingCount)
		scale := meanF(0.0, 1.0, rate)
		op.GeoM.Translate(float64(-tileSize/2), float64(-tileSize/2))
		op.GeoM.Scale(scale, scale)
		op.GeoM.Translate(float64(tileSize/2), float64(tileSize/2))
	case 0 < t.poppingCount:
		const maxScale = 1.2
		rate := 0.0
		if maxPoppingCount*2/3 <= t.poppingCount {
			// 0 to 1
			rate = 1 - float64(t.poppingCount-2*maxPoppingCount/3)/float64(maxPoppingCount/3)
		} else {
			// 1 to 0
			rate = float64(t.poppingCount) / float64(maxPoppingCount*2/3)
		}
		scale := meanF(1.0, maxScale, rate)
		op.GeoM.Translate(float64(-tileSize/2), float64(-tileSize/2))
		op.GeoM.Scale(scale, scale)
		op.GeoM.Translate(float64(tileSize/2), float64(tileSize/2))
	}
	op.GeoM.Translate(float64(x), float64(y))
	r, g, b, a := colorToScale(tileBackgroundColor(v))
	op.ColorM.Scale(r, g, b, a)
	boardImage.DrawImage(tileImage, op)
	str := strconv.Itoa(v)
	scale := 2
	if 2 < len(str) {
		scale = 1
	}
	w := common.ArcadeFont.TextWidth(str) * scale
	h := common.ArcadeFont.TextHeight(str) * scale
	x = x + (tileSize-w)/2
	y = y + (tileSize-h)/2
	common.ArcadeFont.DrawText(boardImage, str, x, y, scale, tileColor(v))
}
<commit_msg>examples/2048: Add comments<commit_after>// Copyright 2016 The Ebiten Authors
//
// 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.

// +build example

package twenty48

import (
	"errors"
	"fmt"
	"image/color"
	"math/rand"
	"sort"
	"strconv"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/examples/common"
)

// TileData represents a tile information like a value and a position.
type TileData struct {
	value int
	x     int
	y     int
}

// Tile represents a tile infomation including TileData and animation states.
type Tile struct {
	current           TileData
	next              TileData
	movingCount       int
	startPoppingCount int
	poppingCount      int
}

// NewTile creates a new Tile object.
func NewTile(value int, x, y int) *Tile {
	return &Tile{
		current: TileData{
			value: value,
			x:     x,
			y:     y,
		},
		startPoppingCount: maxPoppingCount,
	}
}

// Value returns the current value.
func (t *Tile) Value() int {
	return t.current.value
}

// Pos returns the current position.
func (t *Tile) Pos() (int, int) {
	return t.current.x, t.current.y
}

// NextValue returns the next value.
func (t *Tile) NextValue() int {
	return t.next.value
}

// NextPos returns the next position.
func (t *Tile) NextPos() (int, int) {
	return t.next.x, t.next.y
}

// IsMoving returns a boolean value indicating if the tile is animating.
func (t *Tile) IsMoving() bool {
	return 0 < t.movingCount
}

func (t *Tile) stopAnimation() {
	if 0 < t.movingCount {
		t.current = t.next
		t.next = TileData{}
	}
	t.movingCount = 0
	t.startPoppingCount = 0
	t.poppingCount = 0
}

func tileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
	var result *Tile
	for t := range tiles {
		if t.current.x != x || t.current.y != y {
			continue
		}
		if result != nil {
			panic("not reach")
		}
		result = t
	}
	return result
}

func nextTileAt(tiles map[*Tile]struct{}, x, y int) *Tile {
	var result *Tile
	for t := range tiles {
		if 0 < t.movingCount {
			if t.next.x != x || t.next.y != y || t.next.value == 0 {
				continue
			}
		} else {
			if t.current.x != x || t.current.y != y {
				continue
			}
		}
		if result != nil {
			panic("not reach")
		}
		result = t
	}
	return result
}

const (
	maxMovingCount  = 5
	maxPoppingCount = 6
)

// MoveTiles moves tiles in the given tiles map if possible.
// MoveTiles returns true if there are movable tiles, otherwise false.
func MoveTiles(tiles map[*Tile]struct{}, size int, dir Dir) bool {
	vx, vy := dir.Vector()
	tx := []int{}
	ty := []int{}
	for i := 0; i < size; i++ {
		tx = append(tx, i)
		ty = append(ty, i)
	}
	if vx > 0 {
		sort.Sort(sort.Reverse(sort.IntSlice(tx)))
	}
	if vy > 0 {
		sort.Sort(sort.Reverse(sort.IntSlice(ty)))
	}

	moved := false
	for _, j := range ty {
		for _, i := range tx {
			t := tileAt(tiles, i, j)
			if t == nil {
				continue
			}
			if t.next != (TileData{}) {
				panic("not reach")
			}
			if t.IsMoving() {
				panic("not reach")
			}
			ii := i
			jj := j
			for {
				ni := ii + vx
				nj := jj + vy
				if ni < 0 || ni >= size || nj < 0 || nj >= size {
					break
				}
				tt := nextTileAt(tiles, ni, nj)
				if tt == nil {
					ii = ni
					jj = nj
					moved = true
					continue
				}
				if t.current.value != tt.current.value {
					break
				}
				if 0 < tt.movingCount && tt.current.value != tt.next.value {
					// already merged
					break
				}
				ii = ni
				jj = nj
				moved = true
				break
			}
			next := TileData{}
			next.value = t.current.value
			if tt := nextTileAt(tiles, ii, jj); tt != t && tt != nil {
				next.value = t.current.value + tt.current.value
				tt.next.value = 0
				tt.next.x = ii
				tt.next.y = jj
				tt.movingCount = maxMovingCount
			}
			next.x = ii
			next.y = jj
			if t.current != next {
				t.next = next
				t.movingCount = maxMovingCount
			}
		}
	}
	if !moved {
		for t := range tiles {
			t.next = TileData{}
			t.movingCount = 0
		}
	}
	return moved
}

func addRandomTile(tiles map[*Tile]struct{}, size int) error {
	cells := make([]bool, size*size)
	for t := range tiles {
		if t.IsMoving() {
			panic("not reach")
		}
		i := t.current.x + t.current.y*size
		cells[i] = true
	}
	availableCells := []int{}
	for i, b := range cells {
		if b {
			continue
		}
		availableCells = append(availableCells, i)
	}
	if len(availableCells) == 0 {
		return errors.New("twenty48: there is no space to add a new tile")
	}
	c := availableCells[rand.Intn(len(availableCells))]
	v := 2
	if rand.Intn(10) == 0 {
		v = 4
	}
	x := c % size
	y := c / size
	t := NewTile(v, x, y)
	tiles[t] = struct{}{}
	return nil
}

func (t *Tile) Update() error {
	switch {
	case 0 < t.movingCount:
		t.movingCount--
		if t.movingCount == 0 {
			if t.current.value != t.next.value && 0 < t.next.value {
				t.poppingCount = maxPoppingCount
			}
			t.current = t.next
			t.next = TileData{}
		}
	case 0 < t.startPoppingCount:
		t.startPoppingCount--
	case 0 < t.poppingCount:
		t.poppingCount--
	}
	return nil
}

func colorToScale(clr color.Color) (float64, float64, float64, float64) {
	r, g, b, a := clr.RGBA()
	rf := float64(r) / 0xffff
	gf := float64(g) / 0xffff
	bf := float64(b) / 0xffff
	af := float64(a) / 0xffff
	// Convert to non-premultiplied alpha components.
	if 0 < af {
		rf /= af
		gf /= af
		bf /= af
	}
	return rf, gf, bf, af
}

func mean(a, b int, rate float64) int {
	return int(float64(a)*(1-rate) + float64(b)*rate)
}

func meanF(a, b float64, rate float64) float64 {
	return a*(1-rate) + b*rate
}

const (
	tileSize   = 40
	tileMargin = 2
)

var (
	tileImage *ebiten.Image
)

func init() {
	tileImage, _ = ebiten.NewImage(tileSize, tileSize, ebiten.FilterNearest)
	tileImage.Fill(color.White)
}

// Draw draws the current tile to the given boardImage.
func (t *Tile) Draw(boardImage *ebiten.Image) {
	i, j := t.current.x, t.current.y
	ni, nj := t.next.x, t.next.y
	v := t.current.value
	if v == 0 {
		return
	}
	op := &ebiten.DrawImageOptions{}
	x := i*tileSize + (i+1)*tileMargin
	y := j*tileSize + (j+1)*tileMargin
	nx := ni*tileSize + (ni+1)*tileMargin
	ny := nj*tileSize + (nj+1)*tileMargin
	switch {
	case 0 < t.movingCount:
		rate := 1 - float64(t.movingCount)/maxMovingCount
		x = mean(x, nx, rate)
		y = mean(y, ny, rate)
	case 0 < t.startPoppingCount:
		rate := 1 - float64(t.startPoppingCount)/float64(maxPoppingCount)
		scale := meanF(0.0, 1.0, rate)
		op.GeoM.Translate(float64(-tileSize/2), float64(-tileSize/2))
		op.GeoM.Scale(scale, scale)
		op.GeoM.Translate(float64(tileSize/2), float64(tileSize/2))
	case 0 < t.poppingCount:
		const maxScale = 1.2
		rate := 0.0
		if maxPoppingCount*2/3 <= t.poppingCount {
			// 0 to 1
			rate = 1 - float64(t.poppingCount-2*maxPoppingCount/3)/float64(maxPoppingCount/3)
		} else {
			// 1 to 0
			rate = float64(t.poppingCount) / float64(maxPoppingCount*2/3)
		}
		scale := meanF(1.0, maxScale, rate)
		op.GeoM.Translate(float64(-tileSize/2), float64(-tileSize/2))
		op.GeoM.Scale(scale, scale)
		op.GeoM.Translate(float64(tileSize/2), float64(tileSize/2))
	}
	op.GeoM.Translate(float64(x), float64(y))
	r, g, b, a := colorToScale(tileBackgroundColor(v))
	op.ColorM.Scale(r, g, b, a)
	boardImage.DrawImage(tileImage, op)
	str := strconv.Itoa(v)
	scale := 2
	if 2 < len(str) {
		scale = 1
	}
	w := common.ArcadeFont.TextWidth(str) * scale
	h := common.ArcadeFont.TextHeight(str) * scale
	x = x + (tileSize-w)/2
	y = y + (tileSize-h)/2
	common.ArcadeFont.DrawText(boardImage, str, x, y, scale, tileColor(v))
}
<|endoftext|>