<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// A command that reads all blobs necessary for verifying the directory
// structure rooted at a set of backup root scores, ensuring that the entire
// directory structure is intact in GCS.
//
// Optionally, all file content is also read and verified. This is less
// important than verifying directory connectedness if we trust that GCS does
// not corrupt object metadata (where we store expected CRC32C and MD5) and
// does correctly report the object's CRC32C and MD5 sums in listings,
// verifying them periodically.
//
// Output is of the following form:
//
//     <timestamp> <node> [<child node> ...]
//
// where:
//
//  *  Timestamps are formatted according to time.RFC3339.
//
//  *  Node names have one of two forms:
//
//     *   Nodes of the form "d:<hex score>" represent the directory listing
//         contained within the blob of the given score.
//
//     *   Nodes of the form "f:<hex score>" represent a piece of a file,
//         contained within the blob of the given score.
//
// An output line for a directory node means that at the given timestamp we
// certified that a piece of content with the given score was parseable as a
// directory listing that referred to the given scores for its direct children.
//
// An output line for a file node means that at the given timestamp we
// certified that a piece of content with the given score was parseable as a
// piece of a file. File nodes never have children.

package main

import (
	"fmt"
	"log"
	"runtime"
	"strings"
	"time"

	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/graph"
	"github.com/jacobsa/comeback/util"
	"github.com/jacobsa/comeback/verify"
	"github.com/jacobsa/comeback/wiring"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

var cmdVerify = &Command{
	Name: "verify",
}

// TODO(jacobsa): Get these automatically from the registry.
var fRoots = cmdVerify.Flags.String(
	"roots",
	"",
	"Comma-separated list of backup root scores to verify.")

var fFast = cmdVerify.Flags.Bool(
	"fast",
	false,
	"When set, don't verify file content.")

func init() {
	cmdVerify.Run = runVerify // Break flag-related dependency loop.
}

////////////////////////////////////////////////////////////////////////
// Visitor types
////////////////////////////////////////////////////////////////////////

type verifyRecord struct {
	t        time.Time
	node     string
	adjacent []string
}

// A visitor that writes the information it gleans from the wrapped visitor to
// a channel.
type snoopingVisitor struct {
	records chan<- verifyRecord
	wrapped graph.Visitor
}

func (v *snoopingVisitor) Visit(
	ctx context.Context,
	node string) (adjacent []string, err error) {
	// Call through.
	adjacent, err = v.wrapped.Visit(ctx, node)
	if err != nil {
		return
	}

	// Write out a record.
	r := verifyRecord{
		t:        time.Now(),
		node:     node,
		adjacent: adjacent,
	}

	select {
	case <-ctx.Done():
		err = ctx.Err()
		return

	case v.records <- r:
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Output
////////////////////////////////////////////////////////////////////////

// Print output based on the visitor results arriving on the supplied channel.
func formatVerifyOutput(r verifyRecord) (s string) {
	var extra string
	if len(r.adjacent) != 0 {
		extra = fmt.Sprintf(" %s", strings.Join(r.adjacent, " "))
	}

	s = fmt.Sprintf(
		"%s %s%s",
		r.t.Format(time.RFC3339),
		r.node,
		extra)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// List blob.ListScores, but returns a slice instead of writing into a channel.
func listAllScores(
	ctx context.Context,
	bucket gcs.Bucket,
	namePrefix string) (scores []blob.Score, err error) {
	b := syncutil.NewBundle(context.Background())
	defer func() { err = b.Join() }()

	// List scores into a channel.
	scoreChan := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(scoreChan)
		err = blob.ListScores(ctx, bucket, wiring.BlobObjectNamePrefix, scoreChan)
		if err != nil {
			err = fmt.Errorf("ListScores: %v", err)
			return
		}

		return
	})

	// Accumulate into the slice.
	b.Add(func(ctx context.Context) (err error) {
		for score := range scoreChan {
			scores = append(scores, score)
		}

		return
	})

	return
}

////////////////////////////////////////////////////////////////////////
// Verify
////////////////////////////////////////////////////////////////////////

// Run the verification pipeline. Return a count of the number of scores
// verified and the number skipped due to readFiles being false.
func verifyImpl(
	ctx context.Context,
	readFiles bool,
	rootScores []blob.Score,
	knownScores []blob.Score,
	blobStore blob.Store) (nodesVerified uint64, nodesSkipped uint64, err error) {
	b := syncutil.NewBundle(ctx)

	// Visit every node in the graph, snooping on the graph structure into a
	// channel.
	visitorRecords := make(chan verifyRecord, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(visitorRecords)

		visitor := verify.NewVisitor(
			readFiles,
			knownScores,
			blobStore)

		visitor = &snoopingVisitor{
			wrapped: visitor,
			records: visitorRecords,
		}

		// Format root node names.
		var roots []string
		for _, score := range rootScores {
			roots = append(roots, verify.FormatNodeName(true, score))
		}

		// Traverse starting at the specified roots. Use an "experimentally
		// determined" parallelism, which in theory should depend on bandwidth-delay
		// products but in practice comes down to when the OS gets cranky about open
		// files.
		const parallelism = 128

		err = graph.Traverse(
			context.Background(),
			parallelism,
			roots,
			visitor)

		if err != nil {
			err = fmt.Errorf("Traverse: %v", err)
			return
		}

		return
	})

	// Count and output the nodes visited, filtering out file nodes if we're not
	// actually reading and verifying them.
	b.Add(func(ctx context.Context) (err error) {
		for r := range visitorRecords {
			var dir bool
			dir, _, err = verify.ParseNodeName(r.node)
			if err != nil {
				err = fmt.Errorf("ParseNodeName(%q): %v", r.node, err)
				return
			}

			// Skip files if appropriate.
			if !readFiles && !dir {
				nodesSkipped++
				continue
			}

			// Increment the count and output the information.
			nodesVerified++
			fmt.Println(formatVerifyOutput(r))
		}

		return
	})

	err = b.Join()
	return
}

func runVerify(args []string) {
	// Allow parallelism.
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Die on error.
	var err error
	defer func() {
		if err != nil {
			log.Fatalln(err)
		}
	}()

	// Read flags.
	readFiles := !*fFast

	if *fRoots == "" {
		err = fmt.Errorf("You must set --roots.")
		return
	}

	rootHexScores := strings.Split(*fRoots, ",")
	var rootScores []blob.Score
	for _, hexScore := range rootHexScores {
		var score blob.Score
		score, err = blob.ParseHexScore(hexScore)
		if err != nil {
			err = fmt.Errorf("Invalid root %q: %v", hexScore, err)
			return
		}

		rootScores = append(rootScores, score)
	}

	// Grab dependencies.
	bucket := getBucket()
	crypter := getCrypter()

	// Create a blob store.
	blobStore, err := wiring.MakeBlobStore(
		bucket,
		crypter,
		util.NewStringSet())

	if err != nil {
		err = fmt.Errorf("MakeBlobStore: %v", err)
		return
	}

	// List all scores in the bucket, verifying the object record metadata in the
	// process.
	log.Println("Listing scores...")
	knownScores, err := listAllScores(
		context.Background(),
		bucket,
		wiring.BlobObjectNamePrefix)

	if err != nil {
		err = fmt.Errorf("listAllScores: %v", err)
		return
	}

	log.Printf("Listed %d scores.", len(knownScores))

	// Run the rest of the pipeline.
	nodesVerified, nodesSkipped, err := verifyImpl(
		context.Background(),
		readFiles,
		rootScores,
		knownScores,
		blobStore)

	if err != nil {
		err = fmt.Errorf("verifyImpl: %v", err)
		return
	}

	log.Printf(
		"Successfully verified %d nodes (%d skipped due to fast mode).",
		nodesVerified,
		nodesSkipped)

	return
}
<commit_msg>parseVerifyRecord<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// A command that reads all blobs necessary for verifying the directory
// structure rooted at a set of backup root scores, ensuring that the entire
// directory structure is intact in GCS.
//
// Optionally, all file content is also read and verified. This is less
// important than verifying directory connectedness if we trust that GCS does
// not corrupt object metadata (where we store expected CRC32C and MD5) and
// does correctly report the object's CRC32C and MD5 sums in listings,
// verifying them periodically.
//
// Output is of the following form:
//
//     <timestamp> <node> [<child node> ...]
//
// where:
//
//  *  Timestamps are formatted according to time.RFC3339.
//
//  *  Node names have one of two forms:
//
//     *   Nodes of the form "d:<hex score>" represent the directory listing
//         contained within the blob of the given score.
//
//     *   Nodes of the form "f:<hex score>" represent a piece of a file,
//         contained within the blob of the given score.
//
// An output line for a directory node means that at the given timestamp we
// certified that a piece of content with the given score was parseable as a
// directory listing that referred to the given scores for its direct children.
//
// An output line for a file node means that at the given timestamp we
// certified that a piece of content with the given score was parseable as a
// piece of a file. File nodes never have children.

package main

import (
	"bytes"
	"fmt"
	"log"
	"runtime"
	"strings"
	"time"

	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/graph"
	"github.com/jacobsa/comeback/util"
	"github.com/jacobsa/comeback/verify"
	"github.com/jacobsa/comeback/wiring"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

var cmdVerify = &Command{
	Name: "verify",
}

// TODO(jacobsa): Get these automatically from the registry.
var fRoots = cmdVerify.Flags.String(
	"roots",
	"",
	"Comma-separated list of backup root scores to verify.")

var fFast = cmdVerify.Flags.Bool(
	"fast",
	false,
	"When set, don't verify file content.")

func init() {
	cmdVerify.Run = runVerify // Break flag-related dependency loop.
}

////////////////////////////////////////////////////////////////////////
// Visitor types
////////////////////////////////////////////////////////////////////////

type verifyRecord struct {
	t        time.Time
	node     string
	adjacent []string
}

// A visitor that writes the information it gleans from the wrapped visitor to
// a channel.
type snoopingVisitor struct {
	records chan<- verifyRecord
	wrapped graph.Visitor
}

func (v *snoopingVisitor) Visit(
	ctx context.Context,
	node string) (adjacent []string, err error) {
	// Call through.
	adjacent, err = v.wrapped.Visit(ctx, node)
	if err != nil {
		return
	}

	// Write out a record.
	r := verifyRecord{
		t:        time.Now(),
		node:     node,
		adjacent: adjacent,
	}

	select {
	case <-ctx.Done():
		err = ctx.Err()
		return

	case v.records <- r:
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Output
////////////////////////////////////////////////////////////////////////

// Print output based on the visitor results arriving on the supplied channel.
func formatVerifyOutput(r verifyRecord) (s string) {
	var extra string
	if len(r.adjacent) != 0 {
		extra = fmt.Sprintf(" %s", strings.Join(r.adjacent, " "))
	}

	s = fmt.Sprintf(
		"%s %s%s",
		r.t.Format(time.RFC3339),
		r.node,
		extra)

	return
}

// Parse the supplied line (without line break) previously output by the verify
// command.
func parseVerifyRecord(line []byte) (r verifyRecord, err error) {
	// We expect space-separate components.
	components := bytes.Split(line, []byte{' '})
	if len(components) < 2 {
		err = fmt.Errorf(
			"Expected at least two components, got %d.",
			len(components))

		return
	}

	// The first should be the timestmap.
	r.t, err = time.Parse(time.RFC3339, string(components[0]))
	if err != nil {
		err = fmt.Errorf("time.Parse(%q): %v", components[0], err)
		return
	}

	// The next should be the node name.
	r.node = string(components[1])

	// The rest should be adjacent node names.
	for i := 2; i < len(components); i++ {
		r.adjacent = append(r.adjacent, string(components[i]))
	}

	// Make sure all of the node names are legal.
	allNodes := make([]string, 1+len(r.adjacent))
	allNodes[0] = r.node
	copy(allNodes[1:], r.adjacent)

	for _, n := range allNodes {
		_, _, err = verify.ParseNodeName(n)
		if err != nil {
			err = fmt.Errorf("ParseNodeName(%q): %v", n, err)
			return
		}
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// List blob.ListScores, but returns a slice instead of writing into a channel.
func listAllScores(
	ctx context.Context,
	bucket gcs.Bucket,
	namePrefix string) (scores []blob.Score, err error) {
	b := syncutil.NewBundle(context.Background())
	defer func() { err = b.Join() }()

	// List scores into a channel.
	scoreChan := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(scoreChan)
		err = blob.ListScores(ctx, bucket, wiring.BlobObjectNamePrefix, scoreChan)
		if err != nil {
			err = fmt.Errorf("ListScores: %v", err)
			return
		}

		return
	})

	// Accumulate into the slice.
	b.Add(func(ctx context.Context) (err error) {
		for score := range scoreChan {
			scores = append(scores, score)
		}

		return
	})

	return
}

////////////////////////////////////////////////////////////////////////
// Verify
////////////////////////////////////////////////////////////////////////

// Run the verification pipeline. Return a count of the number of scores
// verified and the number skipped due to readFiles being false.
func verifyImpl(
	ctx context.Context,
	readFiles bool,
	rootScores []blob.Score,
	knownScores []blob.Score,
	blobStore blob.Store) (nodesVerified uint64, nodesSkipped uint64, err error) {
	b := syncutil.NewBundle(ctx)

	// Visit every node in the graph, snooping on the graph structure into a
	// channel.
	visitorRecords := make(chan verifyRecord, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(visitorRecords)

		visitor := verify.NewVisitor(
			readFiles,
			knownScores,
			blobStore)

		visitor = &snoopingVisitor{
			wrapped: visitor,
			records: visitorRecords,
		}

		// Format root node names.
		var roots []string
		for _, score := range rootScores {
			roots = append(roots, verify.FormatNodeName(true, score))
		}

		// Traverse starting at the specified roots. Use an "experimentally
		// determined" parallelism, which in theory should depend on bandwidth-delay
		// products but in practice comes down to when the OS gets cranky about open
		// files.
		const parallelism = 128

		err = graph.Traverse(
			context.Background(),
			parallelism,
			roots,
			visitor)

		if err != nil {
			err = fmt.Errorf("Traverse: %v", err)
			return
		}

		return
	})

	// Count and output the nodes visited, filtering out file nodes if we're not
	// actually reading and verifying them.
	b.Add(func(ctx context.Context) (err error) {
		for r := range visitorRecords {
			var dir bool
			dir, _, err = verify.ParseNodeName(r.node)
			if err != nil {
				err = fmt.Errorf("ParseNodeName(%q): %v", r.node, err)
				return
			}

			// Skip files if appropriate.
			if !readFiles && !dir {
				nodesSkipped++
				continue
			}

			// Increment the count and output the information.
			nodesVerified++
			fmt.Println(formatVerifyOutput(r))
		}

		return
	})

	err = b.Join()
	return
}

func runVerify(args []string) {
	// Allow parallelism.
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Die on error.
	var err error
	defer func() {
		if err != nil {
			log.Fatalln(err)
		}
	}()

	// Read flags.
	readFiles := !*fFast

	if *fRoots == "" {
		err = fmt.Errorf("You must set --roots.")
		return
	}

	rootHexScores := strings.Split(*fRoots, ",")
	var rootScores []blob.Score
	for _, hexScore := range rootHexScores {
		var score blob.Score
		score, err = blob.ParseHexScore(hexScore)
		if err != nil {
			err = fmt.Errorf("Invalid root %q: %v", hexScore, err)
			return
		}

		rootScores = append(rootScores, score)
	}

	// Grab dependencies.
	bucket := getBucket()
	crypter := getCrypter()

	// Create a blob store.
	blobStore, err := wiring.MakeBlobStore(
		bucket,
		crypter,
		util.NewStringSet())

	if err != nil {
		err = fmt.Errorf("MakeBlobStore: %v", err)
		return
	}

	// List all scores in the bucket, verifying the object record metadata in the
	// process.
	log.Println("Listing scores...")
	knownScores, err := listAllScores(
		context.Background(),
		bucket,
		wiring.BlobObjectNamePrefix)

	if err != nil {
		err = fmt.Errorf("listAllScores: %v", err)
		return
	}

	log.Printf("Listed %d scores.", len(knownScores))

	// Run the rest of the pipeline.
	nodesVerified, nodesSkipped, err := verifyImpl(
		context.Background(),
		readFiles,
		rootScores,
		knownScores,
		blobStore)

	if err != nil {
		err = fmt.Errorf("verifyImpl: %v", err)
		return
	}

	log.Printf(
		"Successfully verified %d nodes (%d skipped due to fast mode).",
		nodesVerified,
		nodesSkipped)

	return
}
<|endoftext|>


<commit_before>package controllers

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"net/http"
	"net/mail"
	"text/template"
	"time"

	"github.com/coduno/app/model"
	"github.com/coduno/engine/passenger"
	"github.com/coduno/engine/util/password"
	"google.golang.org/appengine/datastore"
	appmail "google.golang.org/appengine/mail"

	"golang.org/x/net/context"
)

var invitation *template.Template

func init() {
	var err error
	invitation, err = template.ParseFiles("./mail/template.invitation")
	if err != nil {
		panic(err)
	}
}

// Invitation handles the creation of a new invitation and sends an e-mail to
// the user.
func Invitation(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, errors.New("permission denied")
	}
	cKey := p.UserKey.Parent()
	if cKey == nil {
		return http.StatusUnauthorized, errors.New("permission denied")
	}
	var company model.Company
	datastore.Get(ctx, cKey, &company)

	// TODO(flowlo): Also check whether the parent of the current user is the
	// parent of the challenge (if any).

	if r.Method == "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	var params = struct {
		Address   string
		Challenge *datastore.Key
	}{}
	if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
		return http.StatusBadRequest, err
	}

	address, err := mail.ParseAddress(params.Address)
	if err != nil {
		return http.StatusBadRequest, err
	}

	var users model.Users
	keys, err := model.NewQueryForUser().
		Filter("Address=", address.Address).
		KeysOnly().
		Limit(1).
		GetAll(ctx, &users)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	var key *datastore.Key
	var user model.User
	if len(keys) == 1 {
		key = keys[0]
		user = users[0]
	} else {
		user = model.User{Address: *address}
		key, err = datastore.Put(ctx, key, &user)
		if err != nil {
			return http.StatusInternalServerError, err
		}
	}

	// TODO(flowlo): Generate token with its own util.
	tokenValue, err := password.Generate(0)
	if err != nil {
		return http.StatusInternalServerError, err
	}
	now := time.Now()
	accessToken := model.AccessToken{
		Value:        string(tokenValue),
		Creation:     now,
		Modification: now,
		Expiry:       now.Add(time.Hour * 24 * 365),
		Description:  "Initialization Token",
	}

	token := base64.URLEncoding.EncodeToString([]byte(params.Challenge.Encode() + accessToken.Value))

	i := model.Invitation{
		User: key,
	}

	buf := new(bytes.Buffer)
	if err = invitation.Execute(buf, struct {
		UserAddress, CompanyAddress mail.Address
		Token                       string
	}{
		user.Address,
		company.Address,
		token,
	}); err != nil {
		return http.StatusInternalServerError, err
	}

	if err = appmail.Send(ctx, &appmail.Message{
		Sender:  "Lorenz Leutgeb <lorenz.leutgeb@cod.uno>",
		To:      []string{user.Address.String()},
		Subject: "We challenge you!",
		Body:    buf.String(),
	}); err != nil {
		return http.StatusInternalServerError, err
	}

	key, err = i.Save(ctx)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(i.Key(key))
	return http.StatusOK, nil
}
<commit_msg>ctrl: Create Profile for user upon Invitation<commit_after>package controllers

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"net/http"
	"net/mail"
	"text/template"
	"time"

	"github.com/coduno/app/model"
	"github.com/coduno/engine/passenger"
	"github.com/coduno/engine/util/password"
	"google.golang.org/appengine/datastore"
	appmail "google.golang.org/appengine/mail"

	"golang.org/x/net/context"
)

var invitation *template.Template

func init() {
	var err error
	invitation, err = template.ParseFiles("./mail/template.invitation")
	if err != nil {
		panic(err)
	}
}

// Invitation handles the creation of a new invitation and sends an e-mail to
// the user.
func Invitation(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, errors.New("permission denied")
	}
	cKey := p.UserKey.Parent()
	if cKey == nil {
		return http.StatusUnauthorized, errors.New("permission denied")
	}
	var company model.Company
	datastore.Get(ctx, cKey, &company)

	// TODO(flowlo): Also check whether the parent of the current user is the
	// parent of the challenge (if any).

	if r.Method == "GET" {
		return http.StatusMethodNotAllowed, nil
	}

	var params = struct {
		Address   string
		Challenge *datastore.Key
	}{}
	if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
		return http.StatusBadRequest, err
	}

	address, err := mail.ParseAddress(params.Address)
	if err != nil {
		return http.StatusBadRequest, err
	}

	var users model.Users
	keys, err := model.NewQueryForUser().
		Filter("Address=", address.Address).
		KeysOnly().
		Limit(1).
		GetAll(ctx, &users)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	var key *datastore.Key
	var user model.User
	if len(keys) == 1 {
		key = keys[0]
		user = users[0]
	} else {
		user = model.User{Address: *address}
		key, err = datastore.Put(ctx, key, &user)
		if err != nil {
			return http.StatusInternalServerError, err
		}
		profile := model.Profile{}
		if _, err = profile.SaveWithParent(ctx, key); err != nil {
			return http.StatusInternalServerError, err
		}
	}

	// TODO(flowlo): Generate token with its own util.
	tokenValue, err := password.Generate(0)
	if err != nil {
		return http.StatusInternalServerError, err
	}
	now := time.Now()
	accessToken := model.AccessToken{
		Value:        string(tokenValue),
		Creation:     now,
		Modification: now,
		Expiry:       now.Add(time.Hour * 24 * 365),
		Description:  "Initialization Token",
	}

	token := base64.URLEncoding.EncodeToString([]byte(params.Challenge.Encode() + accessToken.Value))

	i := model.Invitation{
		User: key,
	}

	buf := new(bytes.Buffer)
	if err = invitation.Execute(buf, struct {
		UserAddress, CompanyAddress mail.Address
		Token                       string
	}{
		user.Address,
		company.Address,
		token,
	}); err != nil {
		return http.StatusInternalServerError, err
	}

	if err = appmail.Send(ctx, &appmail.Message{
		Sender:  "Lorenz Leutgeb <lorenz.leutgeb@cod.uno>",
		To:      []string{user.Address.String()},
		Subject: "We challenge you!",
		Body:    buf.String(),
	}); err != nil {
		return http.StatusInternalServerError, err
	}

	key, err = i.Save(ctx)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(i.Key(key))
	return http.StatusOK, nil
}
<|endoftext|>


<commit_before>package daslog

import (
	"bytes"
	"fmt"
	"io"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"
)

func chkerr(e error, t *testing.T, m string) {
	if e != nil {
		_, fn, line, _ := runtime.Caller(1)
		fnb := filepath.Base(fn)
		t.Fatalf("%s\ncall: %s:%d\nerr : %v\n", m, fnb, line, e)
	}
}

type prfxTmplTest struct {
	E string  // expected
	O Options // options
}

type outTest struct {
	N string // name
	E string // expected
}

// TODO : add more tests
func TestMain(t *testing.T) {
	var buf bytes.Buffer

	l, err := New(Options{
		Destination: &buf,
		LogLevel:    UrgencyLevelCritical,
	})
	chkerr(err, t, "fail to init Daslog")

	l.Notice("Notice")
	l.Noticef("Noticef")
	l.Info("Info")
	l.Infof("Infof")
	l.Error("Error")
	l.Errorf("Errorf")
	l.Critical("Critical")
	l.Criticalf("Criticalf")

	outTests := []outTest{
		outTest{N: "Notice", E: "Notice"},
		outTest{N: "Noticef", E: "Noticef"},
		outTest{N: "Info", E: "Info"},
		outTest{N: "Infof", E: "Infof"},
		outTest{N: "Error", E: "Error"},
		outTest{N: "Errorf", E: "Errorf"},
		outTest{N: "Critical", E: "Critical"},
		outTest{N: "Criticalf", E: "Criticalf"},
	}

	outTestsBuf := strings.Split(buf.String(), "\n")

	for i, tst := range outTestsBuf {
		if i == len(outTestsBuf)-1 {
			continue
		}

		if tst != outTests[i].E {
			t.Fatalf("'%s' != '%s' (out test: #%d, name: %s)\n", outTests[i].E, tst, i, outTests[i].N)
		}
	}

	prfxTests := []prfxTmplTest{
		prfxTmplTest{
			E: time.Now().Local().Format("2006-01-02"),
			O: Options{Prefix: "{{.F}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("2006"),
			O: Options{Prefix: "{{.Y}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("06"),
			O: Options{Prefix: "{{.y}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("01"),
			O: Options{Prefix: "{{.m}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("Jan"),
			O: Options{Prefix: "{{.b}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("January"),
			O: Options{Prefix: "{{.B}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("02"),
			O: Options{Prefix: "{{.d}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("Mon"),
			O: Options{Prefix: "{{.a}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("PM"),
			O: Options{Prefix: "{{.p}} ", LogLevel: UrgencyLevelCritical},
		},
	}

	for i, tst := range prfxTests {
		buf.Reset()
		tst.O.Destination = &buf

		l2, e := New(tst.O)
		chkerr(e, t, fmt.Sprintf("fail to init Daslog (date test #%d)", i))

		l2.Info("test")

		out := strings.Replace(buf.String(), "\n", "", -1)
		exp := tst.E + " test"

		if out != exp {
			t.Fatalf("'%s' != '%s' (date test #%d)\n", out, exp, i)
		}
	}

	buf.Reset()
	var buf2 bytes.Buffer

	l3, err := New(Options{
		Destinations: []io.Writer{&buf, &buf2},
		LogLevel:     UrgencyLevelCritical,
	})
	chkerr(err, t, "fail to init Daslog")

	l3.Info("test")

	if buf.String() != "test\n" || buf2.String() != "test\n" {
		t.Fatalf("destinations test fail\n")
	}
}
<commit_msg>update tests<commit_after>package daslog

import (
	"bytes"
	"fmt"
	"io"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"
)

func chkerr(e error, t *testing.T, m string) {
	if e != nil {
		_, fn, line, _ := runtime.Caller(1)
		fnb := filepath.Base(fn)
		t.Fatalf("%s\ncall: %s:%d\nerr : %v\n", m, fnb, line, e)
	}
}

type prfxTmplTest struct {
	E string  // expected
	O Options // options
}

type outTest struct {
	N string // name
	E string // expected
}

// TODO : add more tests
func TestMain(t *testing.T) {
	var buf bytes.Buffer

	l, err := New(Options{
		Destination: &buf,
		LogLevel:    UrgencyLevelNotice,
	})
	chkerr(err, t, "fail to init Daslog")

	l.Notice("Notice")
	l.Noticef("Noticef\n")
	l.Info("Info")
	l.Infof("Infof\n")
	l.Error("Error")
	l.Errorf("Errorf\n")
	l.Critical("Critical")
	l.Criticalf("Criticalf\n")

	outTests := []outTest{
		outTest{N: "Notice", E: "Notice\n"},
		outTest{N: "Noticef", E: "Noticef\n"},
		outTest{N: "Info", E: "Info\n"},
		outTest{N: "Infof", E: "Infof\n"},
		outTest{N: "Error", E: "Error\n"},
		outTest{N: "Errorf", E: "Errorf\n"},
		outTest{N: "Critical", E: "Critical\n"},
		outTest{N: "Criticalf", E: "Criticalf\n"},
	}

	outTestsBuf := strings.SplitAfterN(buf.String(), "\n", 8)

	for i, tst := range outTestsBuf {
		if tst != outTests[i].E {
			t.Fatalf("'%s' != '%s' (out test: #%d, name: %s)\n", outTests[i].E, tst, i, outTests[i].N)
		}
	}

	prfxTests := []prfxTmplTest{
		prfxTmplTest{
			E: time.Now().Local().Format("2006-01-02"),
			O: Options{Prefix: "{{.F}} ", LogLevel: UrgencyLevelNotice},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("2006"),
			O: Options{Prefix: "{{.Y}} ", LogLevel: UrgencyLevelNotice},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("06"),
			O: Options{Prefix: "{{.y}} ", LogLevel: UrgencyLevelNotice},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("01"),
			O: Options{Prefix: "{{.m}} ", LogLevel: UrgencyLevelNotice},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("Jan"),
			O: Options{Prefix: "{{.b}} ", LogLevel: UrgencyLevelNotice},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("January"),
			O: Options{Prefix: "{{.B}} ", LogLevel: UrgencyLevelNotice},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("02"),
			O: Options{Prefix: "{{.d}} ", LogLevel: UrgencyLevelNotice},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("Mon"),
			O: Options{Prefix: "{{.a}} ", LogLevel: UrgencyLevelNotice},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("PM"),
			O: Options{Prefix: "{{.p}} ", LogLevel: UrgencyLevelNotice},
		},
	}

	for i, tst := range prfxTests {
		buf.Reset()
		tst.O.Destination = &buf

		l2, e := New(tst.O)
		chkerr(e, t, fmt.Sprintf("fail to init Daslog (date test #%d)", i))

		l2.Info("test")

		out := strings.Replace(buf.String(), "\n", "", -1)
		exp := tst.E + " test"

		if out != exp {
			t.Fatalf("'%s' != '%s' (date test #%d)\n", out, exp, i)
		}
	}

	buf.Reset()
	var buf2 bytes.Buffer

	l3, err := New(Options{
		Destinations: []io.Writer{&buf, &buf2},
		LogLevel:     UrgencyLevelNotice,
	})
	chkerr(err, t, "fail to init Daslog")

	l3.Info("test")

	if buf.String() != "test\n" || buf2.String() != "test\n" {
		t.Fatalf("destinations test fail; buf == '%s', buf2 == '%s'\n",
			buf.String(), buf2.String(),
		)
	}

	// UL tests
	buf.Reset()
	l4, err := New(Options{
		Destinations: []io.Writer{&buf},
		LogLevel:     UrgencyLevelNone,
	})
	chkerr(err, t, "fail to init Daslog")

	l4.Critical("test")
	if buf.String() != "" {
		t.Fatalf("non empty buf: %s \n", buf.String())
	}

	buf.Reset()
	l4, err = New(Options{
		Destinations: []io.Writer{&buf},
		LogLevel:     UrgencyLevelError,
	})
	chkerr(err, t, "fail to init Daslog")

	l4.Notice("notice")
	l4.Info("info")
	l4.Error("error")
	l4.Critical("critical")

	outTestsBuf = strings.SplitAfterN(buf.String(), "\n", 2)

	if len(outTestsBuf) != 2 {
		t.Fatalf("UL test error #0: %d != 2 \n", len(outTestsBuf))
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Tom Thorogood. All rights reserved.
// Use of this source code is governed by a Modified
// BSD License that can be found in the LICENSE file.

// Package id3v2 implements support for reading ID3v2 tags.
package id3v2

//go:generate go run generate_ids.go

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"sync"
	"unicode/utf16"
)

// This is an implementation of v2.4.0 of the ID3v2 tagging format,
// defined in: http://id3.org/id3v2.4.0-structure, and v2.3.0 of
// the ID3v2 tagging format, defined in: http://id3.org/id3v2.3.0.

// Version is the version of the ID3v2 tag block.
type Version byte

const (
	// Version24 is v2.4.x of the ID3v2 specification.
	Version24 Version = 0x04
	// Version23 is v2.3.x of the ID3v2 specification.
	Version23 Version = 0x03
)

const (
	tagFlagUnsynchronisation = 1 << (7 - iota)
	tagFlagExtendedHeader
	tagFlagExperimental
	tagFlagFooter

	knownTagFlags = tagFlagUnsynchronisation | tagFlagExtendedHeader |
		tagFlagExperimental | tagFlagFooter
)

// FrameFlags are the frame-level ID3v2 flags.
type FrameFlags uint16

// These are the frame-level flags from v2.4.0 of the specification.
const (
	_ FrameFlags = 1 << (15 - iota)
	FrameFlagV24TagAlterPreservation
	FrameFlagV24FileAlterPreservation
	FrameFlagV24ReadOnly
	_
	_
	_
	_
	_
	FrameFlagV24GroupingIdentity
	_
	_
	FrameFlagV24Compression
	FrameFlagV24Encryption
	FrameFlagV24Unsynchronisation
	FrameFlagV24DataLengthIndicator
)

// These are the frame-level flags from v2.3.0 of the specification.
const (
	FrameFlagV23TagAlterPreservation FrameFlags = 1 << (15 - iota)
	FrameFlagV23FileAlterPreservation
	FrameFlagV23ReadOnly
	_
	_
	_
	_
	_
	FrameFlagV23Compression
	FrameFlagV23Encryption
	FrameFlagV23GroupingIdentity
)

const encodingFrameFlags FrameFlags = 0x00ff

const (
	textEncodingISO88591 = 0x00
	textEncodingUTF16    = 0x01
	textEncodingUTF16BE  = 0x02
	textEncodingUTF8     = 0x03
)

// FrameID is a four-byte frame identifier.
type FrameID uint32

const syncsafeInvalid = ^uint32(0)

func syncsafe(data []byte) uint32 {
	_ = data[3]

	if data[0]&0x80 != 0 || data[1]&0x80 != 0 ||
		data[2]&0x80 != 0 || data[3]&0x80 != 0 {
		return syncsafeInvalid
	}

	return uint32(data[0])<<21 | uint32(data[1])<<14 |
		uint32(data[2])<<7 | uint32(data[3])
}

func id3Split(data []byte, atEOF bool) (advance int, token []byte, err error) {
	i := bytes.Index(data, []byte("ID3"))
	if i == -1 {
		if len(data) < 2 {
			return 0, nil, nil
		}

		return len(data) - 2, nil, nil
	}

	data = data[i:]
	if len(data) < 10 {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	size := syncsafe(data[6:])

	if data[3] == 0xff || data[4] == 0xff || size == syncsafeInvalid {
		// Skipping when we find the string "ID3" in the file but
		// the remaining header is invalid is consistent with the
		// detection logic in §3.1. This also reduces the
		// likelihood of errors being caused by the byte sequence
		// "ID3" (49 44 33) occurring in the audio, but does not
		// eliminate the possibility of errors in this case.
		//
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   An ID3v2 tag can be detected with the following pattern:
		//     $49 44 33 yy yy xx zz zz zz zz
		//   Where yy is less than $FF, xx is the 'flags' byte and zz
		//   is less than $80.
		return i + 3, nil, nil
	}

	if Version(data[3]) > Version24 {
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   If software with ID3v2.4.0 and below support should
		//   encounter version five or higher it should simply
		//   ignore the whole tag.
		return i + 3, nil, nil
	}

	if Version(data[3]) < Version23 {
		// This package only supports v2.3.0 and v2.4.0, skip
		// versions bellow v2.3.0.
		return i + 3, nil, nil
	}

	if data[5]&^knownTagFlags != 0 {
		// Skip tag blocks that contain unknown flags.
		//
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   If one of these undefined flags are set, the tag might
		//   not be readable for a parser that does not know the
		//   flags function.
		return i + 3, nil, nil
	}

	if data[5]&tagFlagFooter == tagFlagFooter {
		size += 10
	}

	if len(data) < 10+int(size) {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	return i + 10 + int(size), data[:10+size], nil
}

const invalidFrameID = ^FrameID(0)

func validIDByte(b byte) bool {
	return (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
}

func frameID(data []byte) FrameID {
	_ = data[3]

	if validIDByte(data[0]) && validIDByte(data[1]) && validIDByte(data[2]) &&
		// Although it violates the specification, some software
		// incorrectly encodes v2.2.0 three character tags as
		// four character v2.3.0 tags with a trailing zero byte
		// when upgrading the tagging format version.
		(validIDByte(data[3]) || data[3] == 0) {
		return FrameID(binary.BigEndian.Uint32(data))
	}

	for _, v := range data {
		if v != 0 {
			return invalidFrameID
		}
	}

	// This is probably the begging of padding.
	return 0
}

var bufPool = &sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 4<<10)
		return &buf
	},
}

// Scan reads all valid ID3v2 tags from the reader and
// returns all the frames in order. It returns an error
// if the tags are invalid.
func Scan(r io.Reader) (Frames, error) {
	buf := bufPool.Get()
	defer bufPool.Put(buf)

	s := bufio.NewScanner(r)
	s.Buffer(*buf.(*[]byte), 1<<28)
	s.Split(id3Split)

	var frames Frames

	for s.Scan() {
		data := s.Bytes()

		header := data[:10]
		data = data[10:]

		if string(header[:3]) != "ID3" {
			panic("id3: bufio.Scanner failed")
		}

		version := Version(header[3])
		switch version {
		case Version24, Version23:
		default:
			panic("id3: bufio.Scanner failed")
		}

		flags := header[5]

		if flags&tagFlagFooter == tagFlagFooter {
			footer := data[len(data)-10:]
			data = data[:len(data)-10]

			if string(footer[:3]) != "3DI" ||
				!bytes.Equal(header[3:], footer[3:]) {
				return nil, errors.New("id3: invalid footer")
			}
		}

		if flags&tagFlagExtendedHeader == tagFlagExtendedHeader {
			size := syncsafe(data)
			if size == syncsafeInvalid || len(data) < int(size) {
				return nil, errors.New("id3: invalid extended header")
			}

			extendedHeader := data[:size]
			data = data[size:]

			_ = extendedHeader
		}

	frames:
		for len(data) > 10 {
			_ = data[9]

			frame := &Frame{
				ID:      frameID(data),
				Version: version,
				Flags:   FrameFlags(binary.BigEndian.Uint16(data[8:])),
			}

			switch frame.ID {
			case 0:
				// We've probably hit padding, the padding
				// validity check below will handle this.
				break frames
			case invalidFrameID:
				return nil, errors.New("id3: invalid frame id")
			}

			var size uint32
			switch version {
			case Version24:
				size = syncsafe(data[4:])
				if size == syncsafeInvalid {
					return nil, errors.New("id3: invalid frame size")
				}
			case Version23:
				size = binary.BigEndian.Uint32(data[4:])
			default:
				panic("unhandled version")
			}

			if len(data) < 10+int(size) {
				return nil, errors.New("id3: frame size exceeds length of tag data")
			}

			if flags&tagFlagUnsynchronisation == tagFlagUnsynchronisation ||
				(version == Version24 && frame.Flags&FrameFlagV24Unsynchronisation != 0) {
				frame.Data = make([]byte, 0, size)

				for i := uint32(0); i < size; i++ {
					v := data[10+i]
					frame.Data = append(frame.Data, v)

					if v == 0xff && i+1 < size && data[10+i+1] == 0x00 {
						i++
					}
				}

				if version == Version24 {
					// Clear the frame level unsynchronisation flag
					frame.Flags &^= FrameFlagV24Unsynchronisation
				}
			} else {
				frame.Data = append([]byte(nil), data[10:10+size]...)
			}

			frames = append(frames, frame)
			data = data[10+size:]
		}

		if flags&tagFlagFooter == tagFlagFooter && len(data) != 0 {
			return nil, errors.New("id3: padding with footer")
		}

		for _, v := range data {
			if v != 0 {
				return nil, errors.New("id3: invalid padding")
			}
		}
	}

	if s.Err() != nil {
		return nil, s.Err()
	}

	return frames, nil
}

// Frames is a slice of ID3v2 frames.
type Frames []*Frame

// Lookup returns the last frame associated with a
// given frame id, or nil.
func (f Frames) Lookup(id FrameID) *Frame {
	for i := len(f) - 1; i >= 0; i-- {
		if f[i].ID == id {
			return f[i]
		}
	}

	return nil
}

// Frame is a single ID3v2 frame.
type Frame struct {
	ID      FrameID
	Version Version
	Flags   FrameFlags
	Data    []byte
}

func (f *Frame) String() string {
	data, terminus := f.Data, ""
	if len(data) > 128 {
		data, terminus = data[:128], "..."
	}

	var version string
	switch f.Version {
	case Version24:
		version = "v2.4"
	case Version23:
		version = "v2.3"
	default:
		version = "?"
	}

	return fmt.Sprintf("&ID3Frame{ID: %s, Version: %s, Flags: 0x%04x, Data: %d:%q%s}",
		f.ID.String(), version, f.Flags, len(f.Data), data, terminus)
}

// Text interprets the frame data as a text string,
// according to §4 of id3v2.4.0-structure.txt.
func (f *Frame) Text() (string, error) {
	if len(f.Data) < 2 {
		return "", errors.New("id3: frame data is invalid")
	}

	if f.Flags&encodingFrameFlags != 0 {
		return "", errors.New("id3: encoding frame flags are not supported")
	}

	data := f.Data[1:]
	var ord binary.ByteOrder = binary.BigEndian

	switch f.Data[0] {
	case textEncodingISO88591:
		for _, v := range data {
			if v&0x80 == 0 {
				continue
			}

			runes := make([]rune, len(data))
			for i, v := range data {
				runes[i] = rune(v)
			}

			return string(runes), nil
		}

		fallthrough
	case textEncodingUTF8:
		if data[len(data)-1] == 0x00 {
			// The specification requires that the string be
			// terminated with 0x00, but not all implementations
			// do this.
			data = data[:len(data)-1]
		}

		return string(data), nil
	case textEncodingUTF16:
		if len(data) < 2 {
			return "", errors.New("id3: missing UTF-16 BOM")
		}

		if data[0] == 0xff && data[1] == 0xfe {
			ord = binary.LittleEndian
		} else if data[0] == 0xfe && data[1] == 0xff {
			ord = binary.BigEndian
		} else {
			return "", errors.New("id3: invalid UTF-16 BOM")
		}

		data = data[2:]
		fallthrough
	case textEncodingUTF16BE:
		if len(data)%2 != 0 {
			return "", errors.New("id3: UTF-16 data is not even number of bytes")
		}

		u16s := make([]uint16, len(data)/2)
		for i := range u16s {
			u16s[i] = ord.Uint16(data[i*2:])
		}

		if u16s[len(u16s)-1] == 0x0000 {
			// The specification requires that the string be
			// terminated with 0x00 0x00, but not all
			// implementations do this.
			u16s = u16s[:len(u16s)-1]
		}

		return string(utf16.Decode(u16s)), nil
	default:
		return "", errors.New("id3: frame uses unsupported encoding")
	}
}
<commit_msg>Move version before data in (*Frame).String<commit_after>// Copyright 2017 Tom Thorogood. All rights reserved.
// Use of this source code is governed by a Modified
// BSD License that can be found in the LICENSE file.

// Package id3v2 implements support for reading ID3v2 tags.
package id3v2

//go:generate go run generate_ids.go

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"sync"
	"unicode/utf16"
)

// This is an implementation of v2.4.0 of the ID3v2 tagging format,
// defined in: http://id3.org/id3v2.4.0-structure, and v2.3.0 of
// the ID3v2 tagging format, defined in: http://id3.org/id3v2.3.0.

// Version is the version of the ID3v2 tag block.
type Version byte

const (
	// Version24 is v2.4.x of the ID3v2 specification.
	Version24 Version = 0x04
	// Version23 is v2.3.x of the ID3v2 specification.
	Version23 Version = 0x03
)

const (
	tagFlagUnsynchronisation = 1 << (7 - iota)
	tagFlagExtendedHeader
	tagFlagExperimental
	tagFlagFooter

	knownTagFlags = tagFlagUnsynchronisation | tagFlagExtendedHeader |
		tagFlagExperimental | tagFlagFooter
)

// FrameFlags are the frame-level ID3v2 flags.
type FrameFlags uint16

// These are the frame-level flags from v2.4.0 of the specification.
const (
	_ FrameFlags = 1 << (15 - iota)
	FrameFlagV24TagAlterPreservation
	FrameFlagV24FileAlterPreservation
	FrameFlagV24ReadOnly
	_
	_
	_
	_
	_
	FrameFlagV24GroupingIdentity
	_
	_
	FrameFlagV24Compression
	FrameFlagV24Encryption
	FrameFlagV24Unsynchronisation
	FrameFlagV24DataLengthIndicator
)

// These are the frame-level flags from v2.3.0 of the specification.
const (
	FrameFlagV23TagAlterPreservation FrameFlags = 1 << (15 - iota)
	FrameFlagV23FileAlterPreservation
	FrameFlagV23ReadOnly
	_
	_
	_
	_
	_
	FrameFlagV23Compression
	FrameFlagV23Encryption
	FrameFlagV23GroupingIdentity
)

const encodingFrameFlags FrameFlags = 0x00ff

const (
	textEncodingISO88591 = 0x00
	textEncodingUTF16    = 0x01
	textEncodingUTF16BE  = 0x02
	textEncodingUTF8     = 0x03
)

// FrameID is a four-byte frame identifier.
type FrameID uint32

const syncsafeInvalid = ^uint32(0)

func syncsafe(data []byte) uint32 {
	_ = data[3]

	if data[0]&0x80 != 0 || data[1]&0x80 != 0 ||
		data[2]&0x80 != 0 || data[3]&0x80 != 0 {
		return syncsafeInvalid
	}

	return uint32(data[0])<<21 | uint32(data[1])<<14 |
		uint32(data[2])<<7 | uint32(data[3])
}

func id3Split(data []byte, atEOF bool) (advance int, token []byte, err error) {
	i := bytes.Index(data, []byte("ID3"))
	if i == -1 {
		if len(data) < 2 {
			return 0, nil, nil
		}

		return len(data) - 2, nil, nil
	}

	data = data[i:]
	if len(data) < 10 {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	size := syncsafe(data[6:])

	if data[3] == 0xff || data[4] == 0xff || size == syncsafeInvalid {
		// Skipping when we find the string "ID3" in the file but
		// the remaining header is invalid is consistent with the
		// detection logic in §3.1. This also reduces the
		// likelihood of errors being caused by the byte sequence
		// "ID3" (49 44 33) occurring in the audio, but does not
		// eliminate the possibility of errors in this case.
		//
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   An ID3v2 tag can be detected with the following pattern:
		//     $49 44 33 yy yy xx zz zz zz zz
		//   Where yy is less than $FF, xx is the 'flags' byte and zz
		//   is less than $80.
		return i + 3, nil, nil
	}

	if Version(data[3]) > Version24 {
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   If software with ID3v2.4.0 and below support should
		//   encounter version five or higher it should simply
		//   ignore the whole tag.
		return i + 3, nil, nil
	}

	if Version(data[3]) < Version23 {
		// This package only supports v2.3.0 and v2.4.0, skip
		// versions bellow v2.3.0.
		return i + 3, nil, nil
	}

	if data[5]&^knownTagFlags != 0 {
		// Skip tag blocks that contain unknown flags.
		//
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   If one of these undefined flags are set, the tag might
		//   not be readable for a parser that does not know the
		//   flags function.
		return i + 3, nil, nil
	}

	if data[5]&tagFlagFooter == tagFlagFooter {
		size += 10
	}

	if len(data) < 10+int(size) {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	return i + 10 + int(size), data[:10+size], nil
}

const invalidFrameID = ^FrameID(0)

func validIDByte(b byte) bool {
	return (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
}

func frameID(data []byte) FrameID {
	_ = data[3]

	if validIDByte(data[0]) && validIDByte(data[1]) && validIDByte(data[2]) &&
		// Although it violates the specification, some software
		// incorrectly encodes v2.2.0 three character tags as
		// four character v2.3.0 tags with a trailing zero byte
		// when upgrading the tagging format version.
		(validIDByte(data[3]) || data[3] == 0) {
		return FrameID(binary.BigEndian.Uint32(data))
	}

	for _, v := range data {
		if v != 0 {
			return invalidFrameID
		}
	}

	// This is probably the begging of padding.
	return 0
}

var bufPool = &sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 4<<10)
		return &buf
	},
}

// Scan reads all valid ID3v2 tags from the reader and
// returns all the frames in order. It returns an error
// if the tags are invalid.
func Scan(r io.Reader) (Frames, error) {
	buf := bufPool.Get()
	defer bufPool.Put(buf)

	s := bufio.NewScanner(r)
	s.Buffer(*buf.(*[]byte), 1<<28)
	s.Split(id3Split)

	var frames Frames

	for s.Scan() {
		data := s.Bytes()

		header := data[:10]
		data = data[10:]

		if string(header[:3]) != "ID3" {
			panic("id3: bufio.Scanner failed")
		}

		version := Version(header[3])
		switch version {
		case Version24, Version23:
		default:
			panic("id3: bufio.Scanner failed")
		}

		flags := header[5]

		if flags&tagFlagFooter == tagFlagFooter {
			footer := data[len(data)-10:]
			data = data[:len(data)-10]

			if string(footer[:3]) != "3DI" ||
				!bytes.Equal(header[3:], footer[3:]) {
				return nil, errors.New("id3: invalid footer")
			}
		}

		if flags&tagFlagExtendedHeader == tagFlagExtendedHeader {
			size := syncsafe(data)
			if size == syncsafeInvalid || len(data) < int(size) {
				return nil, errors.New("id3: invalid extended header")
			}

			extendedHeader := data[:size]
			data = data[size:]

			_ = extendedHeader
		}

	frames:
		for len(data) > 10 {
			_ = data[9]

			frame := &Frame{
				ID:      frameID(data),
				Version: version,
				Flags:   FrameFlags(binary.BigEndian.Uint16(data[8:])),
			}

			switch frame.ID {
			case 0:
				// We've probably hit padding, the padding
				// validity check below will handle this.
				break frames
			case invalidFrameID:
				return nil, errors.New("id3: invalid frame id")
			}

			var size uint32
			switch version {
			case Version24:
				size = syncsafe(data[4:])
				if size == syncsafeInvalid {
					return nil, errors.New("id3: invalid frame size")
				}
			case Version23:
				size = binary.BigEndian.Uint32(data[4:])
			default:
				panic("unhandled version")
			}

			if len(data) < 10+int(size) {
				return nil, errors.New("id3: frame size exceeds length of tag data")
			}

			if flags&tagFlagUnsynchronisation == tagFlagUnsynchronisation ||
				(version == Version24 && frame.Flags&FrameFlagV24Unsynchronisation != 0) {
				frame.Data = make([]byte, 0, size)

				for i := uint32(0); i < size; i++ {
					v := data[10+i]
					frame.Data = append(frame.Data, v)

					if v == 0xff && i+1 < size && data[10+i+1] == 0x00 {
						i++
					}
				}

				if version == Version24 {
					// Clear the frame level unsynchronisation flag
					frame.Flags &^= FrameFlagV24Unsynchronisation
				}
			} else {
				frame.Data = append([]byte(nil), data[10:10+size]...)
			}

			frames = append(frames, frame)
			data = data[10+size:]
		}

		if flags&tagFlagFooter == tagFlagFooter && len(data) != 0 {
			return nil, errors.New("id3: padding with footer")
		}

		for _, v := range data {
			if v != 0 {
				return nil, errors.New("id3: invalid padding")
			}
		}
	}

	if s.Err() != nil {
		return nil, s.Err()
	}

	return frames, nil
}

// Frames is a slice of ID3v2 frames.
type Frames []*Frame

// Lookup returns the last frame associated with a
// given frame id, or nil.
func (f Frames) Lookup(id FrameID) *Frame {
	for i := len(f) - 1; i >= 0; i-- {
		if f[i].ID == id {
			return f[i]
		}
	}

	return nil
}

// Frame is a single ID3v2 frame.
type Frame struct {
	ID      FrameID
	Version Version
	Flags   FrameFlags
	Data    []byte
}

func (f *Frame) String() string {
	version := "?"
	switch f.Version {
	case Version24:
		version = "v2.4"
	case Version23:
		version = "v2.3"
	}

	data, terminus := f.Data, ""
	if len(data) > 128 {
		data, terminus = data[:128], "..."
	}

	return fmt.Sprintf("&ID3Frame{ID: %s, Version: %s, Flags: 0x%04x, Data: %d:%q%s}",
		f.ID.String(), version, f.Flags, len(f.Data), data, terminus)
}

// Text interprets the frame data as a text string,
// according to §4 of id3v2.4.0-structure.txt.
func (f *Frame) Text() (string, error) {
	if len(f.Data) < 2 {
		return "", errors.New("id3: frame data is invalid")
	}

	if f.Flags&encodingFrameFlags != 0 {
		return "", errors.New("id3: encoding frame flags are not supported")
	}

	data := f.Data[1:]
	var ord binary.ByteOrder = binary.BigEndian

	switch f.Data[0] {
	case textEncodingISO88591:
		for _, v := range data {
			if v&0x80 == 0 {
				continue
			}

			runes := make([]rune, len(data))
			for i, v := range data {
				runes[i] = rune(v)
			}

			return string(runes), nil
		}

		fallthrough
	case textEncodingUTF8:
		if data[len(data)-1] == 0x00 {
			// The specification requires that the string be
			// terminated with 0x00, but not all implementations
			// do this.
			data = data[:len(data)-1]
		}

		return string(data), nil
	case textEncodingUTF16:
		if len(data) < 2 {
			return "", errors.New("id3: missing UTF-16 BOM")
		}

		if data[0] == 0xff && data[1] == 0xfe {
			ord = binary.LittleEndian
		} else if data[0] == 0xfe && data[1] == 0xff {
			ord = binary.BigEndian
		} else {
			return "", errors.New("id3: invalid UTF-16 BOM")
		}

		data = data[2:]
		fallthrough
	case textEncodingUTF16BE:
		if len(data)%2 != 0 {
			return "", errors.New("id3: UTF-16 data is not even number of bytes")
		}

		u16s := make([]uint16, len(data)/2)
		for i := range u16s {
			u16s[i] = ord.Uint16(data[i*2:])
		}

		if u16s[len(u16s)-1] == 0x0000 {
			// The specification requires that the string be
			// terminated with 0x00 0x00, but not all
			// implementations do this.
			u16s = u16s[:len(u16s)-1]
		}

		return string(utf16.Decode(u16s)), nil
	default:
		return "", errors.New("id3: frame uses unsupported encoding")
	}
}
<|endoftext|>


<commit_before>package stats

import (
	"fmt"
	"strconv"
	"time"

	"github.com/gempir/go-twitch-irc/v2"
	"github.com/gempir/spamchamp/bot/api"
	"github.com/gempir/spamchamp/bot/store"
	"github.com/paulbellamy/ratecounter"
	log "github.com/sirupsen/logrus"
)

var (
	stats          = map[string]stat{}
	joinedChannels = 0
)

type Broadcaster struct {
	messageQueue   chan twitch.PrivateMessage
	broadcastQueue chan api.BroadcastMessage
	store          *store.Store
}

func NewBroadcaster(messageQueue chan twitch.PrivateMessage, broadcastQueue chan api.BroadcastMessage, store *store.Store) Broadcaster {
	return Broadcaster{
		messageQueue:   messageQueue,
		broadcastQueue: broadcastQueue,
		store:          store,
	}
}

func (b *Broadcaster) Start() {
	log.Info("[stats] starting stats collector")

	go b.startTicker()

	for message := range b.messageQueue {
		if message.ID == "28b511cc-43b3-44b7-a605-230aadbb2f9b" {
			log.Info(message.Message)
			var err error
			joinedChannels, err = strconv.Atoi(message.Message)
			if err != nil {
				log.Errorf("Failed to parse relaybroker message: %s", err.Error())
			}
			continue
		}
		if _, ok := stats[message.RoomID]; !ok {
			stats[message.RoomID] = newStat(message.Channel)
		}

		stats[message.RoomID].messages.Incr(1)
	}
}

func (b *Broadcaster) startTicker() {
	ticker := time.NewTicker(1 * time.Second)

	for range ticker.C {
		message := api.BroadcastMessage{
			Records: []api.Record{},
		}

		msgps := api.Record{
			Title:  "Current messages/s",
			Scores: []api.Score{},
		}

		for channelID, stat := range stats {
			rate := stat.messages.Rate() / 3
			if rate == 0 {
				continue
			}

			score := b.store.GetMsgps(channelID)
			if float64(rate) > score {
				b.store.UpdateMsgps(channelID, rate)
			}

			msgps.Scores = append(msgps.Scores, api.Score{ID: channelID, Score: float64(rate)})
		}

		scores := []api.Score{}
		for _, z := range b.store.GetMsgpsScores() {
			scores = append(scores, api.Score{ID: fmt.Sprintf("%v", z.Member), Score: z.Score})
		}

		maxLen := 10
		if len(msgps.Scores) < 10 {
			maxLen = len(msgps.Scores)
		}
		msgps.Scores = msgps.GetScoresSorted()[0:maxLen]
		message.Records = append(message.Records, msgps)

		message.Records = append(message.Records, api.Record{
			Title:  "Record messages/s",
			Scores: scores,
		})

		message.JoinedChannels = joinedChannels

		b.broadcastQueue <- message
	}
}

type stat struct {
	channelName  string
	messages     *ratecounter.RateCounter
	messageCount int
}

func newStat(channelName string) stat {
	return stat{
		channelName: channelName,
		messages:    ratecounter.NewRateCounter(time.Second * 3),
	}
}
<commit_msg>remove log message<commit_after>package stats

import (
	"fmt"
	"strconv"
	"time"

	"github.com/gempir/go-twitch-irc/v2"
	"github.com/gempir/spamchamp/bot/api"
	"github.com/gempir/spamchamp/bot/store"
	"github.com/paulbellamy/ratecounter"
	log "github.com/sirupsen/logrus"
)

var (
	stats          = map[string]stat{}
	joinedChannels = 0
)

type Broadcaster struct {
	messageQueue   chan twitch.PrivateMessage
	broadcastQueue chan api.BroadcastMessage
	store          *store.Store
}

func NewBroadcaster(messageQueue chan twitch.PrivateMessage, broadcastQueue chan api.BroadcastMessage, store *store.Store) Broadcaster {
	return Broadcaster{
		messageQueue:   messageQueue,
		broadcastQueue: broadcastQueue,
		store:          store,
	}
}

func (b *Broadcaster) Start() {
	log.Info("[stats] starting stats collector")

	go b.startTicker()

	for message := range b.messageQueue {
		if message.ID == "28b511cc-43b3-44b7-a605-230aadbb2f9b" {
			var err error
			joinedChannels, err = strconv.Atoi(message.Message)
			if err != nil {
				log.Errorf("Failed to parse relaybroker message: %s", err.Error())
			}
			continue
		}
		if _, ok := stats[message.RoomID]; !ok {
			stats[message.RoomID] = newStat(message.Channel)
		}

		stats[message.RoomID].messages.Incr(1)
	}
}

func (b *Broadcaster) startTicker() {
	ticker := time.NewTicker(1 * time.Second)

	for range ticker.C {
		message := api.BroadcastMessage{
			Records: []api.Record{},
		}

		msgps := api.Record{
			Title:  "Current messages/s",
			Scores: []api.Score{},
		}

		for channelID, stat := range stats {
			rate := stat.messages.Rate() / 3
			if rate == 0 {
				continue
			}

			score := b.store.GetMsgps(channelID)
			if float64(rate) > score {
				b.store.UpdateMsgps(channelID, rate)
			}

			msgps.Scores = append(msgps.Scores, api.Score{ID: channelID, Score: float64(rate)})
		}

		scores := []api.Score{}
		for _, z := range b.store.GetMsgpsScores() {
			scores = append(scores, api.Score{ID: fmt.Sprintf("%v", z.Member), Score: z.Score})
		}

		maxLen := 10
		if len(msgps.Scores) < 10 {
			maxLen = len(msgps.Scores)
		}
		msgps.Scores = msgps.GetScoresSorted()[0:maxLen]
		message.Records = append(message.Records, msgps)

		message.Records = append(message.Records, api.Record{
			Title:  "Record messages/s",
			Scores: scores,
		})

		message.JoinedChannels = joinedChannels

		b.broadcastQueue <- message
	}
}

type stat struct {
	channelName  string
	messages     *ratecounter.RateCounter
	messageCount int
}

func newStat(channelName string) stat {
	return stat{
		channelName: channelName,
		messages:    ratecounter.NewRateCounter(time.Second * 3),
	}
}
<|endoftext|>


<commit_before>package bxmpp

import (
	"crypto/tls"
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/helper"
	"github.com/jpillora/backoff"
	"github.com/matterbridge/go-xmpp"
	"strings"
	"time"
)

type Bxmpp struct {
	xc      *xmpp.Client
	xmppMap map[string]string
	*bridge.Config
}

func New(cfg *bridge.Config) bridge.Bridger {
	b := &Bxmpp{Config: cfg}
	b.xmppMap = make(map[string]string)
	return b
}

func (b *Bxmpp) Connect() error {
	var err error
	b.Log.Infof("Connecting %s", b.GetString("Server"))
	b.xc, err = b.createXMPP()
	if err != nil {
		b.Log.Debugf("%#v", err)
		return err
	}
	b.Log.Info("Connection succeeded")
	go func() {
		initial := true
		bf := &backoff.Backoff{
			Min:    time.Second,
			Max:    5 * time.Minute,
			Jitter: true,
		}
		for {
			if initial {
				b.handleXMPP()
				initial = false
			}
			d := bf.Duration()
			b.Log.Infof("Disconnected. Reconnecting in %s", d)
			time.Sleep(d)
			b.xc, err = b.createXMPP()
			if err == nil {
				b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
				b.handleXMPP()
				bf.Reset()
			}
		}
	}()
	return nil
}

func (b *Bxmpp) Disconnect() error {
	return nil
}

func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error {
	b.xc.JoinMUCNoHistory(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"))
	return nil
}

func (b *Bxmpp) Send(msg config.Message) (string, error) {
	// ignore delete messages
	if msg.Event == config.EVENT_MSG_DELETE {
		return "", nil
	}
	b.Log.Debugf("=> Receiving %#v", msg)

	// Upload a file (in xmpp case send the upload URL because xmpp has no native upload support)
	if msg.Extra != nil {
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
			b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: rmsg.Channel + "@" + b.GetString("Muc"), Text: rmsg.Username + rmsg.Text})
		}
		if len(msg.Extra["file"]) > 0 {
			return b.handleUploadFile(&msg)
		}
	}

	// Post normal message
	_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})
	if err != nil {
		return "", err
	}
	return "", nil
}

func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
	tc := new(tls.Config)
	tc.InsecureSkipVerify = b.GetBool("SkipTLSVerify")
	tc.ServerName = strings.Split(b.GetString("Server"), ":")[0]
	options := xmpp.Options{
		Host:                         b.GetString("Server"),
		User:                         b.GetString("Jid"),
		Password:                     b.GetString("Password"),
		NoTLS:                        true,
		StartTLS:                     true,
		TLSConfig:                    tc,
		Debug:                        b.GetBool("debug"),
		Logger:                       b.Log.Writer(),
		Session:                      true,
		Status:                       "",
		StatusMessage:                "",
		Resource:                     "",
		InsecureAllowUnencryptedAuth: false,
	}
	var err error
	b.xc, err = options.NewClient()
	return b.xc, err
}

func (b *Bxmpp) xmppKeepAlive() chan bool {
	done := make(chan bool)
	go func() {
		ticker := time.NewTicker(90 * time.Second)
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				b.Log.Debugf("PING")
				err := b.xc.PingC2S("", "")
				if err != nil {
					b.Log.Debugf("PING failed %#v", err)
				}
			case <-done:
				return
			}
		}
	}()
	return done
}

func (b *Bxmpp) handleXMPP() error {
	var ok bool
	done := b.xmppKeepAlive()
	defer close(done)
	for {
		m, err := b.xc.Recv()
		if err != nil {
			return err
		}
		switch v := m.(type) {
		case xmpp.Chat:
			if v.Type == "groupchat" {
				b.Log.Debugf("== Receiving %#v", v)
				// skip invalid messages
				if b.skipMessage(v) {
					continue
				}
				rmsg := config.Message{Username: b.parseNick(v.Remote), Text: v.Text, Channel: b.parseChannel(v.Remote), Account: b.Account, UserID: v.Remote}

				// check if we have an action event
				rmsg.Text, ok = b.replaceAction(rmsg.Text)
				if ok {
					rmsg.Event = config.EVENT_USER_ACTION
				}
				b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
				b.Log.Debugf("<= Message is %#v", rmsg)
				b.Remote <- rmsg
			}
		case xmpp.Presence:
			// do nothing
		}
	}
}

func (b *Bxmpp) replaceAction(text string) (string, bool) {
	if strings.HasPrefix(text, "/me ") {
		return strings.Replace(text, "/me ", "", -1), true
	}
	return text, false
}

// handleUploadFile handles native upload of files
func (b *Bxmpp) handleUploadFile(msg *config.Message) (string, error) {
	for _, f := range msg.Extra["file"] {
		fi := f.(config.FileInfo)
		if fi.Comment != "" {
			msg.Text += fi.Comment + ": "
		}
		if fi.URL != "" {
			msg.Text += fi.URL
		}
		_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})
		if err != nil {
			return "", err
		}
	}
	return "", nil
}

func (b *Bxmpp) parseNick(remote string) string {
	s := strings.Split(remote, "@")
	if len(s) > 0 {
		s = strings.Split(s[1], "/")
		if len(s) == 2 {
			return s[1] // nick
		}
	}
	return ""
}

func (b *Bxmpp) parseChannel(remote string) string {
	s := strings.Split(remote, "@")
	if len(s) >= 2 {
		return s[0] // channel
	}
	return ""
}

// skipMessage skips messages that need to be skipped
func (b *Bxmpp) skipMessage(message xmpp.Chat) bool {
	// skip messages from ourselves
	if b.parseNick(message.Remote) == b.GetString("Nick") {
		return true
	}

	// skip empty messages
	if message.Text == "" {
		return true
	}

	// skip subject messages
	if strings.Contains(message.Text, "</subject>") {
		return true
	}

	// skip delayed messages
	t := time.Time{}
	return message.Stamp == t
}
<commit_msg>Fix incorrect skipmessage (xmpp)<commit_after>package bxmpp

import (
	"crypto/tls"
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/helper"
	"github.com/jpillora/backoff"
	"github.com/matterbridge/go-xmpp"
	"strings"
	"time"
)

type Bxmpp struct {
	xc      *xmpp.Client
	xmppMap map[string]string
	*bridge.Config
}

func New(cfg *bridge.Config) bridge.Bridger {
	b := &Bxmpp{Config: cfg}
	b.xmppMap = make(map[string]string)
	return b
}

func (b *Bxmpp) Connect() error {
	var err error
	b.Log.Infof("Connecting %s", b.GetString("Server"))
	b.xc, err = b.createXMPP()
	if err != nil {
		b.Log.Debugf("%#v", err)
		return err
	}
	b.Log.Info("Connection succeeded")
	go func() {
		initial := true
		bf := &backoff.Backoff{
			Min:    time.Second,
			Max:    5 * time.Minute,
			Jitter: true,
		}
		for {
			if initial {
				b.handleXMPP()
				initial = false
			}
			d := bf.Duration()
			b.Log.Infof("Disconnected. Reconnecting in %s", d)
			time.Sleep(d)
			b.xc, err = b.createXMPP()
			if err == nil {
				b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
				b.handleXMPP()
				bf.Reset()
			}
		}
	}()
	return nil
}

func (b *Bxmpp) Disconnect() error {
	return nil
}

func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error {
	b.xc.JoinMUCNoHistory(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"))
	return nil
}

func (b *Bxmpp) Send(msg config.Message) (string, error) {
	// ignore delete messages
	if msg.Event == config.EVENT_MSG_DELETE {
		return "", nil
	}
	b.Log.Debugf("=> Receiving %#v", msg)

	// Upload a file (in xmpp case send the upload URL because xmpp has no native upload support)
	if msg.Extra != nil {
		for _, rmsg := range helper.HandleExtra(&msg, b.General) {
			b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: rmsg.Channel + "@" + b.GetString("Muc"), Text: rmsg.Username + rmsg.Text})
		}
		if len(msg.Extra["file"]) > 0 {
			return b.handleUploadFile(&msg)
		}
	}

	// Post normal message
	_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})
	if err != nil {
		return "", err
	}
	return "", nil
}

func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
	tc := new(tls.Config)
	tc.InsecureSkipVerify = b.GetBool("SkipTLSVerify")
	tc.ServerName = strings.Split(b.GetString("Server"), ":")[0]
	options := xmpp.Options{
		Host:                         b.GetString("Server"),
		User:                         b.GetString("Jid"),
		Password:                     b.GetString("Password"),
		NoTLS:                        true,
		StartTLS:                     true,
		TLSConfig:                    tc,
		Debug:                        b.GetBool("debug"),
		Logger:                       b.Log.Writer(),
		Session:                      true,
		Status:                       "",
		StatusMessage:                "",
		Resource:                     "",
		InsecureAllowUnencryptedAuth: false,
	}
	var err error
	b.xc, err = options.NewClient()
	return b.xc, err
}

func (b *Bxmpp) xmppKeepAlive() chan bool {
	done := make(chan bool)
	go func() {
		ticker := time.NewTicker(90 * time.Second)
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				b.Log.Debugf("PING")
				err := b.xc.PingC2S("", "")
				if err != nil {
					b.Log.Debugf("PING failed %#v", err)
				}
			case <-done:
				return
			}
		}
	}()
	return done
}

func (b *Bxmpp) handleXMPP() error {
	var ok bool
	done := b.xmppKeepAlive()
	defer close(done)
	for {
		m, err := b.xc.Recv()
		if err != nil {
			return err
		}
		switch v := m.(type) {
		case xmpp.Chat:
			if v.Type == "groupchat" {
				b.Log.Debugf("== Receiving %#v", v)
				// skip invalid messages
				if b.skipMessage(v) {
					continue
				}
				rmsg := config.Message{Username: b.parseNick(v.Remote), Text: v.Text, Channel: b.parseChannel(v.Remote), Account: b.Account, UserID: v.Remote}

				// check if we have an action event
				rmsg.Text, ok = b.replaceAction(rmsg.Text)
				if ok {
					rmsg.Event = config.EVENT_USER_ACTION
				}
				b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
				b.Log.Debugf("<= Message is %#v", rmsg)
				b.Remote <- rmsg
			}
		case xmpp.Presence:
			// do nothing
		}
	}
}

func (b *Bxmpp) replaceAction(text string) (string, bool) {
	if strings.HasPrefix(text, "/me ") {
		return strings.Replace(text, "/me ", "", -1), true
	}
	return text, false
}

// handleUploadFile handles native upload of files
func (b *Bxmpp) handleUploadFile(msg *config.Message) (string, error) {
	for _, f := range msg.Extra["file"] {
		fi := f.(config.FileInfo)
		if fi.Comment != "" {
			msg.Text += fi.Comment + ": "
		}
		if fi.URL != "" {
			msg.Text += fi.URL
		}
		_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})
		if err != nil {
			return "", err
		}
	}
	return "", nil
}

func (b *Bxmpp) parseNick(remote string) string {
	s := strings.Split(remote, "@")
	if len(s) > 0 {
		s = strings.Split(s[1], "/")
		if len(s) == 2 {
			return s[1] // nick
		}
	}
	return ""
}

func (b *Bxmpp) parseChannel(remote string) string {
	s := strings.Split(remote, "@")
	if len(s) >= 2 {
		return s[0] // channel
	}
	return ""
}

// skipMessage skips messages that need to be skipped
func (b *Bxmpp) skipMessage(message xmpp.Chat) bool {
	// skip messages from ourselves
	if b.parseNick(message.Remote) == b.GetString("Nick") {
		return true
	}

	// skip empty messages
	if message.Text == "" {
		return true
	}

	// skip subject messages
	if strings.Contains(message.Text, "</subject>") {
		return true
	}

	// skip delayed messages
	t := time.Time{}
	return message.Stamp != t
}
<|endoftext|>


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

package msh

import (
	"math"
	"testing"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/la"
)

func TestInteg01(tst *testing.T) {

	//verbose()
	chk.PrintTitle("Integ01. integration over rotated square")

	// vertices (diamond shape)
	X := la.NewMatrixDeep2([][]float64{
		{0.0, +0.0},
		{1.0, -1.0},
		{2.0, +0.0},
		{1.0, +1.0},
	})

	// allocate cell integrator with default integration points
	o := NewIntegrator(TypeQua4, nil, "")
	chk.Int(tst, "Nverts", o.Nverts, 4)
	chk.Int(tst, "Ndim", o.Ndim, 2)
	chk.Int(tst, "Npts", o.Npts, 4)

	// integrand function
	fcn := func(x la.Vector) (f float64) {
		f = x[0]*x[0] + x[1]*x[1]
		return
	}

	// perform integration
	res := o.IntegrateSv(X, fcn)
	io.Pforan("1: res = %v\n", res)
	chk.Float64(tst, "∫(x²+y²)dxdy (default)", 1e-15, res, 8.0/3.0)

	// reset integration points
	o.ResetP(nil, "legendre_9")

	// perform integration again
	res = o.IntegrateSv(X, fcn)
	io.Pforan("2: res = %v\n", res)
	chk.Float64(tst, "∫(x²+y²)dxdy (legendre 9)", 1e-15, res, 8.0/3.0)

	// reset integration points
	o.ResetP(nil, "wilson5corner_5")

	// perform integration again
	res = o.IntegrateSv(X, fcn)
	io.Pforan("3: res = %v\n", res)
	chk.Float64(tst, "∫(x²+y²)dxdy (wilson5corner)", 1e-15, res, 8.0/3.0)
}

func TestInteg02(tst *testing.T) {

	//verbose()
	chk.PrintTitle("Integ02. integration over trapezium")

	// vertices (trapezium)
	a, b, h := 3.0, 0.5, 1.0
	X := la.NewMatrixDeep2([][]float64{
		{-a / 2.0, -h / 2.0},
		{+a / 2.0, -h / 2.0},
		{+b / 2.0, +h / 2.0},
		{-b / 2.0, +h / 2.0},
	})

	// allocate cell integrator with default integration points
	o := NewIntegrator(TypeQua4, nil, "legendre_4")

	// integrand function for second moment of inertia about x-axis: Ix
	fcnIx := func(x la.Vector) (f float64) {
		f = x[1] * x[1]
		return
	}

	// integrand function for second moment of inertia about y-axis: Iy
	fcnIy := func(x la.Vector) (f float64) {
		f = x[0] * x[0]
		return
	}

	// integrand function for second moment of inertia about the origin: I0
	fcnI0 := func(x la.Vector) (f float64) {
		f = (x[0]*x[0] + x[1]*x[1])
		return
	}

	// analytical solutions
	anaIx := (a + b) * math.Pow(h, 3) / 24.0
	anaIy := h * (math.Pow(a, 4) - math.Pow(b, 4)) / (48.0 * (a - b))
	anaI0 := anaIx + anaIy

	// compute Ix
	Ix := o.IntegrateSv(X, fcnIx)
	io.Pforan("Ix = %v\n", Ix)
	chk.Float64(tst, "Ix", 1e-15, Ix, anaIx)

	// compute Iy
	Iy := o.IntegrateSv(X, fcnIy)
	io.Pforan("Iy = %v\n", Iy)
	chk.Float64(tst, "Iy", 1e-15, Iy, anaIy)

	// compute I0
	I0 := o.IntegrateSv(X, fcnI0)
	io.Pforan("I0 = %v\n", I0)
	chk.Float64(tst, "I0", 1e-15, I0, anaI0)
}

func TestInteg03(tst *testing.T) {

	//verbose()
	chk.PrintTitle("Integ03. 2nd mom inertia: quarter of circle")

	// integrand function for second moment of inertia about x-axis: Ix
	fcnIx := func(x la.Vector) (f float64) {
		f = x[1] * x[1]
		return
	}

	// constants
	anaIx := math.Pi / 16.0 // analytical solution
	r, R := 0.0, 1.0
	nr, na := 5, 5

	// run for many quads
	//tols := []float64{0.0014, 1e-6, 1e-6, 1e-7, 1e-7, 1e-10} // 11 x 11
	tols := []float64{0.007, 1e-5, 1e-5, 1e-5, 1e-5, 1e-8} // 5 x 5
	ctypes := []int{TypeQua4, TypeQua8, TypeQua9, TypeQua12, TypeQua16, TypeQua17}
	for i, ctype := range ctypes {
		mesh := GenRing2d(ctype, nr, na, r, R, math.Pi/2.0)

		// allocate cell integrator with default integration points
		o := NewMeshIntegrator(mesh, 1)

		// compute Ix
		Ix := o.IntegrateSv(0, fcnIx)
		typekey := TypeIndexToKey[ctype]
		io.Pf("%s : Ix = %v  error = %v\n", typekey, Ix, math.Abs(Ix-anaIx))
		chk.Float64(tst, "Ix", tols[i], Ix, anaIx)
	}
}

func TestInteg04(tst *testing.T) {

	//verbose()
	chk.PrintTitle("Integ04. 2nd mom inergia: ring")

	// integrand function for second moment of inertia about x-axis: Ix
	fcnIx := func(x la.Vector) (f float64) {
		f = x[1] * x[1]
		return
	}

	// constants
	r, R := 1.0, 3.0
	nr, na := 4, 13
	anaIx := math.Pi * (math.Pow(R, 4) - math.Pow(r, 4)) / 4.0

	// run for many quads
	//tols := []float64{2.0, 0.003, 0.003, 0.004, 0.004, 1e-6} // 5 x 21
	tols := []float64{5, 0.02, 0.02, 0.003, 0.003, 1e-5} // 4 x 13
	ctypes := []int{TypeQua4, TypeQua8, TypeQua9, TypeQua12, TypeQua16, TypeQua17}
	for i, ctype := range ctypes {
		mesh := GenRing2d(ctype, nr, na, r, R, 2.0*math.Pi)

		// allocate cell integrator with default integration points
		o := NewMeshIntegrator(mesh, 1)

		// compute Ix
		Ix := o.IntegrateSv(0, fcnIx)
		typekey := TypeIndexToKey[ctype]
		io.Pf("%s : Ix = %v  error = %v\n", typekey, Ix, math.Abs(Ix-anaIx))
		chk.Float64(tst, "Ix", tols[i], Ix, anaIx)
	}
}
<commit_msg>Fix typo<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package msh

import (
	"math"
	"testing"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/la"
)

func TestInteg01(tst *testing.T) {

	//verbose()
	chk.PrintTitle("Integ01. integration over rotated square")

	// vertices (diamond shape)
	X := la.NewMatrixDeep2([][]float64{
		{0.0, +0.0},
		{1.0, -1.0},
		{2.0, +0.0},
		{1.0, +1.0},
	})

	// allocate cell integrator with default integration points
	o := NewIntegrator(TypeQua4, nil, "")
	chk.Int(tst, "Nverts", o.Nverts, 4)
	chk.Int(tst, "Ndim", o.Ndim, 2)
	chk.Int(tst, "Npts", o.Npts, 4)

	// integrand function
	fcn := func(x la.Vector) (f float64) {
		f = x[0]*x[0] + x[1]*x[1]
		return
	}

	// perform integration
	res := o.IntegrateSv(X, fcn)
	io.Pforan("1: res = %v\n", res)
	chk.Float64(tst, "∫(x²+y²)dxdy (default)", 1e-15, res, 8.0/3.0)

	// reset integration points
	o.ResetP(nil, "legendre_9")

	// perform integration again
	res = o.IntegrateSv(X, fcn)
	io.Pforan("2: res = %v\n", res)
	chk.Float64(tst, "∫(x²+y²)dxdy (legendre 9)", 1e-15, res, 8.0/3.0)

	// reset integration points
	o.ResetP(nil, "wilson5corner_5")

	// perform integration again
	res = o.IntegrateSv(X, fcn)
	io.Pforan("3: res = %v\n", res)
	chk.Float64(tst, "∫(x²+y²)dxdy (wilson5corner)", 1e-15, res, 8.0/3.0)
}

func TestInteg02(tst *testing.T) {

	//verbose()
	chk.PrintTitle("Integ02. integration over trapezium")

	// vertices (trapezium)
	a, b, h := 3.0, 0.5, 1.0
	X := la.NewMatrixDeep2([][]float64{
		{-a / 2.0, -h / 2.0},
		{+a / 2.0, -h / 2.0},
		{+b / 2.0, +h / 2.0},
		{-b / 2.0, +h / 2.0},
	})

	// allocate cell integrator with default integration points
	o := NewIntegrator(TypeQua4, nil, "legendre_4")

	// integrand function for second moment of inertia about x-axis: Ix
	fcnIx := func(x la.Vector) (f float64) {
		f = x[1] * x[1]
		return
	}

	// integrand function for second moment of inertia about y-axis: Iy
	fcnIy := func(x la.Vector) (f float64) {
		f = x[0] * x[0]
		return
	}

	// integrand function for second moment of inertia about the origin: I0
	fcnI0 := func(x la.Vector) (f float64) {
		f = (x[0]*x[0] + x[1]*x[1])
		return
	}

	// analytical solutions
	anaIx := (a + b) * math.Pow(h, 3) / 24.0
	anaIy := h * (math.Pow(a, 4) - math.Pow(b, 4)) / (48.0 * (a - b))
	anaI0 := anaIx + anaIy

	// compute Ix
	Ix := o.IntegrateSv(X, fcnIx)
	io.Pforan("Ix = %v\n", Ix)
	chk.Float64(tst, "Ix", 1e-15, Ix, anaIx)

	// compute Iy
	Iy := o.IntegrateSv(X, fcnIy)
	io.Pforan("Iy = %v\n", Iy)
	chk.Float64(tst, "Iy", 1e-15, Iy, anaIy)

	// compute I0
	I0 := o.IntegrateSv(X, fcnI0)
	io.Pforan("I0 = %v\n", I0)
	chk.Float64(tst, "I0", 1e-15, I0, anaI0)
}

func TestInteg03(tst *testing.T) {

	//verbose()
	chk.PrintTitle("Integ03. 2nd mom inertia: quarter of circle")

	// integrand function for second moment of inertia about x-axis: Ix
	fcnIx := func(x la.Vector) (f float64) {
		f = x[1] * x[1]
		return
	}

	// constants
	anaIx := math.Pi / 16.0 // analytical solution
	r, R := 0.0, 1.0
	nr, na := 5, 5

	// run for many quads
	//tols := []float64{0.0014, 1e-6, 1e-6, 1e-7, 1e-7, 1e-10} // 11 x 11
	tols := []float64{0.007, 1e-5, 1e-5, 1e-5, 1e-5, 1e-8} // 5 x 5
	ctypes := []int{TypeQua4, TypeQua8, TypeQua9, TypeQua12, TypeQua16, TypeQua17}
	for i, ctype := range ctypes {
		mesh := GenRing2d(ctype, nr, na, r, R, math.Pi/2.0)

		// allocate cell integrator with default integration points
		o := NewMeshIntegrator(mesh, 1)

		// compute Ix
		Ix := o.IntegrateSv(0, fcnIx)
		typekey := TypeIndexToKey[ctype]
		io.Pf("%s : Ix = %v  error = %v\n", typekey, Ix, math.Abs(Ix-anaIx))
		chk.Float64(tst, "Ix", tols[i], Ix, anaIx)
	}
}

func TestInteg04(tst *testing.T) {

	//verbose()
	chk.PrintTitle("Integ04. 2nd mom inertia: ring")

	// integrand function for second moment of inertia about x-axis: Ix
	fcnIx := func(x la.Vector) (f float64) {
		f = x[1] * x[1]
		return
	}

	// constants
	r, R := 1.0, 3.0
	nr, na := 4, 13
	anaIx := math.Pi * (math.Pow(R, 4) - math.Pow(r, 4)) / 4.0

	// run for many quads
	//tols := []float64{2.0, 0.003, 0.003, 0.004, 0.004, 1e-6} // 5 x 21
	tols := []float64{5, 0.02, 0.02, 0.003, 0.003, 1e-5} // 4 x 13
	ctypes := []int{TypeQua4, TypeQua8, TypeQua9, TypeQua12, TypeQua16, TypeQua17}
	for i, ctype := range ctypes {
		mesh := GenRing2d(ctype, nr, na, r, R, 2.0*math.Pi)

		// allocate cell integrator with default integration points
		o := NewMeshIntegrator(mesh, 1)

		// compute Ix
		Ix := o.IntegrateSv(0, fcnIx)
		typekey := TypeIndexToKey[ctype]
		io.Pf("%s : Ix = %v  error = %v\n", typekey, Ix, math.Abs(Ix-anaIx))
		chk.Float64(tst, "Ix", tols[i], Ix, anaIx)
	}
}
<|endoftext|>


<commit_before>package language

import (
	"github.com/prasmussen/glot-code-runner/language/assembly"
	"github.com/prasmussen/glot-code-runner/language/ats"
	"github.com/prasmussen/glot-code-runner/language/bash"
	"github.com/prasmussen/glot-code-runner/language/c"
	"github.com/prasmussen/glot-code-runner/language/clojure"
	"github.com/prasmussen/glot-code-runner/language/coffeescript"
	"github.com/prasmussen/glot-code-runner/language/cpp"
	"github.com/prasmussen/glot-code-runner/language/crystal"
	"github.com/prasmussen/glot-code-runner/language/csharp"
	"github.com/prasmussen/glot-code-runner/language/d"
	"github.com/prasmussen/glot-code-runner/language/elixir"
	"github.com/prasmussen/glot-code-runner/language/elm"
	"github.com/prasmussen/glot-code-runner/language/erlang"
	"github.com/prasmussen/glot-code-runner/language/fsharp"
	"github.com/prasmussen/glot-code-runner/language/golang"
	"github.com/prasmussen/glot-code-runner/language/groovy"
	"github.com/prasmussen/glot-code-runner/language/haskell"
	"github.com/prasmussen/glot-code-runner/language/idris"
	"github.com/prasmussen/glot-code-runner/language/java"
	"github.com/prasmussen/glot-code-runner/language/javascript"
	"github.com/prasmussen/glot-code-runner/language/julia"
	"github.com/prasmussen/glot-code-runner/language/kotlin"
	"github.com/prasmussen/glot-code-runner/language/lua"
	"github.com/prasmussen/glot-code-runner/language/nim"
	"github.com/prasmussen/glot-code-runner/language/ocaml"
	"github.com/prasmussen/glot-code-runner/language/perl"
	"github.com/prasmussen/glot-code-runner/language/perl6"
	"github.com/prasmussen/glot-code-runner/language/php"
	"github.com/prasmussen/glot-code-runner/language/python"
	"github.com/prasmussen/glot-code-runner/language/ruby"
	"github.com/prasmussen/glot-code-runner/language/rust"
	"github.com/prasmussen/glot-code-runner/language/scala"
	"github.com/prasmussen/glot-code-runner/language/swift"
	"github.com/prasmussen/glot-code-runner/language/typescript"
)

type runFn func([]string, string) (string, string, error)

var languages = map[string]runFn{
	"assembly":     assembly.Run,
	"ats":          ats.Run,
	"bash":         bash.Run,
	"c":            c.Run,
	"clojure":      clojure.Run,
	"coffeescript": coffeescript.Run,
	"crystal":      crystal.Run,
	"csharp":       csharp.Run,
	"d":            d.Run,
	"elixir":       elixir.Run,
	"elm":          elm.Run,
	"cpp":          cpp.Run,
	"erlang":       erlang.Run,
	"fsharp":       fsharp.Run,
	"haskell":      haskell.Run,
	"idris":        idris.Run,
	"go":           golang.Run,
	"groovy":       groovy.Run,
	"java":         java.Run,
	"javascript":   javascript.Run,
	"julia":        julia.Run,
	"kotlin":       kotlin.Run,
	"lua":          lua.Run,
	"nim":          nim.Run,
	"ocaml":        ocaml.Run,
	"perl":         perl.Run,
	"perl6":        perl6.Run,
	"php":          php.Run,
	"python":       python.Run,
	"ruby":         ruby.Run,
	"rust":         rust.Run,
	"scala":        scala.Run,
	"swift":        swift.Run,
	"typescript":   typescript.Run,
}

func IsSupported(lang string) bool {
	_, supported := languages[lang]
	return supported
}

func Run(lang string, files []string, stdin string) (string, string, error) {
	return languages[lang](files, stdin)
}
<commit_msg>Enable mercury support<commit_after>package language

import (
	"github.com/prasmussen/glot-code-runner/language/assembly"
	"github.com/prasmussen/glot-code-runner/language/ats"
	"github.com/prasmussen/glot-code-runner/language/bash"
	"github.com/prasmussen/glot-code-runner/language/c"
	"github.com/prasmussen/glot-code-runner/language/clojure"
	"github.com/prasmussen/glot-code-runner/language/coffeescript"
	"github.com/prasmussen/glot-code-runner/language/cpp"
	"github.com/prasmussen/glot-code-runner/language/crystal"
	"github.com/prasmussen/glot-code-runner/language/csharp"
	"github.com/prasmussen/glot-code-runner/language/d"
	"github.com/prasmussen/glot-code-runner/language/elixir"
	"github.com/prasmussen/glot-code-runner/language/elm"
	"github.com/prasmussen/glot-code-runner/language/erlang"
	"github.com/prasmussen/glot-code-runner/language/fsharp"
	"github.com/prasmussen/glot-code-runner/language/golang"
	"github.com/prasmussen/glot-code-runner/language/groovy"
	"github.com/prasmussen/glot-code-runner/language/haskell"
	"github.com/prasmussen/glot-code-runner/language/idris"
	"github.com/prasmussen/glot-code-runner/language/java"
	"github.com/prasmussen/glot-code-runner/language/javascript"
	"github.com/prasmussen/glot-code-runner/language/julia"
	"github.com/prasmussen/glot-code-runner/language/kotlin"
	"github.com/prasmussen/glot-code-runner/language/lua"
	"github.com/prasmussen/glot-code-runner/language/mercury"
	"github.com/prasmussen/glot-code-runner/language/nim"
	"github.com/prasmussen/glot-code-runner/language/ocaml"
	"github.com/prasmussen/glot-code-runner/language/perl"
	"github.com/prasmussen/glot-code-runner/language/perl6"
	"github.com/prasmussen/glot-code-runner/language/php"
	"github.com/prasmussen/glot-code-runner/language/python"
	"github.com/prasmussen/glot-code-runner/language/ruby"
	"github.com/prasmussen/glot-code-runner/language/rust"
	"github.com/prasmussen/glot-code-runner/language/scala"
	"github.com/prasmussen/glot-code-runner/language/swift"
	"github.com/prasmussen/glot-code-runner/language/typescript"
)

type runFn func([]string, string) (string, string, error)

var languages = map[string]runFn{
	"assembly":     assembly.Run,
	"ats":          ats.Run,
	"bash":         bash.Run,
	"c":            c.Run,
	"clojure":      clojure.Run,
	"coffeescript": coffeescript.Run,
	"crystal":      crystal.Run,
	"csharp":       csharp.Run,
	"d":            d.Run,
	"elixir":       elixir.Run,
	"elm":          elm.Run,
	"cpp":          cpp.Run,
	"erlang":       erlang.Run,
	"fsharp":       fsharp.Run,
	"haskell":      haskell.Run,
	"idris":        idris.Run,
	"go":           golang.Run,
	"groovy":       groovy.Run,
	"java":         java.Run,
	"javascript":   javascript.Run,
	"julia":        julia.Run,
	"kotlin":       kotlin.Run,
	"lua":          lua.Run,
	"mercury":      mercury.Run,
	"nim":          nim.Run,
	"ocaml":        ocaml.Run,
	"perl":         perl.Run,
	"perl6":        perl6.Run,
	"php":          php.Run,
	"python":       python.Run,
	"ruby":         ruby.Run,
	"rust":         rust.Run,
	"scala":        scala.Run,
	"swift":        swift.Run,
	"typescript":   typescript.Run,
}

func IsSupported(lang string) bool {
	_, supported := languages[lang]
	return supported
}

func Run(lang string, files []string, stdin string) (string, string, error) {
	return languages[lang](files, stdin)
}
<|endoftext|>


<commit_before>package bosh_test

import (
	"github.com/FidelityInternational/virgil/bosh"
	. "github.com/cloudfoundry-community/gogobosh"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("#FindDeployment", func() {
	var deployments []Deployment

	BeforeEach(func() {
		deployments = []Deployment{
			{
				Name:        "cf-warden-12345",
				CloudConfig: "none",
				Releases: []Resource{
					Resource{
						Name:    "cf",
						Version: "223",
					},
				},
				Stemcells: []Resource{
					Resource{
						Name:    "bosh-warden-boshlite-ubuntu-trusty-go_agent",
						Version: "3126",
					},
				},
			},
			{
				Name:        "cf-garden-12345",
				CloudConfig: "none",
				Releases: []Resource{
					Resource{
						Name:    "cf",
						Version: "223",
					},
				},
				Stemcells: []Resource{
					Resource{
						Name:    "bosh-warden-boshlite-ubuntu-trusty-go_agent",
						Version: "3126",
					},
				},
			},
		}
	})

	Context("when a deployment can be found", func() {
		It("finds the first matching deployment name based on a regex", func() {
			Ω(bosh.FindDeployment(deployments, "cf-garden*")).Should(Equal("cf-garden-12345"))
		})
	})

	Context("when a deployment cannot be found", func() {
		It("returns an empty string", func() {
			Ω(bosh.FindDeployment(deployments, "bosh*")).Should(BeEmpty())
		})
	})
})

var _ = Describe("#FindVMs", func() {
	It("Returns an array of all VMs matching the given regex", func() {
		vms := []VM{
			{
				IPs:     []string{"1.1.1.1"},
				JobName: "etcd_server-12344",
			},
			{
				IPs:     []string{"4.4.4.4"},
				JobName: "consul_server-567887",
			},
			{
				IPs:     []string{"3.3.3.3"},
				JobName: "etcd_server-98764",
			},
			{
				IPs:     []string{"4.4.4.4"},
				JobName: "consul_server-12344",
			},
			{
				IPs:     []string{"5.5.5.5"},
				JobName: "etcd_server-567887",
			},
		}
		matchedVMs := bosh.FindVMs(vms, "^etcd_server.+$")
		Ω(matchedVMs).Should(HaveLen(3))
		Ω(matchedVMs).Should(ContainElement(VM{
			IPs:     []string{"1.1.1.1"},
			JobName: "etcd_server-12344",
		}))
		Ω(matchedVMs).Should(ContainElement(VM{
			IPs:     []string{"3.3.3.3"},
			JobName: "etcd_server-98764",
		}))
		Ω(matchedVMs).Should(ContainElement(VM{
			IPs:     []string{"5.5.5.5"},
			JobName: "etcd_server-567887",
		}))
	})
})

var _ = Describe("#GetAllIPs", func() {
	It("return IPs for the provided VMs", func() {
		var deploymentVMs = []VM{
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   0,
				VMCID:   "11",
				AgentID: "11",
				IPs:     []string{"11.11.11.11"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   1,
				VMCID:   "2",
				AgentID: "2",
				IPs:     []string{"2.2.2.2"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   2,
				VMCID:   "6",
				AgentID: "6",
				IPs:     []string{"6.6.6.6"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   3,
				VMCID:   "7",
				AgentID: "7",
				IPs:     []string{"7.7.7.7"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   4,
				VMCID:   "8",
				AgentID: "8",
				IPs:     []string{"8.8.8.8"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   5,
				VMCID:   "9",
				AgentID: "9",
				IPs:     []string{"9.9.9.9"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   6,
				VMCID:   "10",
				AgentID: "10",
				IPs:     []string{"10.10.10.10"},
			},
			{
				JobName: "diego_cell-partition-d284104a9345228c01e2",
				Index:   0,
				VMCID:   "4",
				AgentID: "4",
				IPs:     []string{"4.4.4.4"},
			},
			{
				JobName: "diego_cell-partition-d284104a9345228c01e2",
				Index:   1,
				VMCID:   "5",
				AgentID: "5",
				IPs:     []string{"5.5.5.5"},
			},
		}
		vmIPs := bosh.GetAllIPs(deploymentVMs)
		Expect(vmIPs).To(HaveLen(9))
		Expect(vmIPs).To(ContainElement("11.11.11.11"))
		Expect(vmIPs).To(ContainElement("2.2.2.2"))
		Expect(vmIPs).To(ContainElement("4.4.4.4"))
		Expect(vmIPs).To(ContainElement("5.5.5.5"))
		Expect(vmIPs).To(ContainElement("6.6.6.6"))
		Expect(vmIPs).To(ContainElement("7.7.7.7"))
		Expect(vmIPs).To(ContainElement("8.8.8.8"))
		Expect(vmIPs).To(ContainElement("9.9.9.9"))
		Expect(vmIPs).To(ContainElement("10.10.10.10"))
	})
})
<commit_msg>correct formatting<commit_after>package bosh_test

import (
	"github.com/FidelityInternational/virgil/bosh"
	. "github.com/cloudfoundry-community/gogobosh"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("#FindDeployment", func() {
	var deployments []Deployment

	BeforeEach(func() {
		deployments = []Deployment{
			{
				Name:        "cf-warden-12345",
				CloudConfig: "none",
				Releases: []Resource{
					{
						Name:    "cf",
						Version: "223",
					},
				},
				Stemcells: []Resource{
					{
						Name:    "bosh-warden-boshlite-ubuntu-trusty-go_agent",
						Version: "3126",
					},
				},
			},
			{
				Name:        "cf-garden-12345",
				CloudConfig: "none",
				Releases: []Resource{
					{
						Name:    "cf",
						Version: "223",
					},
				},
				Stemcells: []Resource{
					{
						Name:    "bosh-warden-boshlite-ubuntu-trusty-go_agent",
						Version: "3126",
					},
				},
			},
		}
	})

	Context("when a deployment can be found", func() {
		It("finds the first matching deployment name based on a regex", func() {
			Ω(bosh.FindDeployment(deployments, "cf-garden*")).Should(Equal("cf-garden-12345"))
		})
	})

	Context("when a deployment cannot be found", func() {
		It("returns an empty string", func() {
			Ω(bosh.FindDeployment(deployments, "bosh*")).Should(BeEmpty())
		})
	})
})

var _ = Describe("#FindVMs", func() {
	It("Returns an array of all VMs matching the given regex", func() {
		vms := []VM{
			{
				IPs:     []string{"1.1.1.1"},
				JobName: "etcd_server-12344",
			},
			{
				IPs:     []string{"4.4.4.4"},
				JobName: "consul_server-567887",
			},
			{
				IPs:     []string{"3.3.3.3"},
				JobName: "etcd_server-98764",
			},
			{
				IPs:     []string{"4.4.4.4"},
				JobName: "consul_server-12344",
			},
			{
				IPs:     []string{"5.5.5.5"},
				JobName: "etcd_server-567887",
			},
		}
		matchedVMs := bosh.FindVMs(vms, "^etcd_server.+$")
		Ω(matchedVMs).Should(HaveLen(3))
		Ω(matchedVMs).Should(ContainElement(VM{
			IPs:     []string{"1.1.1.1"},
			JobName: "etcd_server-12344",
		}))
		Ω(matchedVMs).Should(ContainElement(VM{
			IPs:     []string{"3.3.3.3"},
			JobName: "etcd_server-98764",
		}))
		Ω(matchedVMs).Should(ContainElement(VM{
			IPs:     []string{"5.5.5.5"},
			JobName: "etcd_server-567887",
		}))
	})
})

var _ = Describe("#GetAllIPs", func() {
	It("return IPs for the provided VMs", func() {
		var deploymentVMs = []VM{
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   0,
				VMCID:   "11",
				AgentID: "11",
				IPs:     []string{"11.11.11.11"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   1,
				VMCID:   "2",
				AgentID: "2",
				IPs:     []string{"2.2.2.2"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   2,
				VMCID:   "6",
				AgentID: "6",
				IPs:     []string{"6.6.6.6"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   3,
				VMCID:   "7",
				AgentID: "7",
				IPs:     []string{"7.7.7.7"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   4,
				VMCID:   "8",
				AgentID: "8",
				IPs:     []string{"8.8.8.8"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   5,
				VMCID:   "9",
				AgentID: "9",
				IPs:     []string{"9.9.9.9"},
			},
			{
				JobName: "dea-partition-d284104a9345228c01e2",
				Index:   6,
				VMCID:   "10",
				AgentID: "10",
				IPs:     []string{"10.10.10.10"},
			},
			{
				JobName: "diego_cell-partition-d284104a9345228c01e2",
				Index:   0,
				VMCID:   "4",
				AgentID: "4",
				IPs:     []string{"4.4.4.4"},
			},
			{
				JobName: "diego_cell-partition-d284104a9345228c01e2",
				Index:   1,
				VMCID:   "5",
				AgentID: "5",
				IPs:     []string{"5.5.5.5"},
			},
		}
		vmIPs := bosh.GetAllIPs(deploymentVMs)
		Expect(vmIPs).To(HaveLen(9))
		Expect(vmIPs).To(ContainElement("11.11.11.11"))
		Expect(vmIPs).To(ContainElement("2.2.2.2"))
		Expect(vmIPs).To(ContainElement("4.4.4.4"))
		Expect(vmIPs).To(ContainElement("5.5.5.5"))
		Expect(vmIPs).To(ContainElement("6.6.6.6"))
		Expect(vmIPs).To(ContainElement("7.7.7.7"))
		Expect(vmIPs).To(ContainElement("8.8.8.8"))
		Expect(vmIPs).To(ContainElement("9.9.9.9"))
		Expect(vmIPs).To(ContainElement("10.10.10.10"))
	})
})
<|endoftext|>