<commit_before>package echo

import (
	"bytes"
	"encoding/xml"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"time"
	"unicode"

	"github.com/webx-top/echo/encoding/json"
	"github.com/webx-top/echo/engine"
)

// Response returns *Response.
func (c *xContext) Response() engine.Response {
	return c.response
}

// Render renders a template with data and sends a text/html response with status
// code. Templates can be registered using `Echo.SetRenderer()`.
func (c *xContext) Render(name string, data interface{}, codes ...int) (err error) {
	if c.auto {
		format := c.Format()
		if render, ok := c.echo.formatRenderers[format]; ok && render != nil {
			switch v := data.(type) {
			case Data: //Skip
			case error:
				c.dataEngine.SetError(v)
			case nil:
				if c.dataEngine.GetData() == nil {
					c.dataEngine.SetData(c.Stored(), c.dataEngine.GetCode().Int())
				}
			default:
				c.dataEngine.SetData(data, c.dataEngine.GetCode().Int())
			}
			return render(c, data)
		}
	}
	c.dataEngine.SetTmplFuncs()
	if data == nil {
		data = c.dataEngine.GetData()
	}
	b, err := c.Fetch(name, data)
	if err != nil {
		return
	}
	b = bytes.TrimLeftFunc(b, unicode.IsSpace)
	c.response.Header().Set(HeaderContentType, MIMETextHTMLCharsetUTF8)
	err = c.Blob(b, codes...)
	return
}

// HTML sends an HTTP response with status code.
func (c *xContext) HTML(html string, codes ...int) (err error) {
	c.response.Header().Set(HeaderContentType, MIMETextHTMLCharsetUTF8)
	err = c.Blob([]byte(html), codes...)
	return
}

// String sends a string response with status code.
func (c *xContext) String(s string, codes ...int) (err error) {
	c.response.Header().Set(HeaderContentType, MIMETextPlainCharsetUTF8)
	err = c.Blob([]byte(s), codes...)
	return
}

func (c *xContext) Blob(b []byte, codes ...int) (err error) {
	if len(codes) > 0 {
		c.code = codes[0]
	}
	if c.code == 0 {
		c.code = http.StatusOK
	}
	err = c.preResponse()
	if err != nil {
		return
	}
	c.response.WriteHeader(c.code)
	_, err = c.response.Write(b)
	return
}

// JSON sends a JSON response with status code.
func (c *xContext) JSON(i interface{}, codes ...int) (err error) {
	var b []byte
	if c.echo.Debug() {
		b, err = json.MarshalIndent(i, "", "  ")
	} else {
		b, err = json.Marshal(i)
	}
	if err != nil {
		return err
	}
	return c.JSONBlob(b, codes...)
}

// JSONBlob sends a JSON blob response with status code.
func (c *xContext) JSONBlob(b []byte, codes ...int) (err error) {
	c.response.Header().Set(HeaderContentType, MIMEApplicationJSONCharsetUTF8)
	err = c.Blob(b, codes...)
	return
}

// JSONP sends a JSONP response with status code. It uses `callback` to construct
// the JSONP payload.
func (c *xContext) JSONP(callback string, i interface{}, codes ...int) (err error) {
	b, err := json.Marshal(i)
	if err != nil {
		return err
	}
	c.response.Header().Set(HeaderContentType, MIMEApplicationJavaScriptCharsetUTF8)
	b = []byte(callback + "(" + string(b) + ");")
	err = c.Blob(b, codes...)
	return
}

// XML sends an XML response with status code.
func (c *xContext) XML(i interface{}, codes ...int) (err error) {
	var b []byte
	if c.echo.Debug() {
		b, err = xml.MarshalIndent(i, "", "  ")
	} else {
		b, err = xml.Marshal(i)
	}
	if err != nil {
		return err
	}
	return c.XMLBlob(b, codes...)
}

// XMLBlob sends a XML blob response with status code.
func (c *xContext) XMLBlob(b []byte, codes ...int) (err error) {
	c.response.Header().Set(HeaderContentType, MIMEApplicationXMLCharsetUTF8)
	b = []byte(xml.Header + string(b))
	err = c.Blob(b, codes...)
	return
}

func (c *xContext) Stream(step func(w io.Writer) bool) {
	c.response.Stream(step)
}

func (c *xContext) SSEvent(event string, data chan interface{}) (err error) {
	hdr := c.response.Header()
	hdr.Set(HeaderContentType, MIMEEventStream)
	hdr.Set(`Cache-Control`, `no-cache`)
	hdr.Set(`Connection`, `keep-alive`)
	c.Stream(func(w io.Writer) bool {
		b, e := c.Fetch(event, <-data)
		if e != nil {
			err = e
			return false
		}
		_, e = w.Write(b)
		if e != nil {
			err = e
			return false
		}
		return true
	})
	return
}

func (c *xContext) Attachment(r io.ReadSeeker, name string) (err error) {
	c.response.Header().Set(HeaderContentType, ContentTypeByExtension(name))
	c.response.Header().Set(HeaderContentDisposition, "attachment; filename="+name)
	c.response.WriteHeader(http.StatusOK)
	c.response.KeepBody(false)
	_, err = io.Copy(c.response, r)
	return
}

func (c *xContext) File(file string, fs ...http.FileSystem) (err error) {
	var f http.File
	customFS := len(fs) > 0 && fs[0] != nil
	if customFS {
		f, err = fs[0].Open(file)
	} else {
		f, err = os.Open(file)
	}
	if err != nil {
		return ErrNotFound
	}
	defer f.Close()

	fi, _ := f.Stat()
	if fi.IsDir() {
		file = filepath.Join(file, "index.html")
		if customFS {
			f, err = fs[0].Open(file)
		} else {
			f, err = os.Open(file)
		}
		if err != nil {
			return ErrNotFound
		}
		fi, _ = f.Stat()
	}
	return c.ServeContent(f, fi.Name(), fi.ModTime())
}

func (c *xContext) ServeContent(content io.ReadSeeker, name string, modtime time.Time) error {
	rq := c.Request()
	rs := c.Response()

	if t, err := time.Parse(http.TimeFormat, rq.Header().Get(HeaderIfModifiedSince)); err == nil && modtime.Before(t.Add(1*time.Second)) {
		rs.Header().Del(HeaderContentType)
		rs.Header().Del(HeaderContentLength)
		return c.NoContent(http.StatusNotModified)
	}

	rs.Header().Set(HeaderContentType, ContentTypeByExtension(name))
	rs.Header().Set(HeaderLastModified, modtime.UTC().Format(http.TimeFormat))
	rs.WriteHeader(http.StatusOK)
	rs.KeepBody(false)
	_, err := io.Copy(rs, content)
	return err
}

// NoContent sends a response with no body and a status code.
func (c *xContext) NoContent(codes ...int) error {
	if len(codes) > 0 {
		c.code = codes[0]
	}
	if c.code == 0 {
		c.code = http.StatusOK
	}
	c.response.WriteHeader(c.code)
	return nil
}

// Redirect redirects the request with status code.
func (c *xContext) Redirect(url string, codes ...int) error {
	code := http.StatusFound
	if len(codes) > 0 {
		code = codes[0]
	}
	if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect {
		return ErrInvalidRedirectCode
	}
	err := c.preResponse()
	if err != nil {
		return err
	}
	format := c.Format()
	if format != `html` && c.auto {
		if render, ok := c.echo.formatRenderers[format]; ok && render != nil {
			if c.dataEngine.GetData() == nil {
				c.Set(`Location`, url)
				c.dataEngine.SetData(c.Stored(), c.dataEngine.GetCode().Int())
			} else {
				c.dataEngine.SetURL(url)
			}
			return render(c, c.dataEngine.GetData())
		}
	}
	c.response.Redirect(url, code)
	return nil
}
<commit_msg>improved<commit_after>package echo

import (
	"bytes"
	"encoding/xml"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"time"
	"unicode"

	"github.com/webx-top/echo/encoding/json"
	"github.com/webx-top/echo/engine"
)

// Response returns *Response.
func (c *xContext) Response() engine.Response {
	return c.response
}

// Render renders a template with data and sends a text/html response with status
// code. Templates can be registered using `Echo.SetRenderer()`.
func (c *xContext) Render(name string, data interface{}, codes ...int) (err error) {
	if c.auto {
		format := c.Format()
		if render, ok := c.echo.formatRenderers[format]; ok && render != nil {
			switch v := data.(type) {
			case Data: //Skip
			case error:
				c.dataEngine.SetError(v)
			case nil:
				if c.dataEngine.GetData() == nil {
					c.dataEngine.SetData(c.Stored(), c.dataEngine.GetCode().Int())
				}
			default:
				c.dataEngine.SetData(data, c.dataEngine.GetCode().Int())
			}
			return render(c, data)
		}
	}
	c.dataEngine.SetTmplFuncs()
	if data == nil {
		data = c.dataEngine.GetData()
	}
	b, err := c.Fetch(name, data)
	if err != nil {
		return
	}
	b = bytes.TrimLeftFunc(b, unicode.IsSpace)
	c.response.Header().Set(HeaderContentType, MIMETextHTMLCharsetUTF8)
	err = c.Blob(b, codes...)
	return
}

// HTML sends an HTTP response with status code.
func (c *xContext) HTML(html string, codes ...int) (err error) {
	c.response.Header().Set(HeaderContentType, MIMETextHTMLCharsetUTF8)
	err = c.Blob([]byte(html), codes...)
	return
}

// String sends a string response with status code.
func (c *xContext) String(s string, codes ...int) (err error) {
	c.response.Header().Set(HeaderContentType, MIMETextPlainCharsetUTF8)
	err = c.Blob([]byte(s), codes...)
	return
}

func (c *xContext) Blob(b []byte, codes ...int) (err error) {
	if len(codes) > 0 {
		c.code = codes[0]
	}
	if c.code == 0 {
		c.code = http.StatusOK
	}
	err = c.preResponse()
	if err != nil {
		return
	}
	c.response.WriteHeader(c.code)
	_, err = c.response.Write(b)
	return
}

// JSON sends a JSON response with status code.
func (c *xContext) JSON(i interface{}, codes ...int) (err error) {
	var b []byte
	if c.echo.Debug() {
		b, err = json.MarshalIndent(i, "", "  ")
	} else {
		b, err = json.Marshal(i)
	}
	if err != nil {
		return err
	}
	return c.JSONBlob(b, codes...)
}

// JSONBlob sends a JSON blob response with status code.
func (c *xContext) JSONBlob(b []byte, codes ...int) (err error) {
	c.response.Header().Set(HeaderContentType, MIMEApplicationJSONCharsetUTF8)
	err = c.Blob(b, codes...)
	return
}

// JSONP sends a JSONP response with status code. It uses `callback` to construct
// the JSONP payload.
func (c *xContext) JSONP(callback string, i interface{}, codes ...int) (err error) {
	b, err := json.Marshal(i)
	if err != nil {
		return err
	}
	c.response.Header().Set(HeaderContentType, MIMEApplicationJavaScriptCharsetUTF8)
	b = []byte(callback + "(" + string(b) + ");")
	err = c.Blob(b, codes...)
	return
}

// XML sends an XML response with status code.
func (c *xContext) XML(i interface{}, codes ...int) (err error) {
	var b []byte
	if c.echo.Debug() {
		b, err = xml.MarshalIndent(i, "", "  ")
	} else {
		b, err = xml.Marshal(i)
	}
	if err != nil {
		return err
	}
	return c.XMLBlob(b, codes...)
}

// XMLBlob sends a XML blob response with status code.
func (c *xContext) XMLBlob(b []byte, codes ...int) (err error) {
	c.response.Header().Set(HeaderContentType, MIMEApplicationXMLCharsetUTF8)
	b = []byte(xml.Header + string(b))
	err = c.Blob(b, codes...)
	return
}

func (c *xContext) Stream(step func(w io.Writer) bool) {
	c.response.Stream(step)
}

func (c *xContext) SSEvent(event string, data chan interface{}) (err error) {
	hdr := c.response.Header()
	hdr.Set(HeaderContentType, MIMEEventStream)
	hdr.Set(`Cache-Control`, `no-cache`)
	hdr.Set(`Connection`, `keep-alive`)
	c.Stream(func(w io.Writer) bool {
		b, e := c.Fetch(event, <-data)
		if e != nil {
			err = e
			return false
		}
		_, e = w.Write(b)
		if e != nil {
			err = e
			return false
		}
		return true
	})
	return
}

func (c *xContext) Attachment(r io.ReadSeeker, name string) (err error) {
	c.response.Header().Set(HeaderContentType, ContentTypeByExtension(name))
	c.response.Header().Set(HeaderContentDisposition, "attachment; filename="+name)
	c.response.WriteHeader(http.StatusOK)
	c.response.KeepBody(false)
	_, err = io.Copy(c.response, r)
	return
}

func (c *xContext) File(file string, fs ...http.FileSystem) (err error) {
	var f http.File
	customFS := len(fs) > 0 && fs[0] != nil
	if customFS {
		f, err = fs[0].Open(file)
	} else {
		f, err = os.Open(file)
	}
	if err != nil {
		return ErrNotFound
	}
	defer f.Close()

	fi, _ := f.Stat()
	if fi.IsDir() {
		file = filepath.Join(file, "index.html")
		if customFS {
			f, err = fs[0].Open(file)
		} else {
			f, err = os.Open(file)
		}
		if err != nil {
			return ErrNotFound
		}
		fi, _ = f.Stat()
	}
	return c.ServeContent(f, fi.Name(), fi.ModTime())
}

func (c *xContext) ServeContent(content io.ReadSeeker, name string, modtime time.Time) error {
	rq := c.Request()
	rs := c.Response()

	if t, err := time.Parse(http.TimeFormat, rq.Header().Get(HeaderIfModifiedSince)); err == nil && modtime.Before(t.Add(1*time.Second)) {
		rs.Header().Del(HeaderContentType)
		rs.Header().Del(HeaderContentLength)
		return c.NoContent(http.StatusNotModified)
	}

	rs.Header().Set(HeaderContentType, ContentTypeByExtension(name))
	rs.Header().Set(HeaderLastModified, modtime.UTC().Format(http.TimeFormat))
	rs.WriteHeader(http.StatusOK)
	rs.KeepBody(false)
	_, err := io.Copy(rs, content)
	return err
}

// NoContent sends a response with no body and a status code.
func (c *xContext) NoContent(codes ...int) error {
	if len(codes) > 0 {
		c.code = codes[0]
	}
	if c.code == 0 {
		c.code = http.StatusOK
	}
	c.response.WriteHeader(c.code)
	return nil
}

// Redirect redirects the request with status code.
func (c *xContext) Redirect(url string, codes ...int) error {
	code := http.StatusFound
	if len(codes) > 0 {
		code = codes[0]
	}
	if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect {
		return ErrInvalidRedirectCode
	}
	err := c.preResponse()
	if err != nil {
		return err
	}
	format := c.Format()
	if format != `html` && c.auto {
		if render, ok := c.echo.formatRenderers[format]; ok && render != nil {
			if c.dataEngine.GetData() == nil {
				c.dataEngine.SetData(c.Stored(), c.dataEngine.GetCode().Int())
			}
			c.dataEngine.SetURL(url)
			return render(c, c.dataEngine.GetData())
		}
	}
	c.response.Redirect(url, code)
	return nil
}
<|endoftext|>


<commit_before>// Package goprove contains lib for checking the Golang best practi
package goprove

import (
	"go/format"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/golang/lint"
	"github.com/ryanuber/go-license"

	"github.com/karolgorecki/goprove/util"
)

func projectBuilds() bool {
	_, err := exec.Command("go", "build", sourcePath).Output()
	return err == nil
}

func isFormatted() bool {
	errors := 0
	filepath.Walk(sourcePath, func(path string, f os.FileInfo, err error) error {
		if !strings.HasSuffix(filepath.Ext(path), ".go") {
			return nil
		}

		file, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		fmtFile, _ := format.Source(file)

		if string(file) != string(fmtFile) {
			errors++
		}
		return nil
	})
	return errors == 0
}

func testPassing() bool {
	output, _ := exec.Command("go", "test", sourcePath+"/...").Output()
	return strings.Index(string(output), `--- FAIL`) == -1
}

func hasLicense() bool {
	if _, err := license.NewFromDir(sourcePath); err != nil {
		return false
	}
	return true
}

func hasReadme() bool {
	return util.FilesExistAny(sourcePath, "readme")
}

func hasContributing() bool {
	return util.FilesExistAny(sourcePath, "contribution", "contribute", "contributing")
}

func isLinted() bool {
	errors := 0
	l := new(lint.Linter)

	filepath.Walk(sourcePath+"/...", func(path string, f os.FileInfo, err error) error {

		if !strings.HasSuffix(filepath.Ext(path), ".go") {
			return nil
		}

		file, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		if lnt, _ := l.Lint(f.Name(), file); len(lnt) > 0 {
			if lnt[0].Confidence > 0.2 {
				errors++
				return nil
			}
		}
		return nil
	})

	return errors == 0
}

func isVetted() bool {
	_, err := exec.Command("go", "vet", sourceGoPath).Output()
	return err == nil
}

func isDirMatch() bool {
	ok := true

	filepath.Walk(sourcePath, func(p string, dir os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !dir.IsDir() || dir.Name() == "." {
			return nil
		}

		if dir.IsDir() && dir.Name() == "cmd" {
			return filepath.SkipDir
		}

		files, _ := filepath.Glob(p + string(os.PathSeparator) + "*.go")
		if len(files) == 0 {
			return nil
		}

		file, err := ioutil.ReadFile(files[0])
		if err != nil {
			return err
		}

		r, _ := regexp.Compile(`package ([\w]+)`)
		match := r.FindStringSubmatch(string(file))
		if len(match) > 1 {
			pkgName := match[1]

			// Ignore the main package since it's usually located under cmd/command-name/
			if pkgName == "main" {
				return nil
			}

			if dir.Name() != pkgName {
				ok = false
			}
		}

		return nil
	})

	return ok
}

func hasBenches() bool {
	return util.FindOccurrencesInTree(sourcePath, `func\sBenchmark\w+\(`, "*_test.go") > 0
}

func hasBlackboxTests() bool {
	return util.FindOccurrencesInTree(sourcePath, `"testing\/quick"`, "*_test.go") > 0
}

func hasBuildPackage() bool {
	return util.FindOccurrencesInTree(sourcePath, `package\smain`, "*.go") > 0
}
<commit_msg>Dir match - skip cmd and _ folders (#18)<commit_after>// Package goprove contains lib for checking the Golang best practi
package goprove

import (
	"go/format"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/golang/lint"
	"github.com/ryanuber/go-license"

	"github.com/karolgorecki/goprove/util"
)

func projectBuilds() bool {
	_, err := exec.Command("go", "build", sourcePath).Output()
	return err == nil
}

func isFormatted() bool {
	errors := 0
	filepath.Walk(sourcePath, func(path string, f os.FileInfo, err error) error {
		if !strings.HasSuffix(filepath.Ext(path), ".go") {
			return nil
		}

		file, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		fmtFile, _ := format.Source(file)

		if string(file) != string(fmtFile) {
			errors++
		}
		return nil
	})
	return errors == 0
}

func testPassing() bool {
	output, _ := exec.Command("go", "test", sourcePath+"/...").Output()
	return strings.Index(string(output), `--- FAIL`) == -1
}

func hasLicense() bool {
	if _, err := license.NewFromDir(sourcePath); err != nil {
		return false
	}
	return true
}

func hasReadme() bool {
	return util.FilesExistAny(sourcePath, "readme")
}

func hasContributing() bool {
	return util.FilesExistAny(sourcePath, "contribution", "contribute", "contributing")
}

func isLinted() bool {
	errors := 0
	l := new(lint.Linter)

	filepath.Walk(sourcePath+"/...", func(path string, f os.FileInfo, err error) error {

		if !strings.HasSuffix(filepath.Ext(path), ".go") {
			return nil
		}

		file, err := ioutil.ReadFile(path)
		if err != nil {
			return nil
		}

		if lnt, _ := l.Lint(f.Name(), file); len(lnt) > 0 {
			if lnt[0].Confidence > 0.2 {
				errors++
				return nil
			}
		}
		return nil
	})

	return errors == 0
}

func isVetted() bool {
	_, err := exec.Command("go", "vet", sourceGoPath).Output()
	return err == nil
}

func isDirMatch() bool {
	ok := true

	filepath.Walk(sourcePath, func(p string, dir os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !dir.IsDir() || dir.Name() == "." {
			return nil
		}

		// If the dir is "cmd" or it starts with "_" we should skip it
		if dir.IsDir() && (dir.Name() == "cmd" || dir.Name()[0] == '_') {
			return filepath.SkipDir
		}

		files, _ := filepath.Glob(p + string(os.PathSeparator) + "*.go")
		if len(files) == 0 {
			return nil
		}

		file, err := ioutil.ReadFile(files[0])
		if err != nil {
			return err
		}

		r, _ := regexp.Compile(`package ([\w]+)`)
		match := r.FindStringSubmatch(string(file))
		if len(match) > 1 {
			if dir.Name() != match[1] {
				ok = false
			}
		}

		return nil
	})

	return ok
}

func hasBenches() bool {
	return util.FindOccurrencesInTree(sourcePath, `func\sBenchmark\w+\(`, "*_test.go") > 0
}

func hasBlackboxTests() bool {
	return util.FindOccurrencesInTree(sourcePath, `"testing\/quick"`, "*_test.go") > 0
}

func hasBuildPackage() bool {
	return util.FindOccurrencesInTree(sourcePath, `package\smain`, "*.go") > 0
}
<|endoftext|>


<commit_before>package fuse

// Written with a look to http://ptspts.blogspot.com/2009/11/fuse-protocol-tutorial-for-linux-26.html

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"net"
	"os"
	"path"
	"syscall"
	"unsafe"
)

const (
	bufSize = 65536 + 100 // See the link above for the details
)

type FileSystem interface{}

type MountPoint struct {
	mountPoint string
	f          *os.File
}

// Mount create a fuse fs on the specified mount point.
func Mount(mountPoint string, fs FileSystem) (m *MountPoint, err os.Error) {
	local, remote, err := net.Socketpair("unixgram")
	if err != nil {
		return
	}

	defer local.Close()
	defer remote.Close()

	mountPoint = path.Clean(mountPoint)
	if !path.Rooted(mountPoint) {
		cwd, err := os.Getwd()
		if err != nil {
			return
		}
		mountPoint = path.Clean(path.Join(cwd, mountPoint))
	}
	pid, err := os.ForkExec("/bin/fusermount",
		[]string{"/bin/fusermount", mountPoint},
		[]string{"_FUSE_COMMFD=3"},
		"",
		[]*os.File{nil, nil, nil, remote.File()})
	if err != nil {
		return
	}
	w, err := os.Wait(pid, 0)
	if err != nil {
		return
	}
	if w.ExitStatus() != 0 {
		return nil, os.NewError(fmt.Sprintf("fusermount exited with code %d\n", w.ExitStatus()))
	}

	f, err := getFuseConn(local)
	if err != nil {
		return
	}
	m = &MountPoint{mountPoint, f}
	go m.loop()
	return
}

func (m *MountPoint) loop() {
	buf := make([]byte, bufSize)
	f := m.f
	errors := make(chan os.Error, 100)
	toW := make(chan [][]byte, 100)
	go m.errorHandler(errors)
	go m.writer(f, toW, errors)
	for {
		n, err := f.Read(buf)
		if err != nil {
			errors <- err
		}
		go m.handle(buf[0:n], toW, errors)
	}
}

func (m *MountPoint) handle(in []byte, toW chan [][]byte, errors chan os.Error) {
	r := bytes.NewBuffer(in)
	var h InHeader
	err := binary.Read(r, binary.LittleEndian, &h)
	if err != nil {
		errors <- err
		return
	}
	fmt.Printf("Here! in = %v, h = %v\n", in, h)
	os.Exit(0)

}

func (m *MountPoint) writer(f *os.File, in chan [][]byte, errors chan os.Error) {
	fd := f.Fd()
	for packet := range in {
		_, err := Writev(fd, packet)
		if err != nil {
			errors <- err
			continue
		}
	}
}

func (m *MountPoint) errorHandler(errors chan os.Error) {
	for err := range errors {
		log.Stderr("MountPoint.errorHandler: ", err)
	}
}

func (m *MountPoint) Unmount() (err os.Error) {
	if m == nil {
		return nil
	}
	pid, err := os.ForkExec("/bin/fusermount",
		[]string{"/bin/fusermount", "-u", m.mountPoint},
		nil,
		"",
		[]*os.File{nil, nil, os.Stderr})
	if err != nil {
		return
	}
	w, err := os.Wait(pid, 0)
	if err != nil {
		return
	}
	if w.ExitStatus() != 0 {
		return os.NewError(fmt.Sprintf("fusermount exited with code %d\n", w.ExitStatus()))
	}
	m.f.Close()
	return
}

func recvmsg(fd int, msg *syscall.Msghdr, flags int) (n int, errno int) {
	n1, _, e1 := syscall.Syscall(syscall.SYS_RECVMSG, uintptr(fd), uintptr(unsafe.Pointer(msg)), uintptr(flags))
	n = int(n1)
	errno = int(e1)
	return
}

func Recvmsg(fd int, msg *syscall.Msghdr, flags int) (n int, err os.Error) {
	n, errno := recvmsg(fd, msg, flags)
	if errno != 0 {
		err = os.NewSyscallError("recvmsg", errno)
	}
	return
}

func writev(fd int, iovecs *syscall.Iovec, cnt int) (n int, errno int) {
	n1, _, e1 := syscall.Syscall(syscall.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(iovecs)), uintptr(cnt))
	n = int(n1)
	errno = int(e1)
	return
}

func Writev(fd int, packet [][]byte) (n int, err os.Error) {
	if len(packet) == 0 {
		return
	}
	iovecs := make([]syscall.Iovec, len(packet))
	for i, v := range packet {
		if v == nil {
			continue
		}
		iovecs[i].Base = (*byte)(unsafe.Pointer(&packet[i][0]))
		iovecs[i].Len = uint64(len(packet[i]))
	}
	n, errno := writev(fd, (*syscall.Iovec)(unsafe.Pointer(&iovecs[0])), len(iovecs))
	if errno != 0 {
		err = os.NewSyscallError("writev", errno)
		return
	}
	return
}

func getFuseConn(local net.Conn) (f *os.File, err os.Error) {
	var msg syscall.Msghdr
	var iov syscall.Iovec
	base := make([]int32, 256)
	control := make([]int32, 256)

	iov.Base = (*byte)(unsafe.Pointer(&base[0]))
	iov.Len = uint64(len(base) * 4)
	msg.Iov = (*syscall.Iovec)(unsafe.Pointer(&iov))
	msg.Iovlen = 1
	msg.Control = (*byte)(unsafe.Pointer(&control[0]))
	msg.Controllen = uint64(len(control) * 4)

	_, err = Recvmsg(local.File().Fd(), &msg, 0)
	if err != nil {
		return
	}

	length := control[0]
	typ := control[2] // syscall.Cmsghdr.Type
	fd := control[4]
	if typ != 1 {
		err = os.NewError(fmt.Sprintf("getFuseConn: recvmsg returned wrong control type: %d", typ))
		return
	}
	if length < 20 {
		err = os.NewError(fmt.Sprintf("getFuseConn: too short control message. Length: %d", length))
		return
	}

	if fd < 0 {
		err = os.NewError(fmt.Sprintf("getFuseConn: fd < 0: %d", fd))
		return
	}
	f = os.NewFile(int(fd), "fuse-conn")
	return
}
<commit_msg>Loop is now just listening, there's writer, errorHandler goroutines and a handler goroutine per request<commit_after>package fuse

// Written with a look to http://ptspts.blogspot.com/2009/11/fuse-protocol-tutorial-for-linux-26.html

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"net"
	"os"
	"path"
	"syscall"
	"unsafe"
)

const (
	bufSize = 65536 + 100 // See the link above for the details
)

type FileSystem interface{}

type MountPoint struct {
	mountPoint string
	f          *os.File
}

// Mount create a fuse fs on the specified mount point.
func Mount(mountPoint string, fs FileSystem) (m *MountPoint, err os.Error) {
	local, remote, err := net.Socketpair("unixgram")
	if err != nil {
		return
	}

	defer local.Close()
	defer remote.Close()

	mountPoint = path.Clean(mountPoint)
	if !path.Rooted(mountPoint) {
		cwd, err := os.Getwd()
		if err != nil {
			return
		}
		mountPoint = path.Clean(path.Join(cwd, mountPoint))
	}
	pid, err := os.ForkExec("/bin/fusermount",
		[]string{"/bin/fusermount", mountPoint},
		[]string{"_FUSE_COMMFD=3"},
		"",
		[]*os.File{nil, nil, nil, remote.File()})
	if err != nil {
		return
	}
	w, err := os.Wait(pid, 0)
	if err != nil {
		return
	}
	if w.ExitStatus() != 0 {
		return nil, os.NewError(fmt.Sprintf("fusermount exited with code %d\n", w.ExitStatus()))
	}

	f, err := getFuseConn(local)
	if err != nil {
		return
	}
	m = &MountPoint{mountPoint, f}
	go m.loop()
	return
}

func (m *MountPoint) loop() {
	buf := make([]byte, bufSize)
	f := m.f
	errors := make(chan os.Error, 100)
	toW := make(chan [][]byte, 100)
	go m.errorHandler(errors)
	go m.writer(f, toW, errors)
	for {
		n, err := f.Read(buf)
		if err != nil {
			errors <- err
		}
		go m.handle(buf[0:n], toW, errors)
	}
}

func (m *MountPoint) handle(in []byte, toW chan [][]byte, errors chan os.Error) {
	r := bytes.NewBuffer(in)
	var h InHeader
	err := binary.Read(r, binary.LittleEndian, &h)
	if err != nil {
		errors <- err
		return
	}
	fmt.Printf("Here! in = %v, h = %v\n", in, h)
	os.Exit(0)

}

func (m *MountPoint) writer(f *os.File, in chan [][]byte, errors chan os.Error) {
	fd := f.Fd()
	for packet := range in {
		_, err := Writev(fd, packet)
		if err != nil {
			errors <- err
			continue
		}
	}
}

func (m *MountPoint) errorHandler(errors chan os.Error) {
	for err := range errors {
		log.Stderr("MountPoint.errorHandler: ", err)
	}
}

func (m *MountPoint) Unmount() (err os.Error) {
	if m == nil {
		return nil
	}
	pid, err := os.ForkExec("/bin/fusermount",
		[]string{"/bin/fusermount", "-u", m.mountPoint},
		nil,
		"",
		[]*os.File{nil, nil, os.Stderr})
	if err != nil {
		return
	}
	w, err := os.Wait(pid, 0)
	if err != nil {
		return
	}
	if w.ExitStatus() != 0 {
		return os.NewError(fmt.Sprintf("fusermount exited with code %d\n", w.ExitStatus()))
	}
	m.f.Close()
	return
}

func recvmsg(fd int, msg *syscall.Msghdr, flags int) (n int, errno int) {
	n1, _, e1 := syscall.Syscall(syscall.SYS_RECVMSG, uintptr(fd), uintptr(unsafe.Pointer(msg)), uintptr(flags))
	n = int(n1)
	errno = int(e1)
	return
}

func Recvmsg(fd int, msg *syscall.Msghdr, flags int) (n int, err os.Error) {
	n, errno := recvmsg(fd, msg, flags)
	if n == 0 && errno == 0 {
		return 0, os.EOF
	}
	if errno != 0 {
		err = os.NewSyscallError("recvmsg", errno)
	}
	return
}

func writev(fd int, iovecs *syscall.Iovec, cnt int) (n int, errno int) {
	n1, _, e1 := syscall.Syscall(syscall.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(iovecs)), uintptr(cnt))
	n = int(n1)
	errno = int(e1)
	return
}

func Writev(fd int, packet [][]byte) (n int, err os.Error) {
	if len(packet) == 0 {
		return
	}
	iovecs := make([]syscall.Iovec, len(packet))
	for i, v := range packet {
		if v == nil {
			continue
		}
		iovecs[i].Base = (*byte)(unsafe.Pointer(&packet[i][0]))
		iovecs[i].Len = uint64(len(packet[i]))
	}
	n, errno := writev(fd, (*syscall.Iovec)(unsafe.Pointer(&iovecs[0])), len(iovecs))

	if errno != 0 {
		err = os.NewSyscallError("writev", errno)
		return
	}
	return
}

func getFuseConn(local net.Conn) (f *os.File, err os.Error) {
	var msg syscall.Msghdr
	var iov syscall.Iovec
	base := make([]int32, 256)
	control := make([]int32, 256)

	iov.Base = (*byte)(unsafe.Pointer(&base[0]))
	iov.Len = uint64(len(base) * 4)
	msg.Iov = (*syscall.Iovec)(unsafe.Pointer(&iov))
	msg.Iovlen = 1
	msg.Control = (*byte)(unsafe.Pointer(&control[0]))
	msg.Controllen = uint64(len(control) * 4)

	_, err = Recvmsg(local.File().Fd(), &msg, 0)
	if err != nil {
		return
	}

	length := control[0]
	typ := control[2] // syscall.Cmsghdr.Type
	fd := control[4]
	if typ != 1 {
		err = os.NewError(fmt.Sprintf("getFuseConn: recvmsg returned wrong control type: %d", typ))
		return
	}
	if length < 20 {
		err = os.NewError(fmt.Sprintf("getFuseConn: too short control message. Length: %d", length))
		return
	}

	if fd < 0 {
		err = os.NewError(fmt.Sprintf("getFuseConn: fd < 0: %d", fd))
		return
	}
	f = os.NewFile(int(fd), "fuse-conn")
	return
}
<|endoftext|>


<commit_before><commit_msg>Removing the t.Parallel call since as @ysimonson points out, the log redirecting within could cause issues with other tests.<commit_after><|endoftext|>


<commit_before>package game

import (
	"errors"
	"github.com/svera/acquire/game/board"
	"github.com/svera/acquire/game/corporation"
	"github.com/svera/acquire/game/player"
	"github.com/svera/acquire/game/tileset"
)

const totalCorporations = 7

type Game struct {
	board         *board.Board
	status        []string
	players       []*player.Player
	corporations  [7]*corporation.Corporation
	tileset       *tileset.Tileset
	currentPlayer uint
}

func New(
	board *board.Board, players []*player.Player, corporations [7]*corporation.Corporation, tileset *tileset.Tileset) (*Game, error) {
	if len(players) < 3 || len(players) > 6 {
		return nil, errors.New("Number of players must be between 3 and 6")
	}

	game := Game{
		board:         board,
		players:       players,
		corporations:  corporations,
		tileset:       tileset,
		currentPlayer: 0,
	}
	for _, player := range game.players {
		game.giveInitialTileset(player)
	}
	for i, corporation := range game.corporations {
		corporation.SetId(uint(i))
	}
	return &game, nil
}

func (g *Game) giveInitialTileset(player *player.Player) {
	for i := 0; i < 6; i++ {
		tile, _ := g.tileset.Draw()
		player.GetTile(tile)
	}
}

// Check if game end conditions are reached
func (g *Game) AreEndConditionsReached() bool {
	active := g.getActiveCorporations()
	if len(active) == 0 {
		return false
	}
	for _, corporation := range active {
		if corporation.Size() >= 41 {
			return true
		}
		if !corporation.IsSafe() {
			return false
		}
	}
	return true
}

// Return all corporations on the board
func (g *Game) getActiveCorporations() []*corporation.Corporation {
	active := []*corporation.Corporation{}
	for _, corporation := range g.corporations {
		if corporation.IsActive() {
			active = append(active, corporation)
		}
	}
	return active
}

// Taken from the game rules:
// "If only one player owns stock in the defunct corporation, that player gets both bonuses. If there's
// a tie for majority stockholder, add the majority and minority bonuses and divide evenly (the minority
// shareholder gets no bonus. If there's a tie for minority stockholder, split the minority bonus among
// the tied players"
func (g *Game) GetMainStockHolders(corporation *corporation.Corporation) map[string][]*player.Player {
	mainStockHolders := map[string][]*player.Player{"primary": {}, "secondary": {}}
	stockHolders := g.getStockHolders(corporation)

	if len(stockHolders) == 1 {
		return map[string][]*player.Player{
			"primary":   {stockHolders[0]},
			"secondary": {stockHolders[0]},
		}
	}

	mainStockHolders["primary"] = stockHoldersWithSameAmount(0, stockHolders, corporation)
	if len(mainStockHolders["primary"]) > 1 {
		return mainStockHolders
	}
	mainStockHolders["secondary"] = stockHoldersWithSameAmount(1, stockHolders, corporation)
	return mainStockHolders
}

// Loop stockHolders from groupStart to get all stock holders with the same amount of shares for
// the passed corporation
func stockHoldersWithSameAmount(groupStart int, stockHolders []*player.Player, corporation *corporation.Corporation) []*player.Player {
	group := []*player.Player{}
	group = append(group, stockHolders[groupStart])
	if groupStart+1 < len(stockHolders) && stockHolders[groupStart].Shares(corporation) == stockHolders[groupStart+1].Shares(corporation) {
		group = append(group, stockHolders[groupStart+1])
		i := groupStart + 2
		for i < len(stockHolders) && stockHolders[groupStart] == stockHolders[i] {
			group = append(group, stockHolders[i])
			i++
		}
	}
	return group
}

// Get players who have stock of the passed corporation, ordered descendently by number of stock shares
// of that corporation
func (g *Game) getStockHolders(corporation *corporation.Corporation) []*player.Player {
	var stockHolders []*player.Player
	sharesDesc := func(p1, p2 *player.Player) bool {
		return p1.Shares(corporation) > p2.Shares(corporation)
	}

	for _, player := range g.players {
		if player.Shares(corporation) > 0 {
			stockHolders = append(stockHolders, player)
		}
	}
	player.By(sharesDesc).Sort(stockHolders)
	return stockHolders
}

// Returns true if a tile is permanently unplayable, that is,
// that putting it on the board would merge two or more safe corporations
func (g *Game) isTileUnplayable(tile tileset.Position) bool {
	adjacents := g.board.AdjacentCells(tile)
	for _, adjacent := range adjacents {
		safeNeighbours := 0
		boardCell := g.board.Cell(adjacent)
		if boardCell != board.CellEmpty && boardCell != board.CellOrphanTile {
			if g.corporations[boardCell].IsSafe() {
				safeNeighbours++
			}
		}
		if safeNeighbours == 2 {
			return true
		}
	}
	return false
}

// Returns true if a tile is temporarily unplayable, that is,
// that putting it on the board would create an 8th corporation
func (g *Game) isTileTemporaryUnplayable(tile tileset.Position) bool {
	if len(g.getActiveCorporations()) < totalCorporations {
		return false
	}
	adjacents := g.board.AdjacentCells(tile)
	for _, adjacent := range adjacents {
		boardCell := g.board.Cell(adjacent)
		if boardCell == board.CellOrphanTile {
			return true
		}
	}
	return false
}
<commit_msg>Simplified function<commit_after>package game

import (
	"errors"
	"github.com/svera/acquire/game/board"
	"github.com/svera/acquire/game/corporation"
	"github.com/svera/acquire/game/player"
	"github.com/svera/acquire/game/tileset"
)

const totalCorporations = 7

type Game struct {
	board         *board.Board
	status        []string
	players       []*player.Player
	corporations  [7]*corporation.Corporation
	tileset       *tileset.Tileset
	currentPlayer uint
}

func New(
	board *board.Board, players []*player.Player, corporations [7]*corporation.Corporation, tileset *tileset.Tileset) (*Game, error) {
	if len(players) < 3 || len(players) > 6 {
		return nil, errors.New("Number of players must be between 3 and 6")
	}

	game := Game{
		board:         board,
		players:       players,
		corporations:  corporations,
		tileset:       tileset,
		currentPlayer: 0,
	}
	for _, player := range game.players {
		game.giveInitialTileset(player)
	}
	for i, corporation := range game.corporations {
		corporation.SetId(uint(i))
	}
	return &game, nil
}

func (g *Game) giveInitialTileset(player *player.Player) {
	for i := 0; i < 6; i++ {
		tile, _ := g.tileset.Draw()
		player.GetTile(tile)
	}
}

// Check if game end conditions are reached
func (g *Game) AreEndConditionsReached() bool {
	active := g.getActiveCorporations()
	if len(active) == 0 {
		return false
	}
	for _, corporation := range active {
		if corporation.Size() >= 41 {
			return true
		}
		if !corporation.IsSafe() {
			return false
		}
	}
	return true
}

// Return all corporations on the board
func (g *Game) getActiveCorporations() []*corporation.Corporation {
	active := []*corporation.Corporation{}
	for _, corporation := range g.corporations {
		if corporation.IsActive() {
			active = append(active, corporation)
		}
	}
	return active
}

// Taken from the game rules:
// "If only one player owns stock in the defunct corporation, that player gets both bonuses. If there's
// a tie for majority stockholder, add the majority and minority bonuses and divide evenly (the minority
// shareholder gets no bonus. If there's a tie for minority stockholder, split the minority bonus among
// the tied players"
func (g *Game) GetMainStockHolders(corporation *corporation.Corporation) map[string][]*player.Player {
	mainStockHolders := map[string][]*player.Player{"primary": {}, "secondary": {}}
	stockHolders := g.getStockHolders(corporation)

	if len(stockHolders) == 1 {
		return map[string][]*player.Player{
			"primary":   {stockHolders[0]},
			"secondary": {stockHolders[0]},
		}
	}

	mainStockHolders["primary"] = stockHoldersWithSameAmount(0, stockHolders, corporation)
	if len(mainStockHolders["primary"]) > 1 {
		return mainStockHolders
	}
	mainStockHolders["secondary"] = stockHoldersWithSameAmount(1, stockHolders, corporation)
	return mainStockHolders
}

// Loop stockHolders from groupStart to get all stock holders with the same amount of shares for
// the passed corporation
func stockHoldersWithSameAmount(groupStart int, stockHolders []*player.Player, corporation *corporation.Corporation) []*player.Player {
	group := []*player.Player{}
	group = append(group, stockHolders[groupStart])

	i := groupStart + 1
	for i < len(stockHolders) && stockHolders[groupStart].Shares(corporation) == stockHolders[i].Shares(corporation) {
		group = append(group, stockHolders[i])
		i++
	}
	return group
}

// Get players who have stock of the passed corporation, ordered descendently by number of stock shares
// of that corporation
func (g *Game) getStockHolders(corporation *corporation.Corporation) []*player.Player {
	var stockHolders []*player.Player
	sharesDesc := func(p1, p2 *player.Player) bool {
		return p1.Shares(corporation) > p2.Shares(corporation)
	}

	for _, player := range g.players {
		if player.Shares(corporation) > 0 {
			stockHolders = append(stockHolders, player)
		}
	}
	player.By(sharesDesc).Sort(stockHolders)
	return stockHolders
}

// Returns true if a tile is permanently unplayable, that is,
// that putting it on the board would merge two or more safe corporations
func (g *Game) isTileUnplayable(tile tileset.Position) bool {
	adjacents := g.board.AdjacentCells(tile)
	for _, adjacent := range adjacents {
		safeNeighbours := 0
		boardCell := g.board.Cell(adjacent)
		if boardCell != board.CellEmpty && boardCell != board.CellOrphanTile {
			if g.corporations[boardCell].IsSafe() {
				safeNeighbours++
			}
		}
		if safeNeighbours == 2 {
			return true
		}
	}
	return false
}

// Returns true if a tile is temporarily unplayable, that is,
// that putting it on the board would create an 8th corporation
func (g *Game) isTileTemporaryUnplayable(tile tileset.Position) bool {
	if len(g.getActiveCorporations()) < totalCorporations {
		return false
	}
	adjacents := g.board.AdjacentCells(tile)
	for _, adjacent := range adjacents {
		boardCell := g.board.Cell(adjacent)
		if boardCell == board.CellOrphanTile {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package etcd

import (
	log "github.com/Sirupsen/logrus"
	"github.com/nightlegend/apigateway/core/utils/etcd"
	"golang.org/x/net/context"
)

// SaveAction :save data to etcd.
func SaveAction() {
	kapi := etcd.EtcdConn()
	resp, err := kapi.Set(context.Background(), "/test", "test", nil)
	if err != nil {
		log.Fatal(err)
	} else {
		// print common key info
		log.Printf("Set is done. Metadata is %q\n", resp)
	}
	// kapi.Watcher(key, opts)

}
<commit_msg>fix can not find function name<commit_after>package etcd

import (
	log "github.com/Sirupsen/logrus"
	"github.com/nightlegend/apigateway/core/utils/etcd"
	"golang.org/x/net/context"
)

// SaveAction :save data to etcd.
func SaveAction() {
	kapi := etcd.Conn()
	resp, err := kapi.Set(context.Background(), "/test", "test", nil)
	if err != nil {
		log.Fatal(err)
	} else {
		// print common key info
		log.Printf("Set is done. Metadata is %q\n", resp)
	}
	// kapi.Watcher(key, opts)

}
<|endoftext|>


<commit_before>package connmgr

import (
	"io"
	"time"

	"github.com/libp2p/go-libp2p-core/peer"
)

// Decayer is implemented by connection managers supporting decaying tags. A
// decaying tag is one whose value automatically decays over time.
//
// The actual application of the decay behaviour is encapsulated in a
// user-provided decaying function (DecayFn). The function is called on every
// tick (determined by the interval parameter), and returns either the new value
// of the tag, or whether it should be erased altogether.
//
// We do not set values on a decaying tag. Rather, we "bump" decaying tags by a
// delta. This calls the BumpFn with the old value and the delta, to determine
// the new value.
//
// Such a pluggable design affords a great deal of flexibility and versatility.
// Behaviours that are straightfoward to implement include:
//
//  * Decay a tag by -1, or by half its current value, on every tick.
//  * Every time a value is bumped, sum it to its current value.
//  * Exponentially boost a score with every bump.
//  * Sum the incoming score, but keep it within min, max bounds.
//
// Commonly used DecayFns and BumpFns are provided in the go-libp2p-connmgr
// module.
type Decayer interface {
	io.Closer

	// RegisterDecayingTag creates and registers a new decaying tag, if and only
	// if a tag with the supplied name doesn't exist yet. Otherwise, an error is
	// returned.
	//
	// The caller provides the interval at which the tag is refreshed, as well
	// as the decay function and the bump function. Refer to godocs on DecayFn
	// and BumpFn for more info.
	RegisterDecayingTag(name string, interval time.Duration, decayFn DecayFn, bumpFn BumpFn) (DecayingTag, error)
}

// DecayFn applies a decay to the peer's score. The implementation must call
// DecayFn at the interval supplied when registering the tag.
//
// It receives a copy of the decaying value, and returns the score after
// applying the decay, as well as a flag to signal if the tag should be erased.
type DecayFn func(value DecayingValue) (after int, rm bool)

// BumpFn applies a delta onto an existing score, and returns the new score.
//
// Non-trivial bump functions include exponential boosting, moving averages,
// ceilings, etc.
type BumpFn func(value DecayingValue, delta int) (after int)

// DecayingTag represents a decaying tag. The tag is a long-lived general
// object, used to operate on tag values for peers.
type DecayingTag interface {
	// Name returns the name of the tag.
	Name() string

	// Interval is the effective interval at which this tag will tick. Upon
	// registration, the desired interval may be overwritten depending on the
	// decayer's resolution, and this method allows you to obtain the effective
	// interval.
	Interval() time.Duration

	// Bump applies a delta to a tag value, calling its bump function. The bump
	// may be applied asynchronously, in which case the returned error is used
	// to indicate an anomaly when queuing.
	Bump(peer peer.ID, delta int) error
}

// DecayingValue represents a value for a decaying tag.
type DecayingValue struct {
	// Tag points to the tag this value belongs to.
	Tag DecayingTag

	// Peer is the peer ID to whom this value is associated.
	Peer peer.ID

	// Added is the timestamp when this value was added for the first time for
	// a tag and a peer.
	Added time.Time

	// LastVisit is the timestamp of the last visit.
	LastVisit time.Time

	// Value is the current value of the tag.
	Value int
}
<commit_msg>minor godoc fixes.<commit_after>package connmgr

import (
	"io"
	"time"

	"github.com/libp2p/go-libp2p-core/peer"
)

// Decayer is implemented by connection managers supporting decaying tags. A
// decaying tag is one whose value automatically decays over time.
//
// The actual application of the decay behaviour is encapsulated in a
// user-provided decaying function (DecayFn). The function is called on every
// tick (determined by the interval parameter), and returns either the new value
// of the tag, or whether it should be erased altogether.
//
// We do not set values on a decaying tag. Rather, we "bump" decaying tags by a
// delta. This calls the BumpFn with the old value and the delta, to determine
// the new value.
//
// Such a pluggable design affords a great deal of flexibility and versatility.
// Behaviours that are straightforward to implement include:
//
//  * Decay a tag by -1, or by half its current value, on every tick.
//  * Every time a value is bumped, sum it to its current value.
//  * Exponentially boost a score with every bump.
//  * Sum the incoming score, but keep it within min, max bounds.
//
// Commonly used DecayFns and BumpFns are provided in this package.
type Decayer interface {
	io.Closer

	// RegisterDecayingTag creates and registers a new decaying tag, if and only
	// if a tag with the supplied name doesn't exist yet. Otherwise, an error is
	// returned.
	//
	// The caller provides the interval at which the tag is refreshed, as well
	// as the decay function and the bump function. Refer to godocs on DecayFn
	// and BumpFn for more info.
	RegisterDecayingTag(name string, interval time.Duration, decayFn DecayFn, bumpFn BumpFn) (DecayingTag, error)
}

// DecayFn applies a decay to the peer's score. The implementation must call
// DecayFn at the interval supplied when registering the tag.
//
// It receives a copy of the decaying value, and returns the score after
// applying the decay, as well as a flag to signal if the tag should be erased.
type DecayFn func(value DecayingValue) (after int, rm bool)

// BumpFn applies a delta onto an existing score, and returns the new score.
//
// Non-trivial bump functions include exponential boosting, moving averages,
// ceilings, etc.
type BumpFn func(value DecayingValue, delta int) (after int)

// DecayingTag represents a decaying tag. The tag is a long-lived general
// object, used to operate on tag values for peers.
type DecayingTag interface {
	// Name returns the name of the tag.
	Name() string

	// Interval is the effective interval at which this tag will tick. Upon
	// registration, the desired interval may be overwritten depending on the
	// decayer's resolution, and this method allows you to obtain the effective
	// interval.
	Interval() time.Duration

	// Bump applies a delta to a tag value, calling its bump function. The bump
	// may be applied asynchronously, in which case the returned error is used
	// to indicate an anomaly when queuing.
	Bump(peer peer.ID, delta int) error
}

// DecayingValue represents a value for a decaying tag.
type DecayingValue struct {
	// Tag points to the tag this value belongs to.
	Tag DecayingTag

	// Peer is the peer ID to whom this value is associated.
	Peer peer.ID

	// Added is the timestamp when this value was added for the first time for
	// a tag and a peer.
	Added time.Time

	// LastVisit is the timestamp of the last visit.
	LastVisit time.Time

	// Value is the current value of the tag.
	Value int
}
<|endoftext|>


<commit_before>package eventchannel

import (
	"bytes"
	"compress/gzip"
	"github.com/stretchr/testify/assert"
	"io/ioutil"
	"sync"
	"testing"
	"time"
)

var maxByteSize = int64(15)
var maxEventCount = int64(3)
var maxTime = 2 * time.Hour

func readGz(encoded bytes.Buffer) string {
	gr, _ := gzip.NewReader(bytes.NewBuffer(encoded.Bytes()))
	defer gr.Close()

	decoded, _ := ioutil.ReadAll(gr)
	return string(decoded)
}

func newSender(data *[]byte) Sender {
	mux := &sync.Mutex{}
	return func(payload []byte) error {
		mux.Lock()
		defer mux.Unlock()
		event := bytes.Buffer{}
		event.Write(payload)
		*data = append(*data, readGz(event)...)
		return nil
	}
}

func TestEventChannel_isBufferFull(t *testing.T) {

	send := func(_ []byte) error { return nil }

	eventChannel := NewEventChannel(send, maxByteSize, maxEventCount, maxTime)
	defer eventChannel.Close()

	eventChannel.buffer([]byte("one"))
	eventChannel.buffer([]byte("two"))

	assert.Equal(t, eventChannel.isBufferFull(), false)

	eventChannel.buffer([]byte("three"))

	assert.Equal(t, eventChannel.isBufferFull(), true)

	eventChannel.reset()

	assert.Equal(t, eventChannel.isBufferFull(), false)

	eventChannel.buffer([]byte("big-event-abcdefghijklmnopqrstuvwxyz"))

	assert.Equal(t, eventChannel.isBufferFull(), true)

}

func TestEventChannel_reset(t *testing.T) {
	send := func(_ []byte) error { return nil }

	eventChannel := NewEventChannel(send, maxByteSize, maxEventCount, maxTime)
	defer eventChannel.Close()

	assert.Equal(t, eventChannel.metrics.eventCount, int64(0))
	assert.Equal(t, eventChannel.metrics.bufferSize, int64(0))

	eventChannel.buffer([]byte("one"))
	eventChannel.buffer([]byte("two"))

	assert.NotEqual(t, eventChannel.metrics.eventCount, int64(0))
	assert.NotEqual(t, eventChannel.metrics.bufferSize, int64(0))

	eventChannel.reset()

	assert.Equal(t, eventChannel.buff.Len(), 0)
	assert.Equal(t, eventChannel.metrics.eventCount, int64(0))
	assert.Equal(t, eventChannel.metrics.bufferSize, int64(0))
}

func TestEventChannel_flush(t *testing.T) {
	data := make([]byte, 0)
	send := newSender(&data)

	eventChannel := NewEventChannel(send, maxByteSize, maxEventCount, maxTime)
	defer eventChannel.Close()

	eventChannel.buffer([]byte("one"))
	eventChannel.buffer([]byte("two"))
	eventChannel.buffer([]byte("three"))
	eventChannel.flush()
	time.Sleep(10 * time.Millisecond)

	assert.Equal(t, string(data), "onetwothree")
}

func TestEventChannel_close(t *testing.T) {
	data := make([]byte, 0)
	send := newSender(&data)

	eventChannel := NewEventChannel(send, 15000, 15000, 2*time.Hour)

	eventChannel.buffer([]byte("one"))
	eventChannel.buffer([]byte("two"))
	eventChannel.buffer([]byte("three"))
	eventChannel.Close()

	time.Sleep(10 * time.Millisecond)

	assert.Equal(t, string(data), "onetwothree")
}

func TestEventChannel_Push(t *testing.T) {
	data := make([]byte, 0)
	send := newSender(&data)

	eventChannel := NewEventChannel(send, 15000, 5, 5*time.Millisecond)
	defer eventChannel.Close()

	eventChannel.Push([]byte("one"))
	eventChannel.Push([]byte("two"))
	eventChannel.Push([]byte("three"))
	eventChannel.Push([]byte("four"))
	eventChannel.Push([]byte("five"))
	eventChannel.Push([]byte("six"))
	eventChannel.Push([]byte("seven"))

	time.Sleep(10 * time.Millisecond)

	assert.Equal(t, string(data), "onetwothreefourfivesixseven")

}

func TestEventChannel_OutputFormat(t *testing.T) {

	toGzip := func(payload string) []byte {
		var buf bytes.Buffer
		zw := gzip.NewWriter(&buf)

		if _, err := zw.Write([]byte(payload)); err != nil {
			assert.Fail(t, err.Error())
		}

		if err := zw.Close(); err != nil {
			assert.Fail(t, err.Error())
		}
		return buf.Bytes()
	}

	data := make([]byte, 0)
	send := func(payload []byte) error {
		data = append(data, payload...)
		return nil
	}

	eventChannel := NewEventChannel(send, 15000, 10, 2*time.Minute)

	eventChannel.Push([]byte("one"))
	eventChannel.flush()
	eventChannel.Push([]byte("two"))
	eventChannel.Push([]byte("three"))

	eventChannel.Close()

	time.Sleep(10 * time.Millisecond)

	expected := append(toGzip("one"), toGzip("twothree")...)

	assert.Equal(t, expected, data)

}
<commit_msg>Fix Test TestEventChannel_OutputFormat (#1468)<commit_after>package eventchannel

import (
	"bytes"
	"compress/gzip"
	"io/ioutil"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

var maxByteSize = int64(15)
var maxEventCount = int64(3)
var maxTime = 2 * time.Hour

func readGz(encoded bytes.Buffer) string {
	gr, _ := gzip.NewReader(bytes.NewBuffer(encoded.Bytes()))
	defer gr.Close()

	decoded, _ := ioutil.ReadAll(gr)
	return string(decoded)
}

func newSender(data *[]byte) Sender {
	mux := &sync.Mutex{}
	return func(payload []byte) error {
		mux.Lock()
		defer mux.Unlock()
		event := bytes.Buffer{}
		event.Write(payload)
		*data = append(*data, readGz(event)...)
		return nil
	}
}

func TestEventChannel_isBufferFull(t *testing.T) {

	send := func(_ []byte) error { return nil }

	eventChannel := NewEventChannel(send, maxByteSize, maxEventCount, maxTime)
	defer eventChannel.Close()

	eventChannel.buffer([]byte("one"))
	eventChannel.buffer([]byte("two"))

	assert.Equal(t, eventChannel.isBufferFull(), false)

	eventChannel.buffer([]byte("three"))

	assert.Equal(t, eventChannel.isBufferFull(), true)

	eventChannel.reset()

	assert.Equal(t, eventChannel.isBufferFull(), false)

	eventChannel.buffer([]byte("big-event-abcdefghijklmnopqrstuvwxyz"))

	assert.Equal(t, eventChannel.isBufferFull(), true)

}

func TestEventChannel_reset(t *testing.T) {
	send := func(_ []byte) error { return nil }

	eventChannel := NewEventChannel(send, maxByteSize, maxEventCount, maxTime)
	defer eventChannel.Close()

	assert.Equal(t, eventChannel.metrics.eventCount, int64(0))
	assert.Equal(t, eventChannel.metrics.bufferSize, int64(0))

	eventChannel.buffer([]byte("one"))
	eventChannel.buffer([]byte("two"))

	assert.NotEqual(t, eventChannel.metrics.eventCount, int64(0))
	assert.NotEqual(t, eventChannel.metrics.bufferSize, int64(0))

	eventChannel.reset()

	assert.Equal(t, eventChannel.buff.Len(), 0)
	assert.Equal(t, eventChannel.metrics.eventCount, int64(0))
	assert.Equal(t, eventChannel.metrics.bufferSize, int64(0))
}

func TestEventChannel_flush(t *testing.T) {
	data := make([]byte, 0)
	send := newSender(&data)

	eventChannel := NewEventChannel(send, maxByteSize, maxEventCount, maxTime)
	defer eventChannel.Close()

	eventChannel.buffer([]byte("one"))
	eventChannel.buffer([]byte("two"))
	eventChannel.buffer([]byte("three"))
	eventChannel.flush()
	time.Sleep(10 * time.Millisecond)

	assert.Equal(t, string(data), "onetwothree")
}

func TestEventChannel_close(t *testing.T) {
	data := make([]byte, 0)
	send := newSender(&data)

	eventChannel := NewEventChannel(send, 15000, 15000, 2*time.Hour)

	eventChannel.buffer([]byte("one"))
	eventChannel.buffer([]byte("two"))
	eventChannel.buffer([]byte("three"))
	eventChannel.Close()

	time.Sleep(10 * time.Millisecond)

	assert.Equal(t, string(data), "onetwothree")
}

func TestEventChannel_Push(t *testing.T) {
	data := make([]byte, 0)
	send := newSender(&data)

	eventChannel := NewEventChannel(send, 15000, 5, 5*time.Millisecond)
	defer eventChannel.Close()

	eventChannel.Push([]byte("one"))
	eventChannel.Push([]byte("two"))
	eventChannel.Push([]byte("three"))
	eventChannel.Push([]byte("four"))
	eventChannel.Push([]byte("five"))
	eventChannel.Push([]byte("six"))
	eventChannel.Push([]byte("seven"))

	time.Sleep(10 * time.Millisecond)

	assert.Equal(t, string(data), "onetwothreefourfivesixseven")

}

func TestEventChannel_OutputFormat(t *testing.T) {

	toGzip := func(payload string) []byte {
		var buf bytes.Buffer
		zw := gzip.NewWriter(&buf)

		if _, err := zw.Write([]byte(payload)); err != nil {
			assert.Fail(t, err.Error())
		}

		if err := zw.Close(); err != nil {
			assert.Fail(t, err.Error())
		}
		return buf.Bytes()
	}

	data := make([]byte, 0)
	send := func(payload []byte) error {
		data = append(data, payload...)
		return nil
	}

	eventChannel := NewEventChannel(send, 15000, 10, 2*time.Minute)

	eventChannel.Push([]byte("one"))
	time.Sleep(1 * time.Millisecond)

	eventChannel.flush()

	eventChannel.Push([]byte("two"))
	time.Sleep(1 * time.Millisecond)

	eventChannel.Push([]byte("three"))
	time.Sleep(1 * time.Millisecond)

	eventChannel.Close()

	time.Sleep(1 * time.Millisecond)

	expected := append(toGzip("one"), toGzip("twothree")...)

	assert.Equal(t, expected, data)
}
<|endoftext|>


<commit_before>package report

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/s-rah/onionscan/utils"
)

const SEV_INFO = "info"
const SEV_LOW = "low"
const SEV_MEDIUM = "medium"
const SEV_HIGH = "high"
const SEV_CRITICAL = "critical"

type Risk struct {
	Severity    string   `json:"severity"`
	Title       string   `json:"title"`
	Description string   `json:"description"`
	Fix         string   `json:"fix"`
	Items       []string `json:"items"`
}

type SimpleReport struct {
	HiddenService string `json:"hiddenService"`
	Risks         []Risk `json:"risks"`
}

func (osr *SimpleReport) AddRisk(severity string, title string, description string, fix string, items []string) {
	osr.Risks = append(osr.Risks, Risk{severity, title, description, fix, items})
}

// Format as JSON
func (osr *SimpleReport) Serialize() (string, error) {
	report, err := json.Marshal(osr)
	if err != nil {
		return "", err
	}
	return string(report), nil
}

var risk_levels = map[string]string{
	SEV_INFO:     "\033[094mInfo:\033[0m",
	SEV_LOW:      "\033[093mLow Risk:\033[0m",
	SEV_MEDIUM:   "\033[093mMedium Risk:\033[0m",
	SEV_HIGH:     "\033[091mHigh Risk:\033[0m",
	SEV_CRITICAL: "\033[091mCritical Risk:\033[0m",
}

// Format as human-readable text to be printed to console
func (osr *SimpleReport) Format(width int) (string, error) {
	buffer := bytes.NewBuffer(nil)
	buffer.WriteString("--------------- OnionScan Report ---------------\n")

	buffer.WriteString(fmt.Sprintf("Generating Report for: %s\n\n", osr.HiddenService))
	const indent = "         "

	for _, risk := range osr.Risks {
		buffer.WriteString(risk_levels[risk.Severity] + " " + risk.Title + "\n")
		if len(risk.Description) > 0 {
			buffer.WriteString(indent + utils.FormatParagraphs(risk.Description, width, len(indent)) + "\n")
		}
		if len(risk.Fix) > 0 {
			buffer.WriteString(indent + utils.FormatParagraphs(risk.Fix, width, len(indent)) + "\n")
		}
		if len(risk.Items) > 0 {
			buffer.WriteString(indent + "Items Identified:\n")
			buffer.WriteString("\n")
			for _, item := range risk.Items {
				buffer.WriteString(indent + item + "\n")
			}
		}
		buffer.WriteString("\n")

	}
	return buffer.String(), nil
}

func SummarizeToSimpleReport(report *AnonymityReport) *SimpleReport {
	var out = NewSimpleReport(report.OnionScanReport.HiddenService)

	if len(report.EmailAddresses) > 0 {
		out.AddRisk(SEV_INFO, "Found Identities", "", "", report.EmailAddresses)
	}

	if len(report.IPAddresses) > 0 {
		out.AddRisk(SEV_INFO, "Found IP Addresses", "", "", report.IPAddresses)
	}

	if len(report.AnalyticsIDs) > 0 {
		out.AddRisk(SEV_INFO, "Found Analytics IDs", "", "", report.AnalyticsIDs)
	}

	if len(report.BitcoinAddresses) > 0 {
		out.AddRisk(SEV_INFO, "Found Bitcoin Addresses", "", "", report.BitcoinAddresses)
	}

	if report.FoundApacheModStatus {
		out.AddRisk(SEV_HIGH, "Apache mod_status is enabled and accessible",
			"Why this is bad: An attacker can gain very valuable information from this internal status page including IP addresses, co-hosted services and user activity.",
			"To fix, disable mod_status or serve it on a different port than the configured hidden service.",
			nil)
	}

	if len(report.RelatedClearnetDomains) > 0 {
		out.AddRisk(SEV_HIGH, "You are hosting a clearnet site on the same server as this onion service!",
			"Why this is bad: This may be intentional, but often isn't. Services are best operated in isolation such that a compromise of one does not mean a compromise of the other.",
			"To fix, host all services on separate infrastructure.",
			report.RelatedClearnetDomains)
	}

	if len(report.RelatedOnionServices) > 0 {
		out.AddRisk(SEV_MEDIUM, "You are hosting multiple onion services on the same server as this onion service!",
			"Why this is bad: This may be intentional, but often isn't. Hidden services are best operated in isolation such that a compromise of one does not mean a compromise of the other.",
			"To fix, host all services on separate infrastructure.",
			report.RelatedOnionServices)
	}

	if len(report.OpenDirectories) > 0 {
		var severity string
		var title string
		if len(report.OpenDirectories) > 10 {
			severity = SEV_MEDIUM
			title = "Large number of open directories were discovered!"
		} else {
			severity = SEV_LOW
			title = "Small number of open directories were discovered!"
		}

		out.AddRisk(severity, title,
			"Why this is bad: Open directories can reveal the existence of files not linked from the sites source code. Most of the time this is benign, but sometimes operators forget to clean up more sensitive folders.",
			"To fix, use .htaccess rules or equivalent to make reading directories listings forbidden. Quick Fix (Disable indexing globally) for Debian / Ubuntu running Apache: a2dismod autoindex as root.",
			report.OpenDirectories)
	}

	if len(report.ExifImages) > 0 {
		var severity string
		var title string
		if len(report.OpenDirectories) > 10 {
			severity = SEV_HIGH
			title = "Large number of images with EXIF metadata were discovered!"
		} else {
			severity = SEV_MEDIUM
			title = "Small number of images with EXIF metadata were discovered!"
		}
		items := []string{}
		for _, image := range report.ExifImages {
			items = append(items, image.Location)
		}
		out.AddRisk(severity, title,
			"Why this is bad: EXIF metadata can itself deanonymize a user or service operator (e.g. GPS location, Name etc.). Or, when combined, can be used to link anonymous identities together.",
			"To fix, re-encode all images to strip EXIF and other metadata.",
			items)
	}

	if report.PrivateKeyDetected {
		out.AddRisk(SEV_CRITICAL, "Hidden service private key is accessible!",
			"Why this is bad: This can be used to impersonate the service at any point in the future.",
			"To fix, generate a new hidden service and make sure the private_key file is not reachable from the web root.",
			nil)
	}
	return out
}

func NewSimpleReport(hiddenService string) *SimpleReport {
	var osr = new(SimpleReport)
	osr.HiddenService = hiddenService
	return osr
}
<commit_msg>Split out simple report checks<commit_after>package report

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/s-rah/onionscan/utils"
)

const SEV_INFO = "info"
const SEV_LOW = "low"
const SEV_MEDIUM = "medium"
const SEV_HIGH = "high"
const SEV_CRITICAL = "critical"

type Risk struct {
	Severity    string   `json:"severity"`
	Title       string   `json:"title"`
	Description string   `json:"description"`
	Fix         string   `json:"fix"`
	Items       []string `json:"items"`
}

type SimpleReport struct {
	HiddenService string `json:"hiddenService"`
	Risks         []Risk `json:"risks"`
}

func (osr *SimpleReport) AddRisk(severity string, title string, description string, fix string, items []string) {
	osr.Risks = append(osr.Risks, Risk{severity, title, description, fix, items})
}

// Format as JSON
func (osr *SimpleReport) Serialize() (string, error) {
	report, err := json.Marshal(osr)
	if err != nil {
		return "", err
	}
	return string(report), nil
}

var risk_levels = map[string]string{
	SEV_INFO:     "\033[094mInfo:\033[0m",
	SEV_LOW:      "\033[093mLow Risk:\033[0m",
	SEV_MEDIUM:   "\033[093mMedium Risk:\033[0m",
	SEV_HIGH:     "\033[091mHigh Risk:\033[0m",
	SEV_CRITICAL: "\033[091mCritical Risk:\033[0m",
}

// Format as human-readable text to be printed to console
func (osr *SimpleReport) Format(width int) (string, error) {
	buffer := bytes.NewBuffer(nil)
	buffer.WriteString("--------------- OnionScan Report ---------------\n")

	buffer.WriteString(fmt.Sprintf("Generating Report for: %s\n\n", osr.HiddenService))
	const indent = "         "

	for _, risk := range osr.Risks {
		buffer.WriteString(risk_levels[risk.Severity] + " " + risk.Title + "\n")
		if len(risk.Description) > 0 {
			buffer.WriteString(indent + utils.FormatParagraphs(risk.Description, width, len(indent)) + "\n")
		}
		if len(risk.Fix) > 0 {
			buffer.WriteString(indent + utils.FormatParagraphs(risk.Fix, width, len(indent)) + "\n")
		}
		if len(risk.Items) > 0 {
			buffer.WriteString(indent + "Items Identified:\n")
			buffer.WriteString("\n")
			for _, item := range risk.Items {
				buffer.WriteString(indent + item + "\n")
			}
		}
		buffer.WriteString("\n")

	}
	return buffer.String(), nil
}

// Interface for SimpleReport checks
type SimpleReportCheck interface {
	Check(out *SimpleReport, report *AnonymityReport)
}

// EmailAddressCheck implementation
type EmailAddressCheck struct{}

func (srt *EmailAddressCheck) Check(out *SimpleReport, report *AnonymityReport) {
	if len(report.EmailAddresses) > 0 {
		out.AddRisk(SEV_INFO, "Found Identities", "", "", report.EmailAddresses)
	}
}

// IPAddressCheck implementation
type IPAddressCheck struct{}

func (srt *IPAddressCheck) Check(out *SimpleReport, report *AnonymityReport) {
	if len(report.IPAddresses) > 0 {
		out.AddRisk(SEV_INFO, "Found IP Addresses", "", "", report.IPAddresses)
	}
}

// AnalyticsIDsCheck implementation
type AnalyticsIDsCheck struct{}

func (srt *AnalyticsIDsCheck) Check(out *SimpleReport, report *AnonymityReport) {
	if len(report.AnalyticsIDs) > 0 {
		out.AddRisk(SEV_INFO, "Found Analytics IDs", "", "", report.AnalyticsIDs)
	}
}

// BitcoinAddressesCheck implementation
type BitcoinAddressesCheck struct{}

func (srt *BitcoinAddressesCheck) Check(out *SimpleReport, report *AnonymityReport) {
	if len(report.BitcoinAddresses) > 0 {
		out.AddRisk(SEV_INFO, "Found Bitcoin Addresses", "", "", report.BitcoinAddresses)
	}

}

// ApacheModStatusCheck implementation
type ApacheModStatusCheck struct{}

func (srt *ApacheModStatusCheck) Check(out *SimpleReport, report *AnonymityReport) {
	if report.FoundApacheModStatus {
		out.AddRisk(SEV_HIGH, "Apache mod_status is enabled and accessible",
			"Why this is bad: An attacker can gain very valuable information from this internal status page including IP addresses, co-hosted services and user activity.",
			"To fix, disable mod_status or serve it on a different port than the configured hidden service.",
			nil)
	}
}

// RelatedClearnetDomainsCheck implementation
type RelatedClearnetDomainsCheck struct{}

func (srt *RelatedClearnetDomainsCheck) Check(out *SimpleReport, report *AnonymityReport) {
	if len(report.RelatedClearnetDomains) > 0 {
		out.AddRisk(SEV_HIGH, "You are hosting a clearnet site on the same server as this onion service!",
			"Why this is bad: This may be intentional, but often isn't. Services are best operated in isolation such that a compromise of one does not mean a compromise of the other.",
			"To fix, host all services on separate infrastructure.",
			report.RelatedClearnetDomains)
	}
}

// RelatedOnionDomainsCheck implementation
type RelatedOnionServicesCheck struct{}

func (srt *RelatedOnionServicesCheck) Check(out *SimpleReport, report *AnonymityReport) {
	if len(report.RelatedOnionServices) > 0 {
		out.AddRisk(SEV_MEDIUM, "You are hosting multiple onion services on the same server as this onion service!",
			"Why this is bad: This may be intentional, but often isn't. Hidden services are best operated in isolation such that a compromise of one does not mean a compromise of the other.",
			"To fix, host all services on separate infrastructure.",
			report.RelatedOnionServices)
	}
}

// OpenDirectoriesCheck implementation
type OpenDirectoriesCheck struct{}

func (srt *OpenDirectoriesCheck) Check(out *SimpleReport, report *AnonymityReport) {
	if len(report.OpenDirectories) > 0 {
		var severity string
		var title string
		if len(report.OpenDirectories) > 10 {
			severity = SEV_MEDIUM
			title = "Large number of open directories were discovered!"
		} else {
			severity = SEV_LOW
			title = "Small number of open directories were discovered!"
		}

		out.AddRisk(severity, title,
			"Why this is bad: Open directories can reveal the existence of files not linked from the sites source code. Most of the time this is benign, but sometimes operators forget to clean up more sensitive folders.",
			"To fix, use .htaccess rules or equivalent to make reading directories listings forbidden. Quick Fix (Disable indexing globally) for Debian / Ubuntu running Apache: a2dismod autoindex as root.",
			report.OpenDirectories)
	}
}

// ExifImagesCheck implementation
type ExifImagesCheck struct{}

func (srt *ExifImagesCheck) Check(out *SimpleReport, report *AnonymityReport) {
	if len(report.ExifImages) > 0 {
		var severity string
		var title string
		if len(report.OpenDirectories) > 10 {
			severity = SEV_HIGH
			title = "Large number of images with EXIF metadata were discovered!"
		} else {
			severity = SEV_MEDIUM
			title = "Small number of images with EXIF metadata were discovered!"
		}
		items := []string{}
		for _, image := range report.ExifImages {
			items = append(items, image.Location)
		}
		out.AddRisk(severity, title,
			"Why this is bad: EXIF metadata can itself deanonymize a user or service operator (e.g. GPS location, Name etc.). Or, when combined, can be used to link anonymous identities together.",
			"To fix, re-encode all images to strip EXIF and other metadata.",
			items)
	}
}

// PrivateKeyCheck implementation
type PrivateKeyCheck struct{}

func (srt *PrivateKeyCheck) Check(out *SimpleReport, report *AnonymityReport) {
	if report.PrivateKeyDetected {
		out.AddRisk(SEV_CRITICAL, "Hidden service private key is accessible!",
			"Why this is bad: This can be used to impersonate the service at any point in the future.",
			"To fix, generate a new hidden service and make sure the private_key file is not reachable from the web root.",
			nil)
	}
}

// Standard checks performed for SimpleReport generation
// Plugins can extend this list by calling RegisterSimpleReportCheck
var checks = []SimpleReportCheck{
	&EmailAddressCheck{},
	&IPAddressCheck{},
	&AnalyticsIDsCheck{},
	&BitcoinAddressesCheck{},
	&ApacheModStatusCheck{},
	&RelatedClearnetDomainsCheck{},
	&RelatedOnionServicesCheck{},
	&OpenDirectoriesCheck{},
	&ExifImagesCheck{},
	&PrivateKeyCheck{},
}

func SummarizeToSimpleReport(report *AnonymityReport) *SimpleReport {
	var out = NewSimpleReport(report.OnionScanReport.HiddenService)
	for _, check := range checks {
		check.Check(out, report)
	}
	return out
}

func NewSimpleReport(hiddenService string) *SimpleReport {
	var osr = new(SimpleReport)
	osr.HiddenService = hiddenService
	return osr
}

func RegisterSimpleReportCheck(check SimpleReportCheck) {
	checks = append(checks, check)
}
<|endoftext|>


<commit_before>package repo

import (
	"math"
	"strings"
	"testing"
	"time"

	"github.com/FlashbackSRS/flashback/cardmodel/mock"
	"github.com/flimzy/testify/require"
)

func TestPrepareBody(t *testing.T) {
	mock.RegisterMock("mock-model")
	require := require.New(t)
	doc := strings.NewReader(testDoc1)
	result, err := prepareBody(Question, 0, "mock-model", doc)
	if err != nil {
		t.Errorf("error preparing body: %s", err)
	}
	require.HTMLEqual(expected1, result, "prepareBody did something funky")
}

var testDoc1 = `<!DOCTYPE html>

<title>FB Card</title>
<base href="https://flashback.ddns.net:4001/">
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline' https://flashback.ddns.net:4001/">

    Question:

instrument

    Question:

instrument

Answer:
instrumento

[sound:pronunciation_es_instrumento.3gp]

    Question:
instrumento

[sound:pronunciation_es_instrumento.3gp]

Answer:

instrument

    `
var expected1 = `<!DOCTYPE html>
<title>FB Card</title>
<base href="https://flashback.ddns.net:4001/"/>
<meta charset="UTF-8"/>
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline' https://flashback.ddns.net:4001/"/>

    Question:

instrument

`

type PrioTest struct {
	Due      time.Time
	Interval time.Duration
	Expected float64
}

var PrioTests = []PrioTest{
	PrioTest{
		Due:      parseTime("2017-01-01 00:00:00"),
		Interval: 24 * time.Hour,
		Expected: 1,
	},
	PrioTest{
		Due:      parseTime("2017-01-01 12:00:00"),
		Interval: 24 * time.Hour,
		Expected: 0.125,
	},
	PrioTest{
		Due:      parseTime("2016-12-31 12:00:00"),
		Interval: 24 * time.Hour,
		Expected: 3.375,
	},
	PrioTest{
		Due:      parseTime("2017-02-01 00:00:00"),
		Interval: 60 * 24 * time.Hour,
		Expected: 0.112912,
	},
	PrioTest{
		Due:      parseTime("2017-01-02 00:00:00"),
		Interval: 24 * time.Hour,
		Expected: 0,
	},
	PrioTest{
		Due:      parseTime("2016-01-02 00:00:00"),
		Interval: 7 * 24 * time.Hour,
		Expected: 150084.109375,
	},
}

func TestPrio(t *testing.T) {
	now := parseTime("2017-01-01 00:00:00")
	for _, test := range PrioTests {
		prio := CardPrio(test.Due, test.Interval, now)
		if math.Abs(float64(prio)-test.Expected) > 0.000001 {
			t.Errorf("%s / %s: Expected priority %f, got %f\n", test.Due, test.Interval, test.Expected, prio)
		}
	}
}

func parseTime(ts string) time.Time {
	t, _ := time.Parse("2006-01-02 15:04:05", ts)
	return t
}
<commit_msg>update test<commit_after>package repo

import (
	"math"
	"strings"
	"testing"
	"time"

	"github.com/FlashbackSRS/flashback/cardmodel/mock"
	"github.com/flimzy/testify/require"
)

func TestPrepareBody(t *testing.T) {
	mock.RegisterMock("mock-model")
	require := require.New(t)
	doc := strings.NewReader(testDoc1)
	result, err := prepareBody(Question, 0, "mock-model", doc)
	if err != nil {
		t.Errorf("error preparing body: %s", err)
	}
	require.HTMLEqual(expected1, result, "prepareBody did something funky")
}

var testDoc1 = `<!DOCTYPE html>

<title>FB Card</title>
<base href="https://flashback.ddns.net:4001/">
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline' https://flashback.ddns.net:4001/">

    Question:

instrument

    Question:

instrument

Answer:
instrumento

[sound:pronunciation_es_instrumento.3gp]

    Question:
instrumento

[sound:pronunciation_es_instrumento.3gp]

Answer:

instrument

    `
var expected1 = `<!DOCTYPE html>
<title>FB Card</title>
<base href="https://flashback.ddns.net:4001/"/>
<meta charset="UTF-8"/>
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline' https://flashback.ddns.net:4001/"/>

    Question:

instrument

`

type PrioTest struct {
	Due      time.Time
	Interval time.Duration
	Expected float64
}

var PrioTests = []PrioTest{
	PrioTest{
		Due:      parseTime("2017-01-01 00:00:00"),
		Interval: 24 * time.Hour,
		Expected: 1,
	},
	PrioTest{
		Due:      parseTime("2017-01-01 12:00:00"),
		Interval: 24 * time.Hour,
		Expected: 0.125,
	},
	PrioTest{
		Due:      parseTime("2016-12-31 12:00:00"),
		Interval: 24 * time.Hour,
		Expected: 3.375,
	},
	PrioTest{
		Due:      parseTime("2017-02-01 00:00:00"),
		Interval: 60 * 24 * time.Hour,
		Expected: 0.112912,
	},
	PrioTest{
		Due:      parseTime("2017-01-02 00:00:00"),
		Interval: 24 * time.Hour,
		Expected: 0,
	},
	PrioTest{
		Due:      parseTime("2016-01-02 00:00:00"),
		Interval: 7 * 24 * time.Hour,
		Expected: 150084.109375,
	},
}

func TestPrio(t *testing.T) {
	now := parseTime("2017-01-01 00:00:00")
	for _, test := range PrioTests {
		prio := CardPrio(&test.Due, &test.Interval, now)
		if math.Abs(float64(prio)-test.Expected) > 0.000001 {
			t.Errorf("%s / %s: Expected priority %f, got %f\n", test.Due, test.Interval, test.Expected, prio)
		}
	}
}

func parseTime(ts string) time.Time {
	t, _ := time.Parse("2006-01-02 15:04:05", ts)
	return t
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

func getVendorSubmodules() (map[string]string, error) {
	output, err := execute(
		exec.Command("git", "submodule", "status"),
	)
	if err != nil {
		return nil, err
	}

	vendors := map[string]string{}

	lines := strings.Split(output, "\n")
	for _, line := range lines {
		if line == "" {
			continue
		}

		parts := strings.Split(strings.TrimLeft(line, " -"), " ")
		if len(parts) >= 2 {
			path := parts[1]
			commit := parts[0]
			if strings.HasPrefix(path, "vendor/") {
				path = strings.TrimPrefix(path, "vendor/")
				vendors[path] = commit
			}
		}
	}

	return vendors, nil
}

func addVendorSubmodule(importpath string) error {
	var (
		target   = "vendor/" + importpath
		prefixes = []string{
			"https://",
			"git+ssh://",
			"git://",
		}

		errs []string
	)

	for _, prefix := range prefixes {
		var url string
		if prefix == "https://" {
			var err error
			url, err = getHttpsURLForImportPath(importpath)
			if err != nil {
				errs = append(errs, err.Error())
				continue
			}
		} else {
			url = prefix + importpath
		}

		_, err := execute(
			exec.Command("git", "submodule", "add", "-f", url, target),
		)
		if err == nil {
			return nil
		}

		errs = append(errs, err.Error())
	}

	return errors.New(strings.Join(errs, "\n"))
}

// NOTE: This list is copied from
// https://github.com/golang/go/blob/10538a8f9e2e718a47633ac5a6e90415a2c3f5f1/src/cmd/go/vcs.go#L821-L861
var wellKnownSites = []string{
	"github.com/",
	"bitbucket.org/",
	"hub.jazz.net/git/",
	"git.apache.org/",
	"git.openstack.org/",
}

func getHttpsURLForImportPath(importpath string) (url string, err error) {
	url = "https://" + importpath
	for _, site := range wellKnownSites {
		if strings.HasPrefix(importpath, site) {
			return url, nil
		}
	}

	// NOTE: Parse <meta name="go-import" content="import-prefix vcs repo-root">
	// For detail, see the output of "go help importpath"
	var doc *goquery.Document
	doc, err = goquery.NewDocument(url)
	if err != nil {
		return
	}
	doc.Find("meta[name=go-import]").Each(func(_ int, selection *goquery.Selection) {
		if err != nil {
			return
		}
		content, exists := selection.Attr("content")
		if !exists {
			err = fmt.Errorf(`"content" attribute not found in meta name="go-import" at %s`, url)
			return
		}
		terms := strings.Fields(content)
		if len(terms) != 3 {
			err = fmt.Errorf(`invalid formatted "content" attribute in meta name="go-import" at %s`, url)
			return
		}
		prefix := terms[0]
		vcs := terms[1]
		repoRoot := terms[2]
		if strings.HasPrefix(importpath, prefix) && vcs == "git" {
			url = repoRoot
		}
	})
	if err != nil {
		return "", err
	}
	return url, nil
}

func removeVendorSubmodule(importpath string) error {
	vendor := "vendor/" + importpath

	_, err := execute(
		exec.Command("git", "submodule", "deinit", "-f", vendor),
	)
	if err != nil {
		return fmt.Errorf(
			"can't deinit submodule: %s", err,
		)
	}

	_, err = execute(
		exec.Command("git", "rm", "--force", vendor),
	)
	if err != nil {
		return fmt.Errorf(
			"can't remove submodule directory: %s", err,
		)
	}

	_, err = execute(
		exec.Command("rm", "-r", filepath.Join(".git", "modules", vendor)),
	)
	if err != nil {
		return fmt.Errorf(
			"can't remove submodule directory in .git/modules: %s", err,
		)
	}

	return nil
}

func updateVendorSubmodule(importpath string) error {
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}

	cmd := exec.Command("git", "pull", "origin", "master")
	cmd.Dir = filepath.Join(cwd, "vendor", importpath)

	_, err = execute(cmd)

	return err
}

func getRootImportpath(importpath string) (string, error) {
	pkg, err := build.Import(importpath, "", build.IgnoreVendor)
	if err != nil {
		return "", err
	}

	cmd := exec.Command("git", "rev-parse", "--show-toplevel")
	cmd.Dir = filepath.Join(pkg.SrcRoot, importpath)

	rootdir, err := execute(cmd)
	if err != nil {
		return "", err
	}

	return strings.Trim(
		strings.TrimSpace(strings.TrimPrefix(rootdir, pkg.SrcRoot)),
		"/",
	), nil
}
<commit_msg>Move wellKnownSites variable before functions<commit_after>package main

import (
	"errors"
	"fmt"
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

// NOTE: This list is copied from
// https://github.com/golang/go/blob/10538a8f9e2e718a47633ac5a6e90415a2c3f5f1/src/cmd/go/vcs.go#L821-L861
var wellKnownSites = []string{
	"github.com/",
	"bitbucket.org/",
	"hub.jazz.net/git/",
	"git.apache.org/",
	"git.openstack.org/",
}

func getVendorSubmodules() (map[string]string, error) {
	output, err := execute(
		exec.Command("git", "submodule", "status"),
	)
	if err != nil {
		return nil, err
	}

	vendors := map[string]string{}

	lines := strings.Split(output, "\n")
	for _, line := range lines {
		if line == "" {
			continue
		}

		parts := strings.Split(strings.TrimLeft(line, " -"), " ")
		if len(parts) >= 2 {
			path := parts[1]
			commit := parts[0]
			if strings.HasPrefix(path, "vendor/") {
				path = strings.TrimPrefix(path, "vendor/")
				vendors[path] = commit
			}
		}
	}

	return vendors, nil
}

func addVendorSubmodule(importpath string) error {
	var (
		target   = "vendor/" + importpath
		prefixes = []string{
			"https://",
			"git+ssh://",
			"git://",
		}

		errs []string
	)

	for _, prefix := range prefixes {
		var url string
		if prefix == "https://" {
			var err error
			url, err = getHttpsURLForImportPath(importpath)
			if err != nil {
				errs = append(errs, err.Error())
				continue
			}
		} else {
			url = prefix + importpath
		}

		_, err := execute(
			exec.Command("git", "submodule", "add", "-f", url, target),
		)
		if err == nil {
			return nil
		}

		errs = append(errs, err.Error())
	}

	return errors.New(strings.Join(errs, "\n"))
}

func getHttpsURLForImportPath(importpath string) (url string, err error) {
	url = "https://" + importpath
	for _, site := range wellKnownSites {
		if strings.HasPrefix(importpath, site) {
			return url, nil
		}
	}

	// NOTE: Parse <meta name="go-import" content="import-prefix vcs repo-root">
	// For detail, see the output of "go help importpath"
	var doc *goquery.Document
	doc, err = goquery.NewDocument(url)
	if err != nil {
		return
	}
	doc.Find("meta[name=go-import]").Each(func(_ int, selection *goquery.Selection) {
		if err != nil {
			return
		}
		content, exists := selection.Attr("content")
		if !exists {
			err = fmt.Errorf(`"content" attribute not found in meta name="go-import" at %s`, url)
			return
		}
		terms := strings.Fields(content)
		if len(terms) != 3 {
			err = fmt.Errorf(`invalid formatted "content" attribute in meta name="go-import" at %s`, url)
			return
		}
		prefix := terms[0]
		vcs := terms[1]
		repoRoot := terms[2]
		if strings.HasPrefix(importpath, prefix) && vcs == "git" {
			url = repoRoot
		}
	})
	if err != nil {
		return "", err
	}
	return url, nil
}

func removeVendorSubmodule(importpath string) error {
	vendor := "vendor/" + importpath

	_, err := execute(
		exec.Command("git", "submodule", "deinit", "-f", vendor),
	)
	if err != nil {
		return fmt.Errorf(
			"can't deinit submodule: %s", err,
		)
	}

	_, err = execute(
		exec.Command("git", "rm", "--force", vendor),
	)
	if err != nil {
		return fmt.Errorf(
			"can't remove submodule directory: %s", err,
		)
	}

	_, err = execute(
		exec.Command("rm", "-r", filepath.Join(".git", "modules", vendor)),
	)
	if err != nil {
		return fmt.Errorf(
			"can't remove submodule directory in .git/modules: %s", err,
		)
	}

	return nil
}

func updateVendorSubmodule(importpath string) error {
	cwd, err := os.Getwd()
	if err != nil {
		return err
	}

	cmd := exec.Command("git", "pull", "origin", "master")
	cmd.Dir = filepath.Join(cwd, "vendor", importpath)

	_, err = execute(cmd)

	return err
}

func getRootImportpath(importpath string) (string, error) {
	pkg, err := build.Import(importpath, "", build.IgnoreVendor)
	if err != nil {
		return "", err
	}

	cmd := exec.Command("git", "rev-parse", "--show-toplevel")
	cmd.Dir = filepath.Join(pkg.SrcRoot, importpath)

	rootdir, err := execute(cmd)
	if err != nil {
		return "", err
	}

	return strings.Trim(
		strings.TrimSpace(strings.TrimPrefix(rootdir, pkg.SrcRoot)),
		"/",
	), nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"sync"
)

// Maps are not concurrency safe, so we need a mutex to manage access to the map
var allLobbiesMutex = &sync.Mutex{}

// Map from every course code to a lobby
var allLobbies = make(map[string]lobby)

func getALobby(courseCode string) lobby {
	allLobbiesMutex.Lock()
	// See if the allLobbies map contains our desired lobby
	someLobby, ok := allLobbies[courseCode]
	if !ok {
		// Construct a new lobby struct, since it hasn't been created yet
		res, err := db.Exec("INSERT INTO lobbies(id, course_code) VALUES(?, ?)", nil, courseCode)
		if err != nil {
			fmt.Println("Error inserting lobby into database", err)
		}
		channelId, err := res.LastInsertId()
		if err != nil {
			fmt.Println("Error getting lobby id")
		}

			someLobby = lobby{
				clients:          make(map[*wsClient]bool),
				broadcast:        make(chan *internalMessage),
				register:         make(chan *wsClient),
				deregister:       make(chan *wsClient),
				channelId:        channelId,
				nextMessageId:    1,
				nextMessageMutex: &sync.Mutex{},
			}
			allLobbies[courseCode] = someLobby
		}
		allLobbies[courseCode] = someLobby
	}
	go someLobby.serveLobby()
	allLobbiesMutex.Unlock()
	return someLobby
}

type lobby struct {
	// A list of our clients, to which we broadcast messages
	// Boolean because we need something, but we don't actually care
	clients map[*wsClient]bool

	// The channel on which we receive broadcast messages (from a particular client)
	broadcast chan *internalMessage

	// The channel on which we receive register requests (from a new websocket connection)
	register chan *wsClient

	// The channel on which we receive deregister requests (from a websocket CLOSE message)
	deregister chan *wsClient

	// Number that is uniquely assigned to this channel
	channelId int64

	// Next message id for this particular channel
	nextMessageId int64

	// Mutex for accessing the nextMessageId variable
	// We need this so we can store nextMessageId in a temporary variable,
	// then increment it, then return the old value
	// We could probably use atomic primitives, e.g. something like getAndIncrement(),
	// but this will solve our problem
	nextMessageMutex *sync.Mutex
}

type internalMessage struct {
	// Actual contents of the message
	MessageText []byte

	// The display name set by the user
	MessageDisplayName []byte

	// The internal author id for our use
	MessageAuthorId int64

	// Message id assigned by the database, but this is also sent to the frontend
	MessageId int64
}

type externalMessage struct {
	// Actual contents of the message
	MessageText string

	// The display name set by the user
	MessageDisplayName string

	// Messaged id assigned by the database, used by the front-end to intelligently re-order messages
	// and potentially re-request missing ones
	MessageId int64
}

func (theLobby *lobby) serveLobby() {
	for {
		// Use this magic select statement syntax, where instead of blocking
		// on a channel, it only chooses the one which is ready!
		select {
		case someClient := <-theLobby.register:
			theLobby.clients[someClient] = true

		case someClient := <-theLobby.deregister:
			// Remove the client from the map
			delete(theLobby.clients, someClient)
			// Close the messages channel to prevent a resource leak
			close(someClient.messagesForClient)

		case msg := <-theLobby.broadcast:
			for someClient := range theLobby.clients {
				// TODO: Check the author and do magic (replace name with "You")
				outgoingMessage := externalMessage{MessageText: string(msg.MessageText), MessageDisplayName: string(msg.MessageDisplayName), MessageId: msg.MessageId}
				stringifiedJson, err := json.Marshal(outgoingMessage)
				if err != nil {
					log.Println("Error marshalling outgoing message", err)
					// Break because we don't want to try the same bogus message with all the users - just exit now
					break
				}
				select {
				case someClient.messagesForClient <- stringifiedJson:
					// The above command sent the message, so we're done!
				default:
					// This is an error condition, let's remove this client
					theLobby.deregister <- someClient
				}

			}
		}

	}
}

// This keeps a running counter of the messages in each room, and concurrency safely
// gives the next number when requested
func (theLobby *lobby) getNextMessageId() int64 {
	theLobby.nextMessageMutex.Lock()
	nextId := theLobby.nextMessageId
	theLobby.nextMessageId += 1
	theLobby.nextMessageMutex.Unlock()
	return nextId
}
<commit_msg>Lazily construct lobbies as required<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"sync"
)

// Maps are not concurrency safe, so we need a mutex to manage access to the map
var allLobbiesMutex = &sync.Mutex{}

// Map from every course code to a lobby
var allLobbies = make(map[string]lobby)

func getALobby(courseCode string) lobby {
	allLobbiesMutex.Lock()
	// See if the allLobbies map contains our desired lobby
	someLobby, ok := allLobbies[courseCode]

	if !ok {
		var wasInDatabase = false
		rows, err := db.Query("SELECT * FROM lobbies WHERE course_code = ?", courseCode)
		if err != nil {
			log.Println("Error searching for course in database")
		}
		defer rows.Close()
		for rows.Next() {
			var id int64
			var course_code string

			err = rows.Scan(&id, &course_code)
			if err != nil {
				log.Println("Scan error", err)
				break
			}

			someLobby = lobby{
				clients:    make(map[*wsClient]bool),
				broadcast:  make(chan *internalMessage),
				register:   make(chan *wsClient),
				deregister: make(chan *wsClient),
				channelId:  id,
				// TODO: Store next message id in database
				nextMessageId:    1,
				nextMessageMutex: &sync.Mutex{},
			}
			wasInDatabase = true
			allLobbies[courseCode] = someLobby
		}
		if !wasInDatabase {
			// Construct a new lobby struct, since it hasn't been created yet
			res, err := db.Exec("INSERT INTO lobbies(id, course_code) VALUES(?, ?)", nil, courseCode)
			if err != nil {
				fmt.Println("Error inserting lobby into database", err)
			}
			channelId, err := res.LastInsertId()
			if err != nil {
				fmt.Println("Error getting lobby id")
			}

			someLobby = lobby{
				clients:          make(map[*wsClient]bool),
				broadcast:        make(chan *internalMessage),
				register:         make(chan *wsClient),
				deregister:       make(chan *wsClient),
				channelId:        channelId,
				nextMessageId:    1,
				nextMessageMutex: &sync.Mutex{},
			}
			allLobbies[courseCode] = someLobby
		}

	}
	go someLobby.serveLobby()
	allLobbiesMutex.Unlock()
	return someLobby
}

type lobby struct {
	// A list of our clients, to which we broadcast messages
	// Boolean because we need something, but we don't actually care
	clients map[*wsClient]bool

	// The channel on which we receive broadcast messages (from a particular client)
	broadcast chan *internalMessage

	// The channel on which we receive register requests (from a new websocket connection)
	register chan *wsClient

	// The channel on which we receive deregister requests (from a websocket CLOSE message)
	deregister chan *wsClient

	// Number that is uniquely assigned to this channel
	channelId int64

	// Next message id for this particular channel
	nextMessageId int64

	// Mutex for accessing the nextMessageId variable
	// We need this so we can store nextMessageId in a temporary variable,
	// then increment it, then return the old value
	// We could probably use atomic primitives, e.g. something like getAndIncrement(),
	// but this will solve our problem
	nextMessageMutex *sync.Mutex
}

type internalMessage struct {
	// Actual contents of the message
	MessageText []byte

	// The display name set by the user
	MessageDisplayName []byte

	// The internal author id for our use
	MessageAuthorId int64

	// Message id assigned by the database, but this is also sent to the frontend
	MessageId int64
}

type externalMessage struct {
	// Actual contents of the message
	MessageText string

	// The display name set by the user
	MessageDisplayName string

	// Messaged id assigned by the database, used by the front-end to intelligently re-order messages
	// and potentially re-request missing ones
	MessageId int64
}

func (theLobby *lobby) serveLobby() {
	for {
		// Use this magic select statement syntax, where instead of blocking
		// on a channel, it only chooses the one which is ready!
		select {
		case someClient := <-theLobby.register:
			theLobby.clients[someClient] = true

		case someClient := <-theLobby.deregister:
			// Remove the client from the map
			delete(theLobby.clients, someClient)
			// Close the messages channel to prevent a resource leak
			close(someClient.messagesForClient)

		case msg := <-theLobby.broadcast:
			for someClient := range theLobby.clients {
				// TODO: Check the author and do magic (replace name with "You")
				outgoingMessage := externalMessage{MessageText: string(msg.MessageText), MessageDisplayName: string(msg.MessageDisplayName), MessageId: msg.MessageId}
				stringifiedJson, err := json.Marshal(outgoingMessage)
				if err != nil {
					log.Println("Error marshalling outgoing message", err)
					// Break because we don't want to try the same bogus message with all the users - just exit now
					break
				}
				select {
				case someClient.messagesForClient <- stringifiedJson:
					// The above command sent the message, so we're done!
				default:
					// This is an error condition, let's remove this client
					theLobby.deregister <- someClient
				}

			}
		}

	}
}

// This keeps a running counter of the messages in each room, and concurrency safely
// gives the next number when requested
func (theLobby *lobby) getNextMessageId() int64 {
	theLobby.nextMessageMutex.Lock()
	nextId := theLobby.nextMessageId
	theLobby.nextMessageId += 1
	theLobby.nextMessageMutex.Unlock()
	return nextId
}
<|endoftext|>


<commit_before>// selection sort

package main

import (
	"fmt"
	"ashumeow/meow_sort"
)

func main() {
	// still more to code...
}<commit_msg>add selection sort<commit_after>// selection sort

package main

import (
	"fmt"
	"ashumeow/meow_sort"
)

func main() {
	meow := meow_sort.RandArray(10)
	fmt.Println("Given array is: ", meow)
	fmt.Println("")

	var minimum int = 0
	var temp int = 0

	for x := 0; x < len(meow_sort); x++ {
		minimum = x
		for xx := x + 1; xx < len(meow); xx++ {
			if meow[xx] < meow[minimum] {
				minimum = xx
			}
		}
		temp = meow[x]
		meow[x] = meow[minimum]
		meow[minimum] = temp
	}
	fmt.Println("Sorted array is: ", meow)
}<|endoftext|>