<commit_before>// Challenge 37 - Break SRP with a zero key
// http://cryptopals.com/sets/5/challenges/37

package cryptopals

import "math/big"

type challenge37 struct {
}

func (challenge37) Client(net Network) bool {
	A := big.NewInt(0)

	net.Write(A)
	net.Read()
	net.Read()

	S := A
	K := sha256Digest(S.Bytes())

	mac := hmacSHA256(K, S.Bytes())
	net.Write(mac)

	return net.Read().(bool)
}

func (challenge37) Server(params srpParams, info srpClientInfo, net Network) bool {
	return challenge36{}.Server(params, info, net)
}
<commit_msg>Fix client to hmac salt instead of secret<commit_after>// Challenge 37 - Break SRP with a zero key
// http://cryptopals.com/sets/5/challenges/37

package cryptopals

import "math/big"

type challenge37 struct {
}

func (challenge37) Client(net Network) bool {
	A := big.NewInt(0)
	net.Write(A)

	s := readBytes(net)
	net.Read()

	S := A
	K := sha256Digest(S.Bytes())

	mac := hmacSHA256(K, s)
	net.Write(mac)

	return net.Read().(bool)
}

func (challenge37) Server(params srpParams, info srpClientInfo, net Network) bool {
	return challenge36{}.Server(params, info, net)
}
<|endoftext|>


<commit_before>package rest

import (
	"bufio"
	"io"
	"mime/multipart"
	"os"
)

type FormFile struct {
	FileName    string
	ContentType string
	File        multipart.File
}

func (this *FormFile) save(path string, limit string) int64 {
	defer this.File.Close()
	of, e := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
	if nil != e {
		panic(e.Error())
	}
	defer of.Close()
	bof := bufio.NewWriterSize(of, 8*1024)
	writeLen, e := io.Copy(bof, this.File)
	if nil != e {
		panic(e.Error())
	}
	return writeLen
}
<commit_msg>Add Limit method<commit_after>package rest

import (
	"bufio"
	"bytes"
	"io"
	"mime/multipart"
	"os"
)

type FormFile struct {
	FileName    string
	ContentType string
	File        multipart.File
	limit       int64
}

func (this *FormFile) Limit(len int64) *FormFile {
	this.limit = len
	return this
}

func (this *FormFile) Save(path string, limit int) int64 {
	defer this.File.Close()
	of, e := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
	if nil != e {
		panic(e.Error())
	}
	defer of.Close()
	buffer := bytes.n
	bof := bufio.NewWriterSize(of, 8*1024)
	writeLen, e := io.Copy(bof, this.File)
	if nil != e {
		panic(e.Error())
	}
	return writeLen
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Boise State University All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"context"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"path"
	"regexp"
	"sync"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
)

const (
	// jupyterNotebookImageMatch matches images from jupyter/docker-stacks, for
	// example: jupyter/minimal-notebook
	jupyterNotebookImageMatch = `[a-zA-Z0-9]+/[a-zA-Z0-9]+-notebook(:[a-zA-Z0-9]*)?`

	// allImageMatch applies no filter
	allImageMatch = `.*`

	// defaultContainerLifetime is used if a lifetime is not provided
	defaultContainerLifetime = time.Minute * 10

	// defaultMaxContainers governs the port set size and triggers reclamation
	defaultMaxContainers = 100

	// collectionFraction is the fraction of lifetime to collect containers.  For
	// example 4 collects every 1/4 of the container lifetime.
	collectionFraction = 4
)

// tempNotebook holds context for a single container
type tempNotebook struct {
	// guard struct (only used for lastAccessed right now)
	sync.Mutex
	// id is the docker container id.
	id string
	// hash is  a random generated hash that is used in the path of the server.
	hash string
	// imageName is the name of the image used to start the container
	imageName string
	// lastAccessed is when the container was used last.
	lastAccessed time.Time
	// port is the passthrough port for the reverse proxy.
	port int
	// userEmail is the email of the user who created this container.
	userEmail string
}

// notebookPool holds data regarding running notebooks.
type notebookPool struct {
	// guards the entire struct
	sync.Mutex

	// availableImages is a list of docker images that installed on the machine,
	// and match the imageMatch expression
	availableImages map[string]struct{}

	// imageMatch filters available images by name
	imageMatch *regexp.Regexp

	// containerMap is stores the contexts for the containers.
	containerMap map[string]*tempNotebook

	// portSet holds free ports
	portSet *portRange

	// maxContainers governs the port set size and resource reclamation.
	maxContainers int

	// containerLifetime governs when the container resources are reclaimed.
	containerLifetime time.Duration

	// disableJupyterAuth controls using an auth token
	disableJupyterAuth bool

	// token is the security token for auto-auth
	token string

	// killCollection stops the automated resource reclamation
	killCollection chan struct{}

	// lastCollMu guards the time for reclamation.  It is used infrequently, and
	// we don't need to lock the whole object.
	lastCollMu sync.Mutex

	// lastCollection is the timestamp the last time the containers were
	// reclaimed.
	lastCollection time.Time

	// deregisterMux is a channel for sending a path that needs to be
	// de-registered from the server mux.
	deregisterMux chan string
}

// errNotebookPoolFull indicates the pool is at maxContainers
var errNotebookPoolFull = errors.New("container pool hit max size limit")

// newNotebookPool creates a notebookPool and sets defaults, overriding some
// with passed arguments.
func newNotebookPool(imageRegexp string, maxContainers int, lifetime time.Duration) (*notebookPool, error) {
	if imageRegexp == "" {
		imageRegexp = jupyterNotebookImageMatch
	}
	if int64(lifetime) <= 0 {
		lifetime = defaultContainerLifetime
	}
	if maxContainers < 1 {
		maxContainers = defaultMaxContainers
	}
	imageMatch, err := regexp.Compile(imageRegexp)
	if err != nil {
		return nil, err
	}
	imageMap := map[string]struct{}{}
	cli, err := client.NewEnvClient()
	if err != nil {
		return nil, err
	}
	images, err := cli.ImageList(context.Background(), types.ImageListOptions{})
	if err != nil {
		return nil, err
	}
	for _, image := range images {
		if len(image.RepoTags) < 1 || !imageMatch.MatchString(image.RepoTags[0]) {
			continue
		}
		log.Printf("found image %s", image.RepoTags[0])
		imageMap[image.RepoTags[0]] = struct{}{}
	}
	pool := &notebookPool{
		availableImages:   imageMap,
		imageMatch:        imageMatch,
		containerMap:      make(map[string]*tempNotebook),
		portSet:           newPortRange(8000, maxContainers),
		maxContainers:     maxContainers,
		containerLifetime: lifetime,
		killCollection:    make(chan struct{}),
		deregisterMux:     make(chan string),
	}
	pool.startCollector(time.Duration(int64(lifetime) / collectionFraction))
	pool.lastCollMu.Lock()
	pool.lastCollection = time.Now()
	pool.lastCollMu.Unlock()
	return pool, nil
}

// defaultHashSize is used for the unique hash generation
const defaultHashSize = 32

// newHash makes a n byte hash and returns the hex encoding.
func newHash(n int) string {
	b := make([]byte, n)
	_, err := rand.Read(b[:])
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", b)
}

// newNotebook initializes and sets values for a new notebook.
func (p *notebookPool) newNotebook(image string, pull bool, email string) (*tempNotebook, error) {
	ctx := context.Background()
	cli, err := client.NewEnvClient()
	if err != nil {
		log.Print(err)
		return nil, err
	}

	// TODO(kyle): possibly provide tag support
	if pull {
		log.Printf("pulling container %s", image)
		ctx, cancel := context.WithTimeout(ctx, 120*time.Second)
		defer cancel()
		out, err := cli.ImagePull(ctx, image, types.ImagePullOptions{})
		if err != nil {
			log.Print(err)
			return nil, err
		}
		defer out.Close()
		s := bufio.NewScanner(out)
		var ps dockerPullStatus
		for s.Scan() {
			err := json.Unmarshal([]byte(s.Text()), &ps)
			if err != nil {
				log.Print(err)
			}
			log.Print(ps.Progress)
		}
	}

	hash := newHash(defaultHashSize)

	port, err := p.portSet.Acquire()
	if err != nil {
		return nil, err
	}
	portString := fmt.Sprintf("%d", port)

	tokenArg := fmt.Sprintf(`--NotebookApp.token="%s"`, p.token)
	var env []string
	if p.disableJupyterAuth {
		tokenArg = fmt.Sprintf(`--NotebookApp.token=""`)
	} else {
		env = []string{fmt.Sprintf("CONFIGPROXY_AUTH_TOKEN=%s", p.token)}
	}
	var pSet = nat.PortSet{}
	pt, err := nat.NewPort("tcp", portString)
	pSet[pt] = struct{}{}
	containerConfig := container.Config{
		Hostname: "0.0.0.0",
		User:     "jovyan",
		Cmd: []string{`jupyter`,
			`notebook`,
			`--no-browser`,
			`--port`,
			portString,
			`--ip=0.0.0.0`,
			fmt.Sprintf("--NotebookApp.base_url=%s", path.Join("/book", hash)),
			`--NotebookApp.port_retries=0`,
			tokenArg,
			`--NotebookApp.disable_check_xsrf=True`,
		},
		Env:          env,
		Image:        image,
		ExposedPorts: pSet,
	}

	hostConfig := container.HostConfig{
		NetworkMode: "host",
	}

	resp, err := cli.ContainerCreate(ctx, &containerConfig, &hostConfig, nil, "")
	if err != nil {
		p.portSet.Drop(port)
		return nil, err
	}

	if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
		p.portSet.Drop(port)
		return nil, err
	}
	log.Printf("created container: %s", resp.ID)
	t := &tempNotebook{
		id:           resp.ID,
		hash:         hash,
		imageName:    image,
		lastAccessed: time.Now(),
		port:         port,
		userEmail:    email,
	}
	err = p.addNotebook(t)
	if err != nil {
		log.Print(err)
		p.portSet.Drop(port)
		return nil, err
	}
	return t, nil
}

// size returns the size of the containerMap with appropriate locks
func (p *notebookPool) size() int {
	p.Lock()
	n := len(p.containerMap)
	p.Unlock()
	return n
}

// addNotebook adds a tempNotebook to the containerMap, if there is room.
func (p *notebookPool) addNotebook(t *tempNotebook) error {
	n := p.size()
	log.Printf("pool size: %d of %d", n+1, p.maxContainers)
	if p.size()+1 > p.maxContainers {
		p.releaseContainers(false, true)
	}
	if p.size()+1 > p.maxContainers {
		return errNotebookPoolFull
	}
	p.Lock()
	p.containerMap[t.hash] = t
	p.Unlock()
	return nil
}

// stopAndKillContainer requests the stopping (docker stop) and the removal of
// the container (docker rm).  Errors are logged, but not returned and rm is
// always called.
func (p *notebookPool) stopAndKillContainer(id string) {
	d := time.Minute
	cli, err := client.NewEnvClient()
	if err != nil {
		log.Print(err)
	}
	ctx := context.Background()
	if err := cli.ContainerStop(ctx, id, &d); err != nil {
		log.Print(err)
	}
	if err := cli.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true}); err != nil {
		log.Print(err)
	}
}

// activeNotebooks fetchs copies of the tempNotebooks and returns them as a
// slice.  The lock is obviously invalid.
func (p *notebookPool) activeNotebooks() []tempNotebook {
	nbs := make([]tempNotebook, p.size())
	p.Lock()
	i := 0
	for k := range p.containerMap {
		c := p.containerMap[k]
		nbs[i] = tempNotebook{
			id:           c.id,
			hash:         c.hash,
			imageName:    c.imageName,
			lastAccessed: c.lastAccessed,
			port:         c.port,
		}
		i++
	}
	p.Unlock()
	return nbs
}

// zombieNotebooks queries docker for containers that aren't under our
// supervision.  These can block ports assigned to our containers.
func (p *notebookPool) zombieContainers() ([]types.Container, error) {
	var cs []types.Container
	ids := map[string]struct{}{}
	p.Lock()
	for _, c := range p.containerMap {
		ids[c.id] = struct{}{}
	}
	p.Unlock()
	cli, err := client.NewEnvClient()
	opts := types.ContainerListOptions{}
	containers, err := cli.ContainerList(context.Background(), opts)
	if err != nil {
		return nil, err
	}
	for _, c := range containers {
		// If we manage it, leave it be
		if _, ok := ids[c.ID]; ok {
			continue
		}
		cs = append(cs, c)
	}
	return cs, nil
}

// nextCollection returns when the collector is run again
func (p *notebookPool) NextCollection() time.Time {
	p.lastCollMu.Lock()
	t := p.lastCollection.Add(p.containerLifetime / collectionFraction)
	p.lastCollMu.Unlock()
	return t
}

// startCollector launches a goroutine that checks for expired containers at
// interval d.  d is typically set to containerLifetime / collectionFraction.  Call
// stopCollector to stop the reclamation.
func (p *notebookPool) startCollector(d time.Duration) {
	go func() {
		ticker := time.NewTicker(d)
		for {
			select {
			case <-ticker.C:
				p.releaseContainers(false, true)
				p.lastCollMu.Lock()
				p.lastCollection = time.Now()
				p.lastCollMu.Unlock()
			case <-p.killCollection:
				ticker.Stop()
				return
			}
		}
	}()
}

// stopCollector sends a message on a channel to kill the auto reclamation.
func (p *notebookPool) stopCollector() {
	p.killCollection <- struct{}{}
}

// releaseContainers checks for expired containers and frees them from the
// containerMap.  It also frees the port in the portSet.  If force is true, age
// is ignored.
func (p *notebookPool) releaseContainers(force, async bool) error {
	p.Lock()
	trash := []tempNotebook{}
	for _, c := range p.containerMap {
		c.Lock()
		age := time.Now().Sub(c.lastAccessed)
		if age.Seconds() > p.containerLifetime.Seconds() || force {
			log.Printf("age: %v\n", age)
			//trash = append(trash, *c)
			trash = append(trash, tempNotebook{
				id:           c.id,
				hash:         c.hash,
				imageName:    c.imageName,
				lastAccessed: c.lastAccessed,
				port:         c.port,
			})
		}
		c.Unlock()
	}
	p.Unlock()
	for i := 0; i < len(trash); i++ {
		type nbCopy struct {
			id           string
			hash         string
			imageName    string
			lastAccessed time.Time
			port         int
		}
		c := nbCopy{
			id:           trash[i].id,
			hash:         trash[i].hash,
			imageName:    trash[i].imageName,
			lastAccessed: trash[i].lastAccessed,
			port:         trash[i].port,
		}
		f := func(c nbCopy) {
			log.Printf("attempting to release container %s last accessed at %v", c.id, c.lastAccessed)
			p.stopAndKillContainer(c.id)
			p.portSet.Drop(c.port)
			p.Lock()
			delete(p.containerMap, c.hash)
			p.Unlock()
			// This isn't very elegant, but we couldn't delete the pattern from the mux
			// before, but now we can with the vendored/updated copy in mux.go.  We add
			// a trailing slash when we register the path, so we must add it here too.
			p.deregisterMux <- path.Join("/book", c.hash) + "/"
		}
		if async {
			go f(c)
		} else {
			f(c)
		}
	}
	return nil
}

// killZombieContainers stops and kills any docker containers that aren't under
// out supervision.
//
// FIXME(kyle): not currently called at any time, when, why, etc...
func (p *notebookPool) killZombieContainers() error {
	zombies, err := p.zombieContainers()
	if err != nil {
		return err
	}
	for _, c := range zombies {
		p.stopAndKillContainer(c.ID)
	}
	return nil
}
<commit_msg>reduce the chance of double locking<commit_after>// Copyright (c) 2017, Boise State University All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"context"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"path"
	"regexp"
	"sync"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
)

const (
	// jupyterNotebookImageMatch matches images from jupyter/docker-stacks, for
	// example: jupyter/minimal-notebook
	jupyterNotebookImageMatch = `[a-zA-Z0-9]+/[a-zA-Z0-9]+-notebook(:[a-zA-Z0-9]*)?`

	// allImageMatch applies no filter
	allImageMatch = `.*`

	// defaultContainerLifetime is used if a lifetime is not provided
	defaultContainerLifetime = time.Minute * 10

	// defaultMaxContainers governs the port set size and triggers reclamation
	defaultMaxContainers = 100

	// collectionFraction is the fraction of lifetime to collect containers.  For
	// example 4 collects every 1/4 of the container lifetime.
	collectionFraction = 4
)

// tempNotebook holds context for a single container
type tempNotebook struct {
	// guard struct (only used for lastAccessed right now)
	sync.Mutex
	// id is the docker container id.
	id string
	// hash is  a random generated hash that is used in the path of the server.
	hash string
	// imageName is the name of the image used to start the container
	imageName string
	// lastAccessed is when the container was used last.
	lastAccessed time.Time
	// port is the passthrough port for the reverse proxy.
	port int
	// userEmail is the email of the user who created this container.
	userEmail string
}

// notebookPool holds data regarding running notebooks.
type notebookPool struct {
	// guards the entire struct
	sync.Mutex

	// availableImages is a list of docker images that installed on the machine,
	// and match the imageMatch expression
	availableImages map[string]struct{}

	// imageMatch filters available images by name
	imageMatch *regexp.Regexp

	// containerMap is stores the contexts for the containers.
	containerMap map[string]*tempNotebook

	// portSet holds free ports
	portSet *portRange

	// maxContainers governs the port set size and resource reclamation.
	maxContainers int

	// containerLifetime governs when the container resources are reclaimed.
	containerLifetime time.Duration

	// disableJupyterAuth controls using an auth token
	disableJupyterAuth bool

	// token is the security token for auto-auth
	token string

	// killCollection stops the automated resource reclamation
	killCollection chan struct{}

	// lastCollMu guards the time for reclamation.  It is used infrequently, and
	// we don't need to lock the whole object.
	lastCollMu sync.Mutex

	// lastCollection is the timestamp the last time the containers were
	// reclaimed.
	lastCollection time.Time

	// deregisterMux is a channel for sending a path that needs to be
	// de-registered from the server mux.
	deregisterMux chan string
}

// errNotebookPoolFull indicates the pool is at maxContainers
var errNotebookPoolFull = errors.New("container pool hit max size limit")

// newNotebookPool creates a notebookPool and sets defaults, overriding some
// with passed arguments.
func newNotebookPool(imageRegexp string, maxContainers int, lifetime time.Duration) (*notebookPool, error) {
	if imageRegexp == "" {
		imageRegexp = jupyterNotebookImageMatch
	}
	if int64(lifetime) <= 0 {
		lifetime = defaultContainerLifetime
	}
	if maxContainers < 1 {
		maxContainers = defaultMaxContainers
	}
	imageMatch, err := regexp.Compile(imageRegexp)
	if err != nil {
		return nil, err
	}
	imageMap := map[string]struct{}{}
	cli, err := client.NewEnvClient()
	if err != nil {
		return nil, err
	}
	images, err := cli.ImageList(context.Background(), types.ImageListOptions{})
	if err != nil {
		return nil, err
	}
	for _, image := range images {
		if len(image.RepoTags) < 1 || !imageMatch.MatchString(image.RepoTags[0]) {
			continue
		}
		log.Printf("found image %s", image.RepoTags[0])
		imageMap[image.RepoTags[0]] = struct{}{}
	}
	pool := &notebookPool{
		availableImages:   imageMap,
		imageMatch:        imageMatch,
		containerMap:      make(map[string]*tempNotebook),
		portSet:           newPortRange(8000, maxContainers),
		maxContainers:     maxContainers,
		containerLifetime: lifetime,
		killCollection:    make(chan struct{}),
		deregisterMux:     make(chan string),
	}
	pool.startCollector(time.Duration(int64(lifetime) / collectionFraction))
	pool.lastCollMu.Lock()
	pool.lastCollection = time.Now()
	pool.lastCollMu.Unlock()
	return pool, nil
}

// defaultHashSize is used for the unique hash generation
const defaultHashSize = 32

// newHash makes a n byte hash and returns the hex encoding.
func newHash(n int) string {
	b := make([]byte, n)
	_, err := rand.Read(b[:])
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", b)
}

// newNotebook initializes and sets values for a new notebook.
func (p *notebookPool) newNotebook(image string, pull bool, email string) (*tempNotebook, error) {
	ctx := context.Background()
	cli, err := client.NewEnvClient()
	if err != nil {
		log.Print(err)
		return nil, err
	}

	// TODO(kyle): possibly provide tag support
	if pull {
		log.Printf("pulling container %s", image)
		ctx, cancel := context.WithTimeout(ctx, 120*time.Second)
		defer cancel()
		out, err := cli.ImagePull(ctx, image, types.ImagePullOptions{})
		if err != nil {
			log.Print(err)
			return nil, err
		}
		defer out.Close()
		s := bufio.NewScanner(out)
		var ps dockerPullStatus
		for s.Scan() {
			err := json.Unmarshal([]byte(s.Text()), &ps)
			if err != nil {
				log.Print(err)
			}
			log.Print(ps.Progress)
		}
	}

	hash := newHash(defaultHashSize)

	port, err := p.portSet.Acquire()
	if err != nil {
		return nil, err
	}
	portString := fmt.Sprintf("%d", port)

	tokenArg := fmt.Sprintf(`--NotebookApp.token="%s"`, p.token)
	var env []string
	if p.disableJupyterAuth {
		tokenArg = fmt.Sprintf(`--NotebookApp.token=""`)
	} else {
		env = []string{fmt.Sprintf("CONFIGPROXY_AUTH_TOKEN=%s", p.token)}
	}
	var pSet = nat.PortSet{}
	pt, err := nat.NewPort("tcp", portString)
	pSet[pt] = struct{}{}
	containerConfig := container.Config{
		Hostname: "0.0.0.0",
		User:     "jovyan",
		Cmd: []string{`jupyter`,
			`notebook`,
			`--no-browser`,
			`--port`,
			portString,
			`--ip=0.0.0.0`,
			fmt.Sprintf("--NotebookApp.base_url=%s", path.Join("/book", hash)),
			`--NotebookApp.port_retries=0`,
			tokenArg,
			`--NotebookApp.disable_check_xsrf=True`,
		},
		Env:          env,
		Image:        image,
		ExposedPorts: pSet,
	}

	hostConfig := container.HostConfig{
		NetworkMode: "host",
	}

	resp, err := cli.ContainerCreate(ctx, &containerConfig, &hostConfig, nil, "")
	if err != nil {
		p.portSet.Drop(port)
		return nil, err
	}

	if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
		p.portSet.Drop(port)
		return nil, err
	}
	log.Printf("created container: %s", resp.ID)
	t := &tempNotebook{
		id:           resp.ID,
		hash:         hash,
		imageName:    image,
		lastAccessed: time.Now(),
		port:         port,
		userEmail:    email,
	}
	err = p.addNotebook(t)
	if err != nil {
		log.Print(err)
		p.portSet.Drop(port)
		return nil, err
	}
	return t, nil
}

// addNotebook adds a tempNotebook to the containerMap, if there is room.
func (p *notebookPool) addNotebook(t *tempNotebook) error {

	p.Lock()
	n := len(p.containerMap)
	log.Printf("pool size: %d of %d", n+1, p.maxContainers)
	if n+1 > p.maxContainers {
		p.releaseContainers(false, true)
	}
	if n+1 > p.maxContainers {
		return errNotebookPoolFull
	}
	p.containerMap[t.hash] = t
	p.Unlock()
	return nil
}

// stopAndKillContainer requests the stopping (docker stop) and the removal of
// the container (docker rm).  Errors are logged, but not returned and rm is
// always called.
func (p *notebookPool) stopAndKillContainer(id string) {
	d := time.Minute
	cli, err := client.NewEnvClient()
	if err != nil {
		log.Print(err)
	}
	ctx := context.Background()
	if err := cli.ContainerStop(ctx, id, &d); err != nil {
		log.Print(err)
	}
	if err := cli.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true}); err != nil {
		log.Print(err)
	}
}

// activeNotebooks fetchs copies of the tempNotebooks and returns them as a
// slice.  The lock is obviously invalid.
func (p *notebookPool) activeNotebooks() []tempNotebook {
	p.Lock()
	n := len(p.containerMap)
	nbs := make([]tempNotebook, n)
	i := 0
	for k := range p.containerMap {
		c := p.containerMap[k]
		nbs[i] = tempNotebook{
			id:           c.id,
			hash:         c.hash,
			imageName:    c.imageName,
			lastAccessed: c.lastAccessed,
			port:         c.port,
		}
		i++
	}
	p.Unlock()
	return nbs
}

// zombieNotebooks queries docker for containers that aren't under our
// supervision.  These can block ports assigned to our containers.
func (p *notebookPool) zombieContainers() ([]types.Container, error) {
	var cs []types.Container
	ids := map[string]struct{}{}
	p.Lock()
	for _, c := range p.containerMap {
		ids[c.id] = struct{}{}
	}
	p.Unlock()
	cli, err := client.NewEnvClient()
	opts := types.ContainerListOptions{}
	containers, err := cli.ContainerList(context.Background(), opts)
	if err != nil {
		return nil, err
	}
	for _, c := range containers {
		// If we manage it, leave it be
		if _, ok := ids[c.ID]; ok {
			continue
		}
		cs = append(cs, c)
	}
	return cs, nil
}

// nextCollection returns when the collector is run again
func (p *notebookPool) NextCollection() time.Time {
	p.lastCollMu.Lock()
	t := p.lastCollection.Add(p.containerLifetime / collectionFraction)
	p.lastCollMu.Unlock()
	return t
}

// startCollector launches a goroutine that checks for expired containers at
// interval d.  d is typically set to containerLifetime / collectionFraction.  Call
// stopCollector to stop the reclamation.
func (p *notebookPool) startCollector(d time.Duration) {
	go func() {
		ticker := time.NewTicker(d)
		for {
			select {
			case <-ticker.C:
				p.releaseContainers(false, true)
				p.lastCollMu.Lock()
				p.lastCollection = time.Now()
				p.lastCollMu.Unlock()
			case <-p.killCollection:
				ticker.Stop()
				return
			}
		}
	}()
}

// stopCollector sends a message on a channel to kill the auto reclamation.
func (p *notebookPool) stopCollector() {
	p.killCollection <- struct{}{}
}

// releaseContainers checks for expired containers and frees them from the
// containerMap.  It also frees the port in the portSet.  If force is true, age
// is ignored.
func (p *notebookPool) releaseContainers(force, async bool) error {
	p.Lock()
	trash := []tempNotebook{}
	for _, c := range p.containerMap {
		c.Lock()
		age := time.Now().Sub(c.lastAccessed)
		if age.Seconds() > p.containerLifetime.Seconds() || force {
			log.Printf("age: %v\n", age)
			//trash = append(trash, *c)
			trash = append(trash, tempNotebook{
				id:           c.id,
				hash:         c.hash,
				imageName:    c.imageName,
				lastAccessed: c.lastAccessed,
				port:         c.port,
			})
		}
		c.Unlock()
	}
	p.Unlock()
	for i := 0; i < len(trash); i++ {
		type nbCopy struct {
			id           string
			hash         string
			imageName    string
			lastAccessed time.Time
			port         int
		}
		c := nbCopy{
			id:           trash[i].id,
			hash:         trash[i].hash,
			imageName:    trash[i].imageName,
			lastAccessed: trash[i].lastAccessed,
			port:         trash[i].port,
		}
		f := func(c nbCopy) {
			log.Printf("attempting to release container %s last accessed at %v", c.id, c.lastAccessed)
			p.stopAndKillContainer(c.id)
			p.portSet.Drop(c.port)
			p.Lock()
			delete(p.containerMap, c.hash)
			p.Unlock()
			// This isn't very elegant, but we couldn't delete the pattern from the mux
			// before, but now we can with the vendored/updated copy in mux.go.  We add
			// a trailing slash when we register the path, so we must add it here too.
			p.deregisterMux <- path.Join("/book", c.hash) + "/"
		}
		if async {
			go f(c)
		} else {
			f(c)
		}
	}
	return nil
}

// killZombieContainers stops and kills any docker containers that aren't under
// out supervision.
//
// FIXME(kyle): not currently called at any time, when, why, etc...
func (p *notebookPool) killZombieContainers() error {
	zombies, err := p.zombieContainers()
	if err != nil {
		return err
	}
	for _, c := range zombies {
		p.stopAndKillContainer(c.ID)
	}
	return nil
}
<|endoftext|>


<commit_before>// A generic resource pool for databases etc
package pool

import (
	"errors"
	"io"
	"time"
)

var (
	TimeoutError    = errors.New("Timeout")
	PoolClosedError = errors.New("Pool is closed")
)

// ResourceOpener opens a resource
type ResourceOpener interface {
	Open() (Resource, error)
}

type Resource interface {
	io.Closer
	// Good returns true when the resource is in good state
	Good() bool
}

type PooledResource interface {
	// Release releases the resource back to the pool for reuse
	Release() error
	// Destroy destroys the resource. It is no longer usable.
	Destroy() error
	// Resource returns the underlying Resource
	Resource() Resource
}

type PoolMetrics interface {
	ReportResources(stats ResourcePoolStat)
	ReportWait(wt time.Duration)
}

type ResourcePoolStat struct {
	AvailableNow  uint32
	ResourcesOpen uint32
	Cap           uint32
	InUse         uint32
}

// ResourcePool manages a pool of resources for reuse.
type ResourcePool struct {
	metrics PoolMetrics //metrics interface to track how the pool performs

	// To get a resource, get a ticket first and then get the resource from
	// either the reserve or open a new one.
	// To release a resource, first put the resource back to the reserve
	// and then put the ticket back.
	// The order is important to make sure we never create >cap(tickets)
	// number of resources.
	reserve chan Resource // idle resources, ready for use
	tickets chan struct{} // ticket to own resources

	opener ResourceOpener
	closed chan struct{}
}

// New creates a new pool of Clients.
func New(maxReserve, maxOpen uint32, opener ResourceOpener, m PoolMetrics) *ResourcePool {
	if maxOpen < maxReserve {
		panic("maxOpen must be > maxReserve")
	}
	return &ResourcePool{
		metrics: m,
		reserve: make(chan Resource, maxReserve),
		tickets: make(chan struct{}, maxOpen),
		opener:  opener,
		closed:  make(chan struct{}),
	}
}

func (p *ResourcePool) Get() (PooledResource, error) {
	return p.GetWithTimeout(365 * 24 * time.Hour) // 1 year is forever
}

type pooledResource struct {
	p   *ResourcePool
	res Resource
}

func (pr *pooledResource) Release() error {
	return pr.p.release(pr)
}

func (pr *pooledResource) Destroy() error {
	return pr.p.destroy(pr)
}

func (pr *pooledResource) Resource() Resource {
	return pr.res
}

func (p *ResourcePool) GetWithTimeout(timeout time.Duration) (PooledResource, error) {
	// order is important: first ticket then reserve
	start := time.Now()
	select {
	case <-p.tickets:
	case <-time.After(timeout):
		return nil, TimeoutError
	case <-p.closed:
		return nil, PoolClosedError
	}
	if p.isClosed() {
		return nil, PoolClosedError
	}
	p.reportMetrics(time.Now().Sub(start))

	for {
		select {
		case r := <-p.reserve:
			if r.Good() {
				return &pooledResource{p: p, res: r}, nil
			}
			r.Close()

		default:
			// no reserve
			break
		}
	}

	r, err := p.opener.Open()
	if err != nil {
		// release ticket on error
		p.tickets <- struct{}{}
		return nil, err
	}
	return &pooledResource{p: p, res: r}, nil
}

func (p *ResourcePool) release(pr PooledResource) error {
	var err error
	// order is important: first reserve then ticket
	res := pr.Resource()
	select {
	case p.reserve <- res:
	default:
		// reserve is full
		err = res.Close()
	}
	p.tickets <- struct{}{}

	if p.isClosed() {
		p.drainReserve()
	}
	return err
}

func (p *ResourcePool) destroy(pr PooledResource) error {
	p.tickets <- struct{}{}
	return pr.Resource().Close()
}

// Close closes the pool. Resources in use are not affected.
func (p *ResourcePool) Close() error {
	close(p.closed)
	p.drainReserve()
	return nil
}

func (p *ResourcePool) isClosed() bool {
	select {
	case <-p.closed:
		return true
	default:
		return false
	}
}

func (p *ResourcePool) drainReserve() {
	for {
		select {
		case r := <-p.reserve:
			r.Close()
		default:
		}
	}
}

/**
Metrics
**/
func (p *ResourcePool) reportMetrics(wt time.Duration) {
	if p.metrics != nil {
		go p.metrics.ReportWait(wt)
		go p.metrics.ReportResources(p.Stats())
	}
}

func (p *ResourcePool) Stats() ResourcePoolStat {
	tot := uint32(cap(p.tickets))
	n := uint32(len(p.tickets))
	inuse := tot - n
	available := uint32(len(p.reserve))

	return ResourcePoolStat{
		AvailableNow:  available,
		ResourcesOpen: inuse + available,
		Cap:           tot,
		InUse:         inuse,
	}
}
<commit_msg>populate tickets<commit_after>// A generic resource pool for databases etc
package pool

import (
	"errors"
	"io"
	"time"
)

var (
	TimeoutError    = errors.New("Timeout")
	PoolClosedError = errors.New("Pool is closed")
)

// ResourceOpener opens a resource
type ResourceOpener interface {
	Open() (Resource, error)
}

type Resource interface {
	io.Closer
	// Good returns true when the resource is in good state
	Good() bool
}

type PooledResource interface {
	// Release releases the resource back to the pool for reuse
	Release() error
	// Destroy destroys the resource. It is no longer usable.
	Destroy() error
	// Resource returns the underlying Resource
	Resource() Resource
}

type PoolMetrics interface {
	ReportResources(stats ResourcePoolStat)
	ReportWait(wt time.Duration)
}

type ResourcePoolStat struct {
	AvailableNow  uint32
	ResourcesOpen uint32
	Cap           uint32
	InUse         uint32
}

// ResourcePool manages a pool of resources for reuse.
type ResourcePool struct {
	metrics PoolMetrics //metrics interface to track how the pool performs

	// To get a resource, get a ticket first and then get the resource from
	// either the reserve or open a new one.
	// To release a resource, first put the resource back to the reserve
	// and then put the ticket back.
	// The order is important to make sure we never create >cap(tickets)
	// number of resources.
	reserve chan Resource // idle resources, ready for use
	tickets chan struct{} // ticket to own resources

	opener ResourceOpener
	closed chan struct{}
}

// New creates a new pool of Clients.
func New(maxReserve, maxOpen uint32, opener ResourceOpener, m PoolMetrics) *ResourcePool {
	if maxOpen < maxReserve {
		panic("maxOpen must be > maxReserve")
	}
	tickets := make(chan struct{}, maxOpen)
	for i := 0; i < maxOpen; i++ {
		tickets <- struct{}{}
	}
	return &ResourcePool{
		metrics: m,
		reserve: make(chan Resource, maxReserve),
		tickets: tickets,
		opener:  opener,
		closed:  make(chan struct{}),
	}
}

func (p *ResourcePool) Get() (PooledResource, error) {
	return p.GetWithTimeout(365 * 24 * time.Hour) // 1 year is forever
}

type pooledResource struct {
	p   *ResourcePool
	res Resource
}

func (pr *pooledResource) Release() error {
	return pr.p.release(pr)
}

func (pr *pooledResource) Destroy() error {
	return pr.p.destroy(pr)
}

func (pr *pooledResource) Resource() Resource {
	return pr.res
}

func (p *ResourcePool) GetWithTimeout(timeout time.Duration) (PooledResource, error) {
	// order is important: first ticket then reserve
	start := time.Now()
	select {
	case <-p.tickets:
	case <-time.After(timeout):
		return nil, TimeoutError
	case <-p.closed:
		return nil, PoolClosedError
	}
	if p.isClosed() {
		return nil, PoolClosedError
	}
	p.reportMetrics(time.Now().Sub(start))

	for {
		select {
		case r := <-p.reserve:
			if r.Good() {
				return &pooledResource{p: p, res: r}, nil
			}
			r.Close()

		default:
			// no reserve
			break
		}
	}

	r, err := p.opener.Open()
	if err != nil {
		// release ticket on error
		p.tickets <- struct{}{}
		return nil, err
	}
	return &pooledResource{p: p, res: r}, nil
}

func (p *ResourcePool) release(pr PooledResource) error {
	var err error
	// order is important: first reserve then ticket
	res := pr.Resource()
	select {
	case p.reserve <- res:
	default:
		// reserve is full
		err = res.Close()
	}
	p.tickets <- struct{}{}

	if p.isClosed() {
		p.drainReserve()
	}
	return err
}

func (p *ResourcePool) destroy(pr PooledResource) error {
	p.tickets <- struct{}{}
	return pr.Resource().Close()
}

// Close closes the pool. Resources in use are not affected.
func (p *ResourcePool) Close() error {
	close(p.closed)
	p.drainReserve()
	return nil
}

func (p *ResourcePool) isClosed() bool {
	select {
	case <-p.closed:
		return true
	default:
		return false
	}
}

func (p *ResourcePool) drainReserve() {
	for {
		select {
		case r := <-p.reserve:
			r.Close()
		default:
		}
	}
}

/**
Metrics
**/
func (p *ResourcePool) reportMetrics(wt time.Duration) {
	if p.metrics != nil {
		go p.metrics.ReportWait(wt)
		go p.metrics.ReportResources(p.Stats())
	}
}

func (p *ResourcePool) Stats() ResourcePoolStat {
	tot := uint32(cap(p.tickets))
	n := uint32(len(p.tickets))
	inuse := tot - n
	available := uint32(len(p.reserve))

	return ResourcePoolStat{
		AvailableNow:  available,
		ResourcesOpen: inuse + available,
		Cap:           tot,
		InUse:         inuse,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var cmdPush = &Command{
	Run:   runPush,
	Usage: "push path name",
	Short: "Deploy single artifact from a local directory",
	Long: `
Deploy single artifact from a local directory

Examples:

  force push connectedApps name
`,
}

var pxml = `<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>%s</members>
        <name>%s</name>
    </types>
    <version>29.0</version>
</Package>`

type metapath struct {
    path string
    name string
}

var metapaths = []metapath{
    metapath{"classes"      , "ApexClass"},
    metapath{"objects"      , "CustomObject"},
    metapath{"tabs"         , "CustomTab"},
    metapath{"flexipages"   , "FlexiPage"},
    metapath{"components"   , "ApexComponent"},
    metapath{"triggers"     , "ApexTrigger"},
    metapath{"pages"        , "ApexPage"},
}

func getPathForMeta(metaname string) string {
    for _, mp := range metapaths {
        if strings.ToLower(mp.name) == strings.ToLower(metaname) {
            return mp.path
        }
    }

    // Unknown, so use metaname
    return metaname
}

func getMetaForPath(path string) string {
    for _, mp := range metapaths {
        if mp.path == path {
            return mp.name
        }
    }

    // Unknown, so use path
    return path
}

func runPush(cmd *Command, args []string) {
	if len(args) == 0 {
		cmd.printUsage()
		return
	}

    if len(args) == 1 {
        fpath := args[0]
        pushByPath(fpath)

        fmt.Printf("Pushed %s to Force.com\n", fpath)
    }

	if len(args) == 2 {
        // If arg[0] is already path or meta, the method will return arg[0]
        objPath := getPathForMeta(args[0])
        objName := args[1]
        pushByName(objPath, objName)

        fmt.Printf("Pushed %s to Force.com\n", objName)
    }
}

func pushByName(objPath string, objName string) {
	wd, _ := os.Getwd()

    // First try for metadata directory
	root := filepath.Join(wd, "metadata")
	if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
        // If not found, try for src directory
        root = filepath.Join(wd, "src")
        if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
            ErrorAndExit("Current directory must contain a src or metadata directory")
        }
	}

	if _, err := os.Stat(filepath.Join(root, objPath)); os.IsNotExist(err) {
		ErrorAndExit("Folder " + objPath + " not found, must specify valid metadata")
	}

    // Find file by walking directory and ignoring extension
	found := false
    fpath := ""
	err := filepath.Walk(filepath.Join(root, objPath), func(path string, f os.FileInfo, err error) error {
		if f.Mode().IsRegular() {
            fname := strings.ToLower(f.Name())
            fname = strings.TrimSuffix(fname, filepath.Ext(fname))
			if strings.ToLower(fname) == strings.ToLower(objName) {
				found = true
                fpath = filepath.Join(root, objPath, f.Name())
			}
		}
		return nil
	})
	if err != nil {
		ErrorAndExit(err.Error())
	}
	if !found {
		ErrorAndExit("Could not find " + objName + " in " + objPath)
	}

    pushByPath(fpath)
}

// Push metadata object by path to a file
func pushByPath(fpath string) {
    fpath, err := filepath.Abs(fpath)
    if err != nil {
        ErrorAndExit("Cound not find " + fpath)
    }
    if _, err := os.Stat(fpath); err != nil {
        ErrorAndExit("Cound not open " + fpath)
    }

    hasMeta := true
    fname := filepath.Base(fpath)
    fname = strings.TrimSuffix(fname, filepath.Ext(fname))
    fdir := filepath.Dir(fpath)
    typePath := filepath.Base(fdir)
    srcDir := filepath.Dir(fdir)
    metaType := getMetaForPath(typePath)
    // Should be present since we worked back to srcDir
    frel, _ := filepath.Rel(srcDir, fpath)

    // Try to find meta file
    fmeta := fpath + "-meta.xml"
    fmetarel := ""
    if _, err := os.Stat(fmeta); err != nil {
        if os.IsNotExist(err) {
            hasMeta = false
        } else {
            ErrorAndExit("Cound not open " + fmeta)
        }
    } else {
        // Should be present since we worked back to srcDir
        fmetarel, _ = filepath.Rel(srcDir, fmeta)
    }

    // DEBUG
    // fmt.Println("Dir: " + fdir)
    // fmt.Println("fname: " + fname)
    // fmt.Println("Dir end: " + typePath)
    // fmt.Println("srcDir: " + srcDir)
    // fmt.Println("Type: " + metaType)
    // fmt.Println("relPath: " + frel)

	files := make(ForceMetadataFiles)

    fdata, err := ioutil.ReadFile(fpath)
    files[frel] = fdata
    if hasMeta {
        fdata, err = ioutil.ReadFile(fmeta)
        files[fmetarel] = fdata
    }

    files["package.xml"] = []byte(fmt.Sprintf(pxml, fname, metaType))

    deployFiles(files)
}

func deployFiles(files ForceMetadataFiles) {
	force, _ := ActiveForce()
	var DeploymentOptions ForceDeployOptions
	successes, problems, err := force.Metadata.Deploy(files, DeploymentOptions)
	if err != nil {
		ErrorAndExit(err.Error())
	}
	fmt.Printf("\nFailures - %d\n", len(problems))
	for _, problem := range problems {
		if problem.FullName == "" {
			fmt.Println(problem.Problem)
		} else {
			fmt.Printf("ERROR with %s:\n %s\n", problem.FullName, problem.Problem)
		}
	}

	fmt.Printf("\nSuccesses - %d\n", len(successes))
	for _, success := range successes {
		if success.FullName != "package.xml" {
			verb := "unchanged"
			if success.Changed {
				verb = "changed"
			} else if success.Deleted {
				verb = "deleted"
			} else if success.Created {
				verb = "created"
			}
			fmt.Printf("%s\n\tstatus: %s\n\tid=%s\n", success.FullName, verb, success.Id)
		}
	}
}
<commit_msg>Accept multiple files in force push<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var cmdPush = &Command{
	Run:   runPush,
	Usage: "push (<metadata> <name> | <file>...)",
	Short: "Deploy artifact from a local directory",
	Long: `
Deploy artifact from a local directory
<metadata>: Accepts either actual directory name or Metadata type

Examples:
  force push classes MyClass
  force push ApexClass MyClass
  force push src/classes/MyClass.cls
`,
}

var xmlBody = `<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">%s
    <version>29.0</version>
</Package>`

var xmlType = `
    <types>%s
        <name>%s</name>
    </types>`
var xmlMember = `
        <members>%s</members>`

type metapath struct {
    path string
    name string
}

var metapaths = []metapath{
    metapath{"classes"      , "ApexClass"},
    metapath{"objects"      , "CustomObject"},
    metapath{"tabs"         , "CustomTab"},
    metapath{"flexipages"   , "FlexiPage"},
    metapath{"components"   , "ApexComponent"},
    metapath{"triggers"     , "ApexTrigger"},
    metapath{"pages"        , "ApexPage"},
}

func getPathForMeta(metaname string) string {
    for _, mp := range metapaths {
        if strings.ToLower(mp.name) == strings.ToLower(metaname) {
            return mp.path
        }
    }

    // Unknown, so use metaname
    return metaname
}

func getMetaForPath(path string) string {
    for _, mp := range metapaths {
        if mp.path == path {
            return mp.name
        }
    }

    // Unknown, so use path
    return path
}

func argIsFile(fpath string) bool {
    if _, err := os.Stat(fpath); err != nil {
        return false
    }
    return true
}

func runPush(cmd *Command, args []string) {
	if len(args) == 0 {
		cmd.printUsage()
		return
	}

    if argIsFile(args[0]) {
        fpath := args[0]
        pushByPaths(args)

        fmt.Printf("Pushed %s to Force.com\n", fpath)
        return
    }

	if len(args) == 2 {
        // If arg[0] is already path or meta, the method will return arg[0]
        objPath := getPathForMeta(args[0])
        objName := args[1]
        pushByName(objPath, objName)

        fmt.Printf("Pushed %s to Force.com\n", objName)
        return
    }

    fmt.Println("Could not find file or determine metadata")

    // If we got here, something is not valid
    cmd.printUsage()
}

func pushByName(objPath string, objName string) {
	wd, _ := os.Getwd()

    // First try for metadata directory
	root := filepath.Join(wd, "metadata")
	if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
        // If not found, try for src directory
        root = filepath.Join(wd, "src")
        if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
            ErrorAndExit("Current directory must contain a src or metadata directory")
        }
	}

	if _, err := os.Stat(filepath.Join(root, objPath)); os.IsNotExist(err) {
		ErrorAndExit("Folder " + objPath + " not found, must specify valid metadata")
	}

    // Find file by walking directory and ignoring extension
	found := false
    fpath := ""
	err := filepath.Walk(filepath.Join(root, objPath), func(path string, f os.FileInfo, err error) error {
		if f.Mode().IsRegular() {
            fname := strings.ToLower(f.Name())
            fname = strings.TrimSuffix(fname, filepath.Ext(fname))
			if strings.ToLower(fname) == strings.ToLower(objName) {
				found = true
                fpath = filepath.Join(root, objPath, f.Name())
			}
		}
		return nil
	})
	if err != nil {
		ErrorAndExit(err.Error())
	}
	if !found {
		ErrorAndExit("Could not find " + objName + " in " + objPath)
	}

    pushByPath(fpath)
}

func pushByPath(fpath string) {
    pushByPaths([]string{fpath})
}

// Push metadata object by path to a file
func pushByPaths(fpaths []string) {
	files := make(ForceMetadataFiles)
    xmlMap := make(map[string][]string)

    for _, fpath := range fpaths{
        addFile(files, xmlMap, fpath)
    }

    files["package.xml"] = buildXml(xmlMap)

    deployFiles(files)
}

func addFile(files ForceMetadataFiles, xmlMap map[string][]string, fpath string) {
    fpath, err := filepath.Abs(fpath)
    if err != nil {
        ErrorAndExit("Cound not find " + fpath)
    }
    if _, err := os.Stat(fpath); err != nil {
        ErrorAndExit("Cound not open " + fpath)
    }

    hasMeta := true
    fname := filepath.Base(fpath)
    fname = strings.TrimSuffix(fname, filepath.Ext(fname))
    fdir := filepath.Dir(fpath)
    typePath := filepath.Base(fdir)
    srcDir := filepath.Dir(fdir)
    metaType := getMetaForPath(typePath)
    // Should be present since we worked back to srcDir
    frel, _ := filepath.Rel(srcDir, fpath)

    // Try to find meta file
    fmeta := fpath + "-meta.xml"
    fmetarel := ""
    if _, err := os.Stat(fmeta); err != nil {
        if os.IsNotExist(err) {
            hasMeta = false
        } else {
            ErrorAndExit("Cound not open " + fmeta)
        }
    } else {
        // Should be present since we worked back to srcDir
        fmetarel, _ = filepath.Rel(srcDir, fmeta)
    }

    xmlMap[metaType] = append(xmlMap[metaType], fname)

    fdata, err := ioutil.ReadFile(fpath)
    files[frel] = fdata
    if hasMeta {
        fdata, err = ioutil.ReadFile(fmeta)
        files[fmetarel] = fdata
    }
}

func buildXml(xmlMap map[string][]string) []byte {
    var typeXml string
    for metaType, members := range xmlMap{
        fmt.Println("Type: " + metaType)
        var membersXml string
        for _, member := range members{
            fmt.Println("member: " + member)
            membersXml += fmt.Sprintf(xmlMember, member)
        }

        if membersXml != "" {
            typeXml += fmt.Sprintf(xmlType, membersXml, metaType)
        }
    }

    bodyXml := fmt.Sprintf(xmlBody, typeXml)

    return []byte(bodyXml)
}

func deployFiles(files ForceMetadataFiles) {
	force, _ := ActiveForce()
	var DeploymentOptions ForceDeployOptions
	successes, problems, err := force.Metadata.Deploy(files, DeploymentOptions)
	if err != nil {
		ErrorAndExit(err.Error())
	}
	fmt.Printf("\nFailures - %d\n", len(problems))
	for _, problem := range problems {
		if problem.FullName == "" {
			fmt.Println(problem.Problem)
		} else {
			fmt.Printf("ERROR with %s:\n %s\n", problem.FullName, problem.Problem)
		}
	}

	fmt.Printf("\nSuccesses - %d\n", len(successes))
	for _, success := range successes {
		if success.FullName != "package.xml" {
			verb := "unchanged"
			if success.Changed {
				verb = "changed"
			} else if success.Deleted {
				verb = "deleted"
			} else if success.Created {
				verb = "created"
			}
			fmt.Printf("%s\n\tstatus: %s\n\tid=%s\n", success.FullName, verb, success.Id)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"
)

// Push command line tool
func cmdPush() int {

	// git-lob push [--all] [--recheck] [--force] [<remote> [<ref>...]]

	// Validate custom options
	errorList := validateCustomOptions(GlobalOptions, nil, []string{"all", "recheck", "force"})
	if len(errorList) > 0 {
		fmt.Fprintf(os.Stderr, strings.Join(errorList, "\n"))
		return 9
	}

	optAll := GlobalOptions.BoolOpts.Contains("all")
	optRecheck := GlobalOptions.BoolOpts.Contains("recheck")
	optForce := GlobalOptions.BoolOpts.Contains("force")
	optDryRun := GlobalOptions.DryRun

	// Determine remote
	var remoteName string
	var refspecs []*GitRefSpec
	if len(GlobalOptions.Args) > 0 {
		// first parameter must be remote if there are arguments
		remoteName = GlobalOptions.Args[0]

		// Remaining args are refspecs
		if len(GlobalOptions.Args) > 1 {
			// Not valid if --all
			if optAll {
				fmt.Fprintf(os.Stderr, "git-lob: Too many arguments; cannot include refspec when using --all\n")
				return 7
			}
			for _, arg := range GlobalOptions.Args[1:] {
				r := ParseGitRefSpec(arg)
				refspecs = append(refspecs, r)
			}
		}

	} else {
		remoteName = GetGitDefaultRemote()
	}

	// check the remote config to make sure it's valid
	provider, err := GetProviderForRemote(remoteName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "git-lob: %v\n", err)
		return 7
	}

	if len(refspecs) == 0 {
		// No refspecs specified, so determine default
		if optAll {
			branches, err := GetGitLocalBranches()
			if err != nil {
				fmt.Fprintf(os.Stderr, "git-lob: unable to get local branch list - %v\n", err)
				return 7
			}
			for _, branch := range branches {
				refspecs = append(refspecs, &GitRefSpec{branch, "", ""})
			}

		} else {
			// Check remote.<remote>.push for default refspec
			def, ok := GlobalOptions.GitConfig[fmt.Sprintf("remote.%v.push", remoteName)]
			if ok {
				pushspecs := strings.Fields(def)
				for _, s := range pushspecs {
					refspecs = append(refspecs, &GitRefSpec{s, "", ""})
				}
			} else {
				// determine refspec from current branch & push settings
				branch := GetGitCurrentBranch()
				refspecs = append(refspecs, &GitRefSpec{branch, "", ""})
			}
		}
	}

	progress := func(fileInProgress string, isSkipped bool, filepercent, overallpercent int, rate string) (abort bool) {
		// Note, no newline, just carriage return to overwrite single line in place
		if GlobalOptions.Verbose {
			// report file name too
			if filepercent == 100 {
				// in verbose mode we include newline after reporting file name at 100%
				if isSkipped {
					fmt.Printf("\rSkipped: %v (Up to date)\n", fileInProgress)
				} else {
					fmt.Printf("\rPushed: %v 100%%\n", fileInProgress)
				}
			} else {
				fmt.Printf("\rPushing: %v %d%%\tOverall: %d%%\t(%v)", fileInProgress, filepercent, overallpercent, rate)
			}
		} else if !GlobalOptions.Quiet {
			fmt.Printf("\rPushing: %d%%\t(%v)", overallpercent, rate)
		}

		return false

	}

	if !GlobalOptions.Quiet {
		fmt.Println("Pushing binaries for", refspecs, "to", remoteName)
	}

	switch p := provider.(type) {
	case BasicSyncProvider:
		PushBasic(p, remoteName, refspecs, optDryRun, optForce, optRecheck, progress)
	case SmartSyncProvider:
		PushSmart(p, remoteName, refspecs, optDryRun, optForce, optRecheck, progress)
	}

	// Because no newlines in progress reporting
	if !GlobalOptions.Quiet {
		fmt.Println()
	}

	return 0
}

func PushBasic(provider BasicSyncProvider, remoteName string, refspecs []*GitRefSpec, dryRun, force, recheck bool,
	callback func(fileInProgress string, isSkipped bool, filepercent, overallpercent int, rate string) (abort bool)) {
	// TODO
}
func PushSmart(provider SmartSyncProvider, remoteName string, refspecs []*GitRefSpec, dryRun, force, recheck bool,
	callback func(fileInProgress string, isSkipped bool, filepercent, overallpercent int, rate string) (abort bool)) {
	// TODO
}

func cmdPushHelp() {
	fmt.Println(`Usage: git-lob push [options] [<remote> [<ref>...]]

  Uploads binaries to a remote, sending only binaries required to ensure
  that remote has the binary resources referenced at a set of commits.

  Behaves much like 'git push' except there are no destination refs, only
  supporting binary files.

Parameters:
  <remote>: The destination to upload to. This should correspond to the
            name of a remote (no direct URLs permitted) which is configured
            in .git/config. See REMOTES below for more details, additional
            config parameters are required in the remote.

            If no remote is specified, branch.*.remote configuration for the
            current branch is consulted to determine where to push. If the
            configuration is missing, it defaults to origin.
     <ref>: Which local reference(s) up to which we should make sure binaries
            are uploaded for. You can specify zero, one, or many local refs.
            There is no destination ref as in git push.

            If no ref is specified, and --all is not used, then
            remote.<remotename>.push is used if present, otherwise the current
            branch is used.

            COMMIT RANGES

            You can also specify a range of refs in the form <ref1>..<ref2> to
            force git-lob push to check a specific range of commits for
            binaries, instead of using its own records of which commits it
            thinks are already up to date on this remote.
            See HISTORY CHECKING below.

Options:
  --all         Push all branches; cannot be used with other refs.
  --recheck     Re-check entire commit history to each ref instead of only
                back to last commit we believe is already pushed.
                See HISTORY CHECKING below for more details.
  --force       Always upload files even if the provider believes the file is
                already present on the remote. You shouldn't need this.
  --quiet, -q   Print less output
  --verbose, -v Print more output
  --dry-run     Don't actually delete anything, just report

REMOTES

Binaries in git-lob are not stored in the regular git repo, but a corresponding
binary store must always exist to provide the actual binary content. A remote
in git usually only gives you the real git repo, so git-lob needs to expand
the configuration parameters to git remotes to specify the location of the
corresponding remote binary store.

The <remote> parameter refers to a named remote in .git/config (plain URLs
cannot be supported). The remote entry itself is the same as any normal git
remote, except that it requires additional git-lob specific parameters. The
parameters depend on the type of binary storage ('provider') being used; see
'git-lob providers' for a list of available providers and
'git-lob provider <provider>' for specific details of one provider.

As an example, let's take the 'filesystem' provider, which simply uses the OS's
file system as a remote transport (obviously very simplistic):

[remote "origin"]
    # these 2 lines are standard git
    url = ssh://git@bitbucket.org/something/somthing.git
    fetch = +refs/heads/*:refs/remotes/origin/*
    # these next 2 lines are required to configure the remote binary store
    git-lob-provider = filesystem
    git-lob-path = /Volumes/shared/something/something/binary/store

Other providers may require other parameters. It's important to note that you
can share a binary store among multiple remote repos if you wish, much like
the local git-lob.sharedstore option, since binaries are stored by SHA.
Identical file content in multiple repos can be stored only once this way.
Of course, access control may be an issue to consider here though.

HISTORY CHECKING

When pushing binaries for a given ref, git-lob performs a search for commits
which reference git-lob binaries from that ref backwards, before checking
which of those binaries it needs to upload. This is so that we only upload
binaries that are actually referenced by the ref you're choosing to push,
and don't waste time on binaries in unpublished feature branches etc.

Because searching the whole of the git history can be slow on large
repositories, git-lob speeds this search up by keeping a record of which
commits it believes the remote already has all binaries for.

These records are updated whenever you git-lob push/pull. We do not use git's
own remote branch refs to track this, because pushing commits can be done
completely separately from binaries so we can't rely on that information.
So pushing and pulling branches in git has no effect on this state, only
git-lob push/pull.

If for some reason these records are wrong, and you need to push binaries
for a bigger range of commits, you can do this 2 ways:

1. Use the --recheck option. This is the 'nuclear option'; it will scan the
   entire history of the repo again to make 100% sure everything is correct.
   Can take a while on large repos.

2. Use a commit range for <ref>, i.e. <ref1>..<ref2>. git-lob will check that
   entire range of commits for binary references which will then be checked
   with the remote.

There are not many circumstances where you need to manually override the commit
range that is checked for binaries. Even if you edit commits, rebase etc,
git-lob should not miss any binaries, because the commit SHAs would change and
it would know to check any referenced binaries again. The main reason why
you would need to override the history checking is if the remote changed, for
example if someone manually deleted the remote binary store, or you moved to
a new URL without copying the data and needed to re-populate it from your local
repo.

`)
}
<commit_msg>Make sure we validate the provider config before proceeding in push<commit_after>package main

import (
	"fmt"
	"os"
	"strings"
)

// Push command line tool
func cmdPush() int {

	// git-lob push [--all] [--recheck] [--force] [<remote> [<ref>...]]

	// Validate custom options
	errorList := validateCustomOptions(GlobalOptions, nil, []string{"all", "recheck", "force"})
	if len(errorList) > 0 {
		fmt.Fprintf(os.Stderr, strings.Join(errorList, "\n"))
		return 9
	}

	optAll := GlobalOptions.BoolOpts.Contains("all")
	optRecheck := GlobalOptions.BoolOpts.Contains("recheck")
	optForce := GlobalOptions.BoolOpts.Contains("force")
	optDryRun := GlobalOptions.DryRun

	// Determine remote
	var remoteName string
	var refspecs []*GitRefSpec
	if len(GlobalOptions.Args) > 0 {
		// first parameter must be remote if there are arguments
		remoteName = GlobalOptions.Args[0]

		// Remaining args are refspecs
		if len(GlobalOptions.Args) > 1 {
			// Not valid if --all
			if optAll {
				fmt.Fprintf(os.Stderr, "git-lob: Too many arguments; cannot include refspec when using --all\n")
				return 7
			}
			for _, arg := range GlobalOptions.Args[1:] {
				r := ParseGitRefSpec(arg)
				refspecs = append(refspecs, r)
			}
		}

	} else {
		remoteName = GetGitDefaultRemote()
	}

	// check the remote config to make sure it's valid
	provider, err := GetProviderForRemote(remoteName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "git-lob: %v\n", err)
		return 6
	}
	if err = provider.ValidateConfig(remoteName); err != nil {
		fmt.Fprintf(os.Stderr, "git-lob: remote %v has configuration problems:\n%v\n", remoteName, err)
		return 6
	}

	if len(refspecs) == 0 {
		// No refspecs specified, so determine default
		if optAll {
			branches, err := GetGitLocalBranches()
			if err != nil {
				fmt.Fprintf(os.Stderr, "git-lob: unable to get local branch list - %v\n", err)
				return 7
			}
			for _, branch := range branches {
				refspecs = append(refspecs, &GitRefSpec{branch, "", ""})
			}

		} else {
			// Check remote.<remote>.push for default refspec
			def, ok := GlobalOptions.GitConfig[fmt.Sprintf("remote.%v.push", remoteName)]
			if ok {
				pushspecs := strings.Fields(def)
				for _, s := range pushspecs {
					refspecs = append(refspecs, &GitRefSpec{s, "", ""})
				}
			} else {
				// determine refspec from current branch & push settings
				branch := GetGitCurrentBranch()
				refspecs = append(refspecs, &GitRefSpec{branch, "", ""})
			}
		}
	}

	progress := func(fileInProgress string, isSkipped bool, filepercent, overallpercent int, rate string) (abort bool) {
		// Note, no newline, just carriage return to overwrite single line in place
		if GlobalOptions.Verbose {
			// report file name too
			if filepercent == 100 {
				// in verbose mode we include newline after reporting file name at 100%
				if isSkipped {
					fmt.Printf("\rSkipped: %v (Up to date)\n", fileInProgress)
				} else {
					fmt.Printf("\rPushed: %v 100%%\n", fileInProgress)
				}
			} else {
				fmt.Printf("\rPushing: %v %d%%\tOverall: %d%%\t(%v)", fileInProgress, filepercent, overallpercent, rate)
			}
		} else if !GlobalOptions.Quiet {
			fmt.Printf("\rPushing: %d%%\t(%v)", overallpercent, rate)
		}

		return false

	}

	if !GlobalOptions.Quiet {
		fmt.Println("Pushing binaries for", refspecs, "to", remoteName)
	}

	switch p := provider.(type) {
	case BasicSyncProvider:
		PushBasic(p, remoteName, refspecs, optDryRun, optForce, optRecheck, progress)
	case SmartSyncProvider:
		PushSmart(p, remoteName, refspecs, optDryRun, optForce, optRecheck, progress)
	}

	// Because no newlines in progress reporting
	if !GlobalOptions.Quiet {
		fmt.Println()
	}

	return 0
}

func PushBasic(provider BasicSyncProvider, remoteName string, refspecs []*GitRefSpec, dryRun, force, recheck bool,
	callback func(fileInProgress string, isSkipped bool, filepercent, overallpercent int, rate string) (abort bool)) {
	// TODO
}
func PushSmart(provider SmartSyncProvider, remoteName string, refspecs []*GitRefSpec, dryRun, force, recheck bool,
	callback func(fileInProgress string, isSkipped bool, filepercent, overallpercent int, rate string) (abort bool)) {
	// TODO
}

func cmdPushHelp() {
	fmt.Println(`Usage: git-lob push [options] [<remote> [<ref>...]]

  Uploads binaries to a remote, sending only binaries required to ensure
  that remote has the binary resources referenced at a set of commits.

  Behaves much like 'git push' except there are no destination refs, only
  supporting binary files.

Parameters:
  <remote>: The destination to upload to. This should correspond to the
            name of a remote (no direct URLs permitted) which is configured
            in .git/config. See REMOTES below for more details, additional
            config parameters are required in the remote.

            If no remote is specified, branch.*.remote configuration for the
            current branch is consulted to determine where to push. If the
            configuration is missing, it defaults to origin.
     <ref>: Which local reference(s) up to which we should make sure binaries
            are uploaded for. You can specify zero, one, or many local refs.
            There is no destination ref as in git push.

            If no ref is specified, and --all is not used, then
            remote.<remotename>.push is used if present, otherwise the current
            branch is used.

            COMMIT RANGES

            You can also specify a range of refs in the form <ref1>..<ref2> to
            force git-lob push to check a specific range of commits for
            binaries, instead of using its own records of which commits it
            thinks are already up to date on this remote.
            See HISTORY CHECKING below.

Options:
  --all         Push all branches; cannot be used with other refs.
  --recheck     Re-check entire commit history to each ref instead of only
                back to last commit we believe is already pushed.
                See HISTORY CHECKING below for more details.
  --force       Always upload files even if the provider believes the file is
                already present on the remote. You shouldn't need this.
  --quiet, -q   Print less output
  --verbose, -v Print more output
  --dry-run     Don't actually delete anything, just report

REMOTES

Binaries in git-lob are not stored in the regular git repo, but a corresponding
binary store must always exist to provide the actual binary content. A remote
in git usually only gives you the real git repo, so git-lob needs to expand
the configuration parameters to git remotes to specify the location of the
corresponding remote binary store.

The <remote> parameter refers to a named remote in .git/config (plain URLs
cannot be supported). The remote entry itself is the same as any normal git
remote, except that it requires additional git-lob specific parameters. The
parameters depend on the type of binary storage ('provider') being used; see
'git-lob providers' for a list of available providers and
'git-lob provider <provider>' for specific details of one provider.

As an example, let's take the 'filesystem' provider, which simply uses the OS's
file system as a remote transport (obviously very simplistic):

[remote "origin"]
    # these 2 lines are standard git
    url = ssh://git@bitbucket.org/something/somthing.git
    fetch = +refs/heads/*:refs/remotes/origin/*
    # these next 2 lines are required to configure the remote binary store
    git-lob-provider = filesystem
    git-lob-path = /Volumes/shared/something/something/binary/store

Other providers may require other parameters. It's important to note that you
can share a binary store among multiple remote repos if you wish, much like
the local git-lob.sharedstore option, since binaries are stored by SHA.
Identical file content in multiple repos can be stored only once this way.
Of course, access control may be an issue to consider here though.

HISTORY CHECKING

When pushing binaries for a given ref, git-lob performs a search for commits
which reference git-lob binaries from that ref backwards, before checking
which of those binaries it needs to upload. This is so that we only upload
binaries that are actually referenced by the ref you're choosing to push,
and don't waste time on binaries in unpublished feature branches etc.

Because searching the whole of the git history can be slow on large
repositories, git-lob speeds this search up by keeping a record of which
commits it believes the remote already has all binaries for.

These records are updated whenever you git-lob push/pull. We do not use git's
own remote branch refs to track this, because pushing commits can be done
completely separately from binaries so we can't rely on that information.
So pushing and pulling branches in git has no effect on this state, only
git-lob push/pull.

If for some reason these records are wrong, and you need to push binaries
for a bigger range of commits, you can do this 2 ways:

1. Use the --recheck option. This is the 'nuclear option'; it will scan the
   entire history of the repo again to make 100% sure everything is correct.
   Can take a while on large repos.

2. Use a commit range for <ref>, i.e. <ref1>..<ref2>. git-lob will check that
   entire range of commits for binary references which will then be checked
   with the remote.

There are not many circumstances where you need to manually override the commit
range that is checked for binaries. Even if you edit commits, rebase etc,
git-lob should not miss any binaries, because the commit SHAs would change and
it would know to check any referenced binaries again. The main reason why
you would need to override the history checking is if the remote changed, for
example if someone manually deleted the remote binary store, or you moved to
a new URL without copying the data and needed to re-populate it from your local
repo.

`)
}
<|endoftext|>


<commit_before>package nd

import (
  "crypto/rand"
  mr "math/rand"
)

var IntRand func() int
var IntnRand func(int) int
var CryptRand func([]byte) (int, error)

func init() {
  ResetIntRand()
  ResetIntnRand()
  ResetCryptRand()
}

func ResetCryptRand() {
  CryptRand = func(b []byte) (int, error) { return rand.Read(b) }
}

func ForceCryptRand(forced []byte) {
  CryptRand = func(b []byte) (int, error) {
    n := copy(b, forced)
    return n, nil
  }
}

func ResetIntRand() {
  IntRand = func() int { return mr.Int() }
}

func ForceIntRand(forced int) {
  IntRand = func() int { return forced }
}

func ResetIntnRand() {
  IntnRand = func(n int) int { return mr.Intn(n) }
}

func ForceIntnRand(forced int) {
  IntnRand = func(_ int) int { return forced }
}
<commit_msg>Adding a seed to generate random numbers<commit_after>package nd

import (
  "time"
  "crypto/rand"
  mr "math/rand"
)

var IntRand func() int
var IntnRand func(int) int
var CryptRand func([]byte) (int, error)

func init() {
  mr.Seed(time.Now().UTC().UnixNano())
  ResetIntRand()
  ResetIntnRand()
  ResetCryptRand()
}

func ResetCryptRand() {
  CryptRand = func(b []byte) (int, error) { return rand.Read(b) }
}

func ForceCryptRand(forced []byte) {
  CryptRand = func(b []byte) (int, error) {
    n := copy(b, forced)
    return n, nil
  }
}

func ResetIntRand() {
  IntRand = func() int { return mr.Int() }
}

func ForceIntRand(forced int) {
  IntRand = func() int { return forced }
}

func ResetIntnRand() {
  IntnRand = func(n int) int { return mr.Intn(n) }
}

func ForceIntnRand(forced int) {
  IntnRand = func(_ int) int { return forced }
}
<|endoftext|>


<commit_before>// Package repr attempts to represent Go values in a form that can be copy-and-pasted into source
// code directly.
//
// Unfortunately some values (such as pointers to basic types) can not be represented directly in
// Go. These values will be represented as `&<value>`. eg. `&23`
package repr

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"reflect"
)

type reprOptions struct {
	indent string
}

func (r *reprOptions) nextIndent(indent string) string {
	if r.indent != "" {
		return indent + r.indent
	}
	return ""
}

func (r *reprOptions) thisIndent(indent string) string {
	if r.indent != "" {
		return indent
	}
	return ""
}

// Option modifies the default behaviour.
type Option func(o *reprOptions)

// Indent output by this much.
func Indent(indent string) Option { return func(o *reprOptions) { o.indent = indent } }

// Repr returns a string representing v.
func Repr(v interface{}, options ...Option) string {
	w := bytes.NewBuffer(nil)
	Write(w, v, options...)
	return w.String()
}

// Print v to os.Stdout on one line.
func Print(v interface{}, options ...Option) {
	Write(os.Stdout, v, options...)
	fmt.Fprintln(os.Stdout)
}

// Write writes a representation of v to w.
func Write(w io.Writer, v interface{}, options ...Option) {
	os := &reprOptions{}
	for _, o := range options {
		o(os)
	}
	reprValue(w, reflect.ValueOf(v), os, "")
}

func reprValue(w io.Writer, v reflect.Value, options *reprOptions, indent string) {
	in := options.thisIndent(indent)
	ni := options.nextIndent(indent)
	switch v.Kind() {
	case reflect.Slice, reflect.Array:
		fmt.Fprintf(w, "%s{", v.Type())
		if options.indent != "" && v.Len() != 0 {
			fmt.Fprintf(w, "\n")
		}
		for i := 0; i < v.Len(); i++ {
			e := v.Index(i)
			fmt.Fprintf(w, "%s", ni)
			reprValue(w, e, options, ni)
			if options.indent != "" {
				fmt.Fprintf(w, ",\n")
			} else if i < v.Len()-1 {
				fmt.Fprintf(w, ", ")
			}
		}
		fmt.Fprintf(w, "%s}", in)
	case reflect.Chan:
		fmt.Fprintf(w, "make(")
		fmt.Fprintf(w, "%s", v.Type())
		fmt.Fprintf(w, ", %d)", v.Cap())
	case reflect.Map:
		fmt.Fprintf(w, "%s{", v.Type())
		if options.indent != "" && v.Len() != 0 {
			fmt.Fprintf(w, "\n")
		}
		for i, k := range v.MapKeys() {
			kv := v.MapIndex(k)
			fmt.Fprintf(w, "%s", ni)
			reprValue(w, k, options, ni)
			fmt.Fprintf(w, ": ")
			reprValue(w, kv, options, in)
			if options.indent != "" {
				fmt.Fprintf(w, ",\n")
			} else if i < v.Len()-1 {
				fmt.Fprintf(w, ", ")
			}
		}
		fmt.Fprintf(w, "%s}", in)
	case reflect.Struct:
		fmt.Fprintf(w, "%s{", v.Type())
		if options.indent != "" && v.NumField() != 0 {
			fmt.Fprintf(w, "\n")
		}
		for i := 0; i < v.NumField(); i++ {
			t := v.Type().Field(i)
			f := v.Field(i)
			fmt.Fprintf(w, "%s%s: ", ni, t.Name)
			reprValue(w, f, options, ni)
			if options.indent != "" {
				fmt.Fprintf(w, ",\n")
			} else if i < v.NumField()-1 {
				fmt.Fprintf(w, ", ")
			}
		}
		fmt.Fprintf(w, "%s}", indent)
	case reflect.Ptr:
		if v.IsNil() {
			fmt.Fprintf(w, "nil")
			return
		}
		fmt.Fprintf(w, "&")
		reprValue(w, v.Elem(), options, indent)
	case reflect.String:
		fmt.Fprintf(w, "%q", v.Interface())
	case reflect.Interface:
		reprValue(w, v.Elem(), options, indent)
	default:
		fmt.Fprintf(w, "%v", v)
	}
}
<commit_msg>Fix indenting for maps.<commit_after>// Package repr attempts to represent Go values in a form that can be copy-and-pasted into source
// code directly.
//
// Unfortunately some values (such as pointers to basic types) can not be represented directly in
// Go. These values will be represented as `&<value>`. eg. `&23`
package repr

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"reflect"
)

type reprOptions struct {
	indent string
}

func (r *reprOptions) nextIndent(indent string) string {
	if r.indent != "" {
		return indent + r.indent
	}
	return ""
}

func (r *reprOptions) thisIndent(indent string) string {
	if r.indent != "" {
		return indent
	}
	return ""
}

// Option modifies the default behaviour.
type Option func(o *reprOptions)

// Indent output by this much.
func Indent(indent string) Option { return func(o *reprOptions) { o.indent = indent } }

// Repr returns a string representing v.
func Repr(v interface{}, options ...Option) string {
	w := bytes.NewBuffer(nil)
	Write(w, v, options...)
	return w.String()
}

// Print v to os.Stdout on one line.
func Print(v interface{}, options ...Option) {
	Write(os.Stdout, v, options...)
	fmt.Fprintln(os.Stdout)
}

// Write writes a representation of v to w.
func Write(w io.Writer, v interface{}, options ...Option) {
	os := &reprOptions{}
	for _, o := range options {
		o(os)
	}
	reprValue(w, reflect.ValueOf(v), os, "")
}

func reprValue(w io.Writer, v reflect.Value, options *reprOptions, indent string) {
	in := options.thisIndent(indent)
	ni := options.nextIndent(indent)
	switch v.Kind() {
	case reflect.Slice, reflect.Array:
		fmt.Fprintf(w, "%s{", v.Type())
		if options.indent != "" && v.Len() != 0 {
			fmt.Fprintf(w, "\n")
		}
		for i := 0; i < v.Len(); i++ {
			e := v.Index(i)
			fmt.Fprintf(w, "%s", ni)
			reprValue(w, e, options, ni)
			if options.indent != "" {
				fmt.Fprintf(w, ",\n")
			} else if i < v.Len()-1 {
				fmt.Fprintf(w, ", ")
			}
		}
		fmt.Fprintf(w, "%s}", in)
	case reflect.Chan:
		fmt.Fprintf(w, "make(")
		fmt.Fprintf(w, "%s", v.Type())
		fmt.Fprintf(w, ", %d)", v.Cap())
	case reflect.Map:
		fmt.Fprintf(w, "%s{", v.Type())
		if options.indent != "" && v.Len() != 0 {
			fmt.Fprintf(w, "\n")
		}
		for i, k := range v.MapKeys() {
			kv := v.MapIndex(k)
			fmt.Fprintf(w, "%s", ni)
			reprValue(w, k, options, ni)
			fmt.Fprintf(w, ": ")
			reprValue(w, kv, options, ni)
			if options.indent != "" {
				fmt.Fprintf(w, ",\n")
			} else if i < v.Len()-1 {
				fmt.Fprintf(w, ", ")
			}
		}
		fmt.Fprintf(w, "%s}", in)
	case reflect.Struct:
		fmt.Fprintf(w, "%s{", v.Type())
		if options.indent != "" && v.NumField() != 0 {
			fmt.Fprintf(w, "\n")
		}
		for i := 0; i < v.NumField(); i++ {
			t := v.Type().Field(i)
			f := v.Field(i)
			fmt.Fprintf(w, "%s%s: ", ni, t.Name)
			reprValue(w, f, options, ni)
			if options.indent != "" {
				fmt.Fprintf(w, ",\n")
			} else if i < v.NumField()-1 {
				fmt.Fprintf(w, ", ")
			}
		}
		fmt.Fprintf(w, "%s}", indent)
	case reflect.Ptr:
		if v.IsNil() {
			fmt.Fprintf(w, "nil")
			return
		}
		fmt.Fprintf(w, "&")
		reprValue(w, v.Elem(), options, indent)
	case reflect.String:
		fmt.Fprintf(w, "%q", v.Interface())
	case reflect.Interface:
		reprValue(w, v.Elem(), options, indent)
	default:
		fmt.Fprintf(w, "%v", v)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/crackcomm/go-clitable"
	"github.com/fatih/structs"
	"github.com/maliceio/go-plugin-utils/database/elasticsearch"
	"github.com/maliceio/go-plugin-utils/utils"
	"github.com/parnurzeal/gorequest"
	"github.com/urfave/cli"
)

// Version stores the plugin's version
var Version string

// BuildTime stores the plugin's build time
var BuildTime string

const (
	name     = "fileinfo"
	category = "metadata"
)

type pluginResults struct {
	ID       string   `structs:"id"`
	FileInfo FileInfo `structs:"fileinfo"`
}

// FileInfo json object
type FileInfo struct {
	SSDeep   string            `json:"ssdeep" structs:"ssdeep"`
	TRiD     []string          `json:"trid" structs:"trid"`
	Exiftool map[string]string `json:"exiftool" structs:"exiftool"`
}

// ParseExiftoolOutput convert exiftool output into JSON
func ParseExiftoolOutput(exifout string) map[string]string {

	var ignoreTags = []string{
		"Directory",
		"File Name",
		"File Permissions",
		"File Modification Date/Time",
	}

	lines := strings.Split(exifout, "\n")
	datas := make(map[string]string, len(lines))

	for _, line := range lines {
		keyvalue := strings.Split(line, ":")
		if len(keyvalue) != 2 {
			continue
		}
		if !utils.StringInSlice(strings.TrimSpace(keyvalue[0]), ignoreTags) {
			datas[strings.TrimSpace(utils.CamelCase(keyvalue[0]))] = strings.TrimSpace(keyvalue[1])
		}
	}

	return datas
}

// ParseSsdeepOutput convert ssdeep output into JSON
func ParseSsdeepOutput(ssdout string) string {

	// Break output into lines
	lines := strings.Split(ssdout, "\n")
	// Break second line into hash and path
	hashAndPath := strings.Split(lines[1], ",")

	return strings.TrimSpace(hashAndPath[0])
}

// ParseTRiDOutput convert trid output into JSON
func ParseTRiDOutput(tridout string) []string {

	keepLines := []string{}

	lines := strings.Split(tridout, "\n")
	lines = lines[6:]
	// fmt.Println(lines)

	for _, line := range lines {
		if len(strings.TrimSpace(line)) != 0 {
			keepLines = append(keepLines, strings.TrimSpace(line))
		}
	}

	return keepLines
}

func printStatus(resp gorequest.Response, body string, errs []error) {
	fmt.Println(resp.Status)
}

func printMarkDownTable(finfo FileInfo) {

	// print ssdeep
	fmt.Println("#### SSDeep")
	fmt.Println(finfo.SSDeep)
	fmt.Println()
	// print trid
	fmt.Println("#### TRiD")
	table := clitable.New([]string{"TRiD", ""})
	for _, trd := range finfo.TRiD {
		fmt.Println(" - ", trd)
	}
	fmt.Println()
	// print exiftool
	fmt.Println("#### Exiftool")
	table = clitable.New([]string{"Field", "Value"})
	for key, value := range finfo.Exiftool {
		table.AddRow(map[string]interface{}{"Field": key, "Value": value})
	}
	table.Markdown = true
	table.Print()
}

var appHelpTemplate = `Usage: {{.Name}} {{if .Flags}}[OPTIONS] {{end}}COMMAND [arg...]

{{.Usage}}

Version: {{.Version}}{{if or .Author .Email}}

Author:{{if .Author}}
  {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}}
  {{.Email}}{{end}}{{end}}
{{if .Flags}}
Options:
  {{range .Flags}}{{.}}
  {{end}}{{end}}
Commands:
  {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
  {{end}}
Run '{{.Name}} COMMAND --help' for more information on a command.
`

func main() {
	cli.AppHelpTemplate = appHelpTemplate
	app := cli.NewApp()
	app.Name = "fileinfo"
	app.Author = "blacktop"
	app.Email = "https://github.com/blacktop"
	app.Version = Version + ", BuildTime: " + BuildTime
	app.Compiled, _ = time.Parse("20060102", BuildTime)
	app.Usage = "Malice File Info Plugin - ssdeep/exiftool/TRiD"
	var elasitcsearch string
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose, V",
			Usage: "verbose output",
		},
		cli.BoolFlag{
			Name:  "table, t",
			Usage: "output as Markdown table",
		},
		cli.BoolFlag{
			Name:   "post, p",
			Usage:  "POST results to Malice webhook",
			EnvVar: "MALICE_ENDPOINT",
		},
		cli.BoolFlag{
			Name:   "proxy, x",
			Usage:  "proxy settings for Malice webhook endpoint",
			EnvVar: "MALICE_PROXY",
		},
		cli.StringFlag{
			Name:        "elasitcsearch",
			Value:       "",
			Usage:       "elasitcsearch address for Malice to store results",
			EnvVar:      "MALICE_ELASTICSEARCH",
			Destination: &elasitcsearch,
		},
	}
	app.Action = func(c *cli.Context) error {
		path := c.Args().First()

		if _, err := os.Stat(path); os.IsNotExist(err) {
			utils.Assert(err)
		}

		if c.Bool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		fileInfo := FileInfo{
			SSDeep:   ParseSsdeepOutput(utils.RunCommand("ssdeep", path)),
			TRiD:     ParseTRiDOutput(utils.RunCommand("trid", path)),
			Exiftool: ParseExiftoolOutput(utils.RunCommand("exiftool", path)),
		}

		// upsert into Database
		elasticsearch.InitElasticSearch()
		elasticsearch.WritePluginResultsToDatabase(elasticsearch.PluginResults{
			ID:       utils.Getopt("MALICE_SCANID", utils.GetSHA256(path)),
			Name:     name,
			Category: category,
			Data:     structs.Map(fileInfo),
		})

		if c.Bool("table") {
			printMarkDownTable(fileInfo)
		} else {
			fileInfoJSON, err := json.Marshal(fileInfo)
			utils.Assert(err)
			if c.Bool("post") {
				request := gorequest.New()
				if c.Bool("proxy") {
					request = gorequest.New().Proxy(os.Getenv("MALICE_PROXY"))
				}
				request.Post(os.Getenv("MALICE_ENDPOINT")).
					Set("Task", path).
					Send(fileInfoJSON).
					End(printStatus)
			}
			// write to stdout
			fmt.Println(string(fileInfoJSON))
		}
		return nil
	}

	err := app.Run(os.Args)
	utils.Assert(err)
}
<commit_msg>handle possible failures of the fileinfo tools<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/crackcomm/go-clitable"
	"github.com/fatih/structs"
	"github.com/maliceio/go-plugin-utils/database/elasticsearch"
	"github.com/maliceio/go-plugin-utils/utils"
	"github.com/parnurzeal/gorequest"
	"github.com/urfave/cli"
)

// Version stores the plugin's version
var Version string

// BuildTime stores the plugin's build time
var BuildTime string

const (
	name     = "fileinfo"
	category = "metadata"
)

type pluginResults struct {
	ID       string   `structs:"id"`
	FileInfo FileInfo `structs:"fileinfo"`
}

// FileInfo json object
type FileInfo struct {
	SSDeep   string            `json:"ssdeep" structs:"ssdeep"`
	TRiD     []string          `json:"trid" structs:"trid"`
	Exiftool map[string]string `json:"exiftool" structs:"exiftool"`
}

// ParseExiftoolOutput convert exiftool output into JSON
func ParseExiftoolOutput(exifout string) map[string]string {

	var ignoreTags = []string{
		"Directory",
		"File Name",
		"File Permissions",
		"File Modification Date/Time",
	}

	lines := strings.Split(exifout, "\n")
	if !(len(lines) > 1) {
		return ""
	}
	datas := make(map[string]string, len(lines))

	for _, line := range lines {
		keyvalue := strings.Split(line, ":")
		if len(keyvalue) != 2 {
			continue
		}
		if !utils.StringInSlice(strings.TrimSpace(keyvalue[0]), ignoreTags) {
			datas[strings.TrimSpace(utils.CamelCase(keyvalue[0]))] = strings.TrimSpace(keyvalue[1])
		}
	}

	return datas
}

// ParseSsdeepOutput convert ssdeep output into JSON
func ParseSsdeepOutput(ssdout string) string {

	// Break output into lines
	lines := strings.Split(ssdout, "\n")
	if !(len(lines) > 1) {
		return ""
	}
	// Break second line into hash and path
	hashAndPath := strings.Split(lines[1], ",")

	return strings.TrimSpace(hashAndPath[0])
}

// ParseTRiDOutput convert trid output into JSON
func ParseTRiDOutput(tridout string) []string {

	keepLines := []string{}

	lines := strings.Split(tridout, "\n")
	if !(len(lines) > 1) {
		return ""
	}
	lines = lines[6:]
	// fmt.Println(lines)

	for _, line := range lines {
		if len(strings.TrimSpace(line)) != 0 {
			keepLines = append(keepLines, strings.TrimSpace(line))
		}
	}

	return keepLines
}

func printStatus(resp gorequest.Response, body string, errs []error) {
	fmt.Println(resp.Status)
}

func printMarkDownTable(finfo FileInfo) {

	// print ssdeep
	fmt.Println("#### SSDeep")
	fmt.Println(finfo.SSDeep)
	fmt.Println()
	// print trid
	fmt.Println("#### TRiD")
	table := clitable.New([]string{"TRiD", ""})
	for _, trd := range finfo.TRiD {
		fmt.Println(" - ", trd)
	}
	fmt.Println()
	// print exiftool
	fmt.Println("#### Exiftool")
	table = clitable.New([]string{"Field", "Value"})
	for key, value := range finfo.Exiftool {
		table.AddRow(map[string]interface{}{"Field": key, "Value": value})
	}
	table.Markdown = true
	table.Print()
}

var appHelpTemplate = `Usage: {{.Name}} {{if .Flags}}[OPTIONS] {{end}}COMMAND [arg...]

{{.Usage}}

Version: {{.Version}}{{if or .Author .Email}}

Author:{{if .Author}}
  {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}}
  {{.Email}}{{end}}{{end}}
{{if .Flags}}
Options:
  {{range .Flags}}{{.}}
  {{end}}{{end}}
Commands:
  {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
  {{end}}
Run '{{.Name}} COMMAND --help' for more information on a command.
`

func main() {
	cli.AppHelpTemplate = appHelpTemplate
	app := cli.NewApp()
	app.Name = "fileinfo"
	app.Author = "blacktop"
	app.Email = "https://github.com/blacktop"
	app.Version = Version + ", BuildTime: " + BuildTime
	app.Compiled, _ = time.Parse("20060102", BuildTime)
	app.Usage = "Malice File Info Plugin - ssdeep/exiftool/TRiD"
	var elasitcsearch string
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose, V",
			Usage: "verbose output",
		},
		cli.BoolFlag{
			Name:  "table, t",
			Usage: "output as Markdown table",
		},
		cli.BoolFlag{
			Name:   "post, p",
			Usage:  "POST results to Malice webhook",
			EnvVar: "MALICE_ENDPOINT",
		},
		cli.BoolFlag{
			Name:   "proxy, x",
			Usage:  "proxy settings for Malice webhook endpoint",
			EnvVar: "MALICE_PROXY",
		},
		cli.StringFlag{
			Name:        "elasitcsearch",
			Value:       "",
			Usage:       "elasitcsearch address for Malice to store results",
			EnvVar:      "MALICE_ELASTICSEARCH",
			Destination: &elasitcsearch,
		},
	}
	app.Action = func(c *cli.Context) error {
		path := c.Args().First()

		if _, err := os.Stat(path); os.IsNotExist(err) {
			utils.Assert(err)
		}

		if c.Bool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		fileInfo := FileInfo{
			SSDeep:   ParseSsdeepOutput(utils.RunCommand("ssdeep", path)),
			TRiD:     ParseTRiDOutput(utils.RunCommand("trid", path)),
			Exiftool: ParseExiftoolOutput(utils.RunCommand("exiftool", path)),
		}

		// upsert into Database
		elasticsearch.InitElasticSearch()
		elasticsearch.WritePluginResultsToDatabase(elasticsearch.PluginResults{
			ID:       utils.Getopt("MALICE_SCANID", utils.GetSHA256(path)),
			Name:     name,
			Category: category,
			Data:     structs.Map(fileInfo),
		})

		if c.Bool("table") {
			printMarkDownTable(fileInfo)
		} else {
			fileInfoJSON, err := json.Marshal(fileInfo)
			utils.Assert(err)
			if c.Bool("post") {
				request := gorequest.New()
				if c.Bool("proxy") {
					request = gorequest.New().Proxy(os.Getenv("MALICE_PROXY"))
				}
				request.Post(os.Getenv("MALICE_ENDPOINT")).
					Set("Task", path).
					Send(fileInfoJSON).
					End(printStatus)
			}
			// write to stdout
			fmt.Println(string(fileInfoJSON))
		}
		return nil
	}

	err := app.Run(os.Args)
	utils.Assert(err)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
)

var authKey = []byte("somesecretauth")
var store sessions.Store
var pool *Pool
var clients map[int64]*Client

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      int64
	client1 *Client
	client2 *Client
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		fmt.Println("match found")

		b := make([]byte, 8)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 8 {
			return
		}
		crId, _ := binary.Varint(b)

		room := &Room{crId, c1, c2}

		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	var uid int64
	var b []byte

	if userid == nil {
		b = make([]byte, 8)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 8 {
			return 0, err
		}
		session.Values["userid"] = b
		session.Save(r, w)
	} else {
		b = []byte(userid.([]uint8))
	}
	uid, _ = binary.Varint(b)
	return uid, nil
}

func main() {
	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)
	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)
	http.ListenAndServe(":8080", nil)
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

  	fmt.Println("uid: ", uid)
	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string),
		retChan: retChan,
	}
	clients[uid] = client
	pool.in <- client

	fmt.Println("added ", uid, " to queue")
	chatroom := <-retChan

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":", chatroom.id, "}")
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	fmt.Fprint(w, uid)
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	message := "some string"

	client := clients[uid]

	client.out <- message

	fmt.Fprint(w, "{\"status\":\"success\"}")
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	message := <-clients[uid].in

	fmt.Fprint(w, message)
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<commit_msg>sql login<commit_after>package main

import (
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"encoding/json"
	"github.com/ziutek/mymysql/mysql"
	_ "github.com/ziutek/mymysql/native"
	_ "github.com/ziutek/mymysql/thrsafe"
	"runtime"
)

const MESSAGE_QUEUE_SIZE = 10

var authKey = []byte("somesecretauth")
var store sessions.Store
var pool *Pool
var clients map[int64]*Client

var db = mysql.New("tcp", "", "localhost:3306", "root", "", "suitup")
// var tv syscall.Timeval

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      int64
	client1 *Client
	client2 *Client
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		fmt.Println("match found")

		b := make([]byte, 8)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 8 {
			return
		}
		crId, _ := binary.Varint(b)

		room := &Room{crId, c1, c2}

		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	var uid int64
	var b []byte

	if userid == nil {
		b = make([]byte, 8)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 8 {
			return 0, err
		}
		session.Values["userid"] = b
		session.Save(r, w)
	} else {
		b = []byte(userid.([]uint8))
	}
	uid, _ = binary.Varint(b)
	return uid, nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	fmt.Println(1)
	err := db.Connect()
	fmt.Println(2)
	handleError(err)
	fmt.Println(3)
	defer db.Close()
	fmt.Println(4)

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)
	http.ListenAndServe(":8080", nil)
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

  	fmt.Println("uid: ", uid)
	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}
	clients[uid] = client
	pool.in <- client

	fmt.Println("added ", uid, " to queue")
	chatroom := <- retChan

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":", chatroom.id, "}")
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	fmt.Fprint(w, uid)
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	message := "some string"

	client := clients[uid]
	if client != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}")
	} else {
		fmt.Fprint(w, "{\"status\":\"failure\"}")
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	client := clients[uid]
	if client != nil {
		fmt.Println("waiting")
		message := <- clients[uid].in
		fmt.Println("received")
		fmt.Fprint(w, message)
	} else {
		fmt.Fprint(w, "{\"status\":\"failure\"}")
	}
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		row, _, err := db.QueryFirst("SELECT id FROM users WHERE facebook_id='%d';", uid)
		handleError(err)

		if row != nil {
			fmt.Fprint(w, "{\"status\":\"success\",\"uid\":", row.Str(0), "}")
		} else {
			regStmt, err := db.Prepare("INSERT INTO users (facebook_id, username, email, level, points) VALUES(?, ?, ?, ?, ?);")
			handleError(err)
			regStmt.Run(uid, "", "", 0, 0)
			fmt.Fprint(w, "{\"status\":\"success\"}")
		}
	} else {
		fmt.Fprint(w, "{\"status\":\"failure\"}")
	}
}

func readHttpBody(response *http.Response) string {

	fmt.Println("Reading body")

	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	fmt.Println("Uncached response GET")
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

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

}

func GetMe(token string) string {
	fmt.Println("Getting me")
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>