<commit_before>package api

import (
	"fmt"
	"net/smtp"
	"strings"

	"errors"
	"github.com/OpenBazaar/openbazaar-go/api/notifications"
	"github.com/OpenBazaar/openbazaar-go/core"
	"github.com/OpenBazaar/openbazaar-go/repo"
)

// Notification manager intercepts data form 'inChan' which is embedded
// in different parts of the system and retransmits to the 'outChan',
// which is listened by websocket API, while adding specific handling for
// each received object.
type notificationManager struct {
	node *core.OpenBazaarNode
}

func manageNotifications(node *core.OpenBazaarNode, out chan []byte) chan interface{} {
	manager := &notificationManager{node: node}
	nodeBroadcast := make(chan interface{})
	go func() {
		for {
			n := <-nodeBroadcast
			// Fixme: right now this assumes that n is a notification but it should be agnostic
			// enough to let us send any data to the websocket. You can technically do that by
			// sending over a []byte as the serialize function ignores []bytes but it's kind of hacky.
			manager.sendNotification(n)
			sanitized, err := SanitizeJSON(notifications.Serialize(n))
			if err != nil {
				log.Error(err)
				continue
			}
			out <- sanitized
		}
	}()
	return nodeBroadcast
}

type notifier interface {
	notify(n interface{}) error
}

// Send notification via all supported notifier mechanisms
func (m *notificationManager) sendNotification(n interface{}) {
	for _, notifier := range m.getNotifiers() {
		if err := notifier.notify(n); err != nil {
			log.Errorf("Notification failed: %s", err.Error())
		}
	}
}

// Create list of notifiers based on settings data (currently only SMTP is available)
// TODO: should be extended to include new notifiers in the list
func (m *notificationManager) getNotifiers() []notifier {
	settings, err := m.node.Datastore.Settings().Get()
	notifiers := []notifier{}
	if err != nil {
		return notifiers
	}

	// SMTP notifier
	conf := settings.SMTPSettings
	if conf != nil && conf.Notifications {
		notifiers = append(notifiers, &smtpNotifier{settings: conf})
	}
	return notifiers
}

// Notifier implementations
type smtpNotifier struct {
	settings *repo.SMTPSettings
}

func (notifier *smtpNotifier) notify(n interface{}) error {
	template := strings.Join([]string{
		"From: %s",
		"To: %s",
		"MIME-Version: 1.0",
		"Content-Type: text/html; charset=UTF-8",
		"Subject: [OpenBazaar] %s\r\n",
		"%s\r\n",
	}, "\r\n")
	head, body := notifications.Describe(n)
	if head == "" || body == "" {
		return nil
	}
	conf := notifier.settings
	data := fmt.Sprintf(template, conf.SenderEmail, conf.RecipientEmail, head, body)
	return sendEmail(notifier.settings, []byte(data))
}

// Send email using PLAIN authentication to the server
func sendEmail(conf *repo.SMTPSettings, body []byte) error {
	hostAndPort := strings.Split(conf.ServerAddress, ":")
	host := conf.ServerAddress
	if len(hostAndPort) == 2 {
		host = hostAndPort[0]
	}
	auth := smtp.PlainAuth("", conf.SenderEmail, conf.Password, host)
	recipients := []string{conf.RecipientEmail}
	return smtp.SendMail(conf.ServerAddress, auth, conf.SenderEmail, recipients, body)
}

func validateSMTPSettings(s repo.SettingsData) error {
	if s.SMTPSettings != nil && s.SMTPSettings.Notifications &&
		(s.SMTPSettings.Password == "" || s.SMTPSettings.Username == "" || s.SMTPSettings.RecipientEmail == "" || s.SMTPSettings.SenderEmail == "" || s.SMTPSettings.ServerAddress == "") {
		return errors.New("SMTP fields must be set if notifications are turned on")
	}
	return nil
}
<commit_msg>Fix bug in smpt notifier<commit_after>package api

import (
	"fmt"
	"net/smtp"
	"strings"

	"errors"
	"github.com/OpenBazaar/openbazaar-go/api/notifications"
	"github.com/OpenBazaar/openbazaar-go/core"
	"github.com/OpenBazaar/openbazaar-go/repo"
)

// Notification manager intercepts data form 'inChan' which is embedded
// in different parts of the system and retransmits to the 'outChan',
// which is listened by websocket API, while adding specific handling for
// each received object.
type notificationManager struct {
	node *core.OpenBazaarNode
}

func manageNotifications(node *core.OpenBazaarNode, out chan []byte) chan interface{} {
	manager := &notificationManager{node: node}
	nodeBroadcast := make(chan interface{})
	go func() {
		for {
			n := <-nodeBroadcast
			// Fixme: right now this assumes that n is a notification but it should be agnostic
			// enough to let us send any data to the websocket. You can technically do that by
			// sending over a []byte as the serialize function ignores []bytes but it's kind of hacky.
			manager.sendNotification(n)
			sanitized, err := SanitizeJSON(notifications.Serialize(n))
			if err != nil {
				log.Error(err)
				continue
			}
			out <- sanitized
		}
	}()
	return nodeBroadcast
}

type notifier interface {
	notify(n interface{}) error
}

// Send notification via all supported notifier mechanisms
func (m *notificationManager) sendNotification(n interface{}) {
	for _, notifier := range m.getNotifiers() {
		if err := notifier.notify(n); err != nil {
			log.Errorf("Notification failed: %s", err.Error())
		}
	}
}

// Create list of notifiers based on settings data (currently only SMTP is available)
// TODO: should be extended to include new notifiers in the list
func (m *notificationManager) getNotifiers() []notifier {
	settings, err := m.node.Datastore.Settings().Get()
	notifiers := []notifier{}
	if err != nil {
		return notifiers
	}

	// SMTP notifier
	conf := settings.SMTPSettings
	if conf != nil && conf.Notifications {
		notifiers = append(notifiers, &smtpNotifier{settings: conf})
	}
	return notifiers
}

// Notifier implementations
type smtpNotifier struct {
	settings *repo.SMTPSettings
}

func (notifier *smtpNotifier) notify(n interface{}) error {
	template := strings.Join([]string{
		"From: %s",
		"To: %s",
		"MIME-Version: 1.0",
		"Content-Type: text/html; charset=UTF-8",
		"Subject: [OpenBazaar] %s\r\n",
		"%s\r\n",
	}, "\r\n")
	head, body := notifications.Describe(n)
	if head == "" || body == "" {
		return nil
	}
	conf := notifier.settings
	data := fmt.Sprintf(template, conf.SenderEmail, conf.RecipientEmail, head, body)
	return sendEmail(notifier.settings, []byte(data))
}

// Send email using PLAIN authentication to the server
func sendEmail(conf *repo.SMTPSettings, body []byte) error {
	hostAndPort := strings.Split(conf.ServerAddress, ":")
	host := conf.ServerAddress
	if len(hostAndPort) == 2 {
		host = hostAndPort[0]
	}
	auth := smtp.PlainAuth("", conf.Username, conf.Password, host)
	recipients := []string{conf.RecipientEmail}
	return smtp.SendMail(conf.ServerAddress, auth, conf.SenderEmail, recipients, body)
}

func validateSMTPSettings(s repo.SettingsData) error {
	if s.SMTPSettings != nil && s.SMTPSettings.Notifications &&
		(s.SMTPSettings.Password == "" || s.SMTPSettings.Username == "" || s.SMTPSettings.RecipientEmail == "" || s.SMTPSettings.SenderEmail == "" || s.SMTPSettings.ServerAddress == "") {
		return errors.New("SMTP fields must be set if notifications are turned on")
	}
	return nil
}
<|endoftext|>


<commit_before>package graphql

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/samsarahq/thunder/reactive"
)

const (
	MaxSubscriptions = 200
	MinRerunInterval = 1 * time.Second
)

type MakeCtxFunc func(context.Context) context.Context

type GraphqlLogger interface {
	StartExecution(ctx context.Context, tags map[string]string, initial bool)
	FinishedExecution(ctx context.Context, tags map[string]string, delay time.Duration)
	Error(ctx context.Context, err error, tags map[string]string)
}

type conn struct {
	writeMu sync.Mutex
	socket  *websocket.Conn

	schema  *Schema
	makeCtx MakeCtxFunc
	logger  GraphqlLogger

	url string

	mu            sync.Mutex
	subscriptions map[string]*reactive.Rerunner
}

type inEnvelope struct {
	ID      string          `json:"id"`
	Type    string          `json:"type"`
	Message json.RawMessage `json:"message"`
}

type outEnvelope struct {
	ID      string      `json:"id,omitempty"`
	Type    string      `json:"type"`
	Message interface{} `json:"message,omitempty"`
}

type subscribeMessage struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables"`
}

type mutateMessage struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables"`
}

type SanitizedError interface {
	error
	SanitizedError() string
}

type SafeError struct {
	message string
}

func (e SafeError) Error() string {
	return e.message
}

func (e SafeError) SanitizedError() string {
	return e.message
}

func NewSafeError(format string, a ...interface{}) error {
	return SafeError{message: fmt.Sprintf(format, a...)}
}

func sanitizeError(err error) string {
	if sanitized, ok := err.(SanitizedError); ok {
		return sanitized.SanitizedError()
	}
	return "Internal server error"
}

func isCloseError(err error) bool {
	_, ok := err.(*websocket.CloseError)
	return ok || err == websocket.ErrCloseSent
}

func (c *conn) writeOrClose(id string, typ string, message interface{}) {
	c.writeMu.Lock()
	defer c.writeMu.Unlock()

	if err := c.socket.WriteJSON(outEnvelope{
		ID:      id,
		Type:    typ,
		Message: message,
	}); err != nil {
		if !isCloseError(err) {
			c.socket.Close()
			log.Printf("socket.WriteJSON: %s\n", err)
		}
	}
}

func mustMarshalJson(v interface{}) string {
	bytes, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}
	return string(bytes)
}

func (c *conn) handleSubscribe(id string, subscribe *subscribeMessage) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.subscriptions[id]; ok {
		return NewSafeError("duplicate subscription")
	}

	if len(c.subscriptions)+1 > MaxSubscriptions {
		return NewSafeError("too many subscriptions")
	}

	selectionSet, err := Parse(subscribe.Query, subscribe.Variables)
	if err != nil {
		return err
	}
	if err := PrepareQuery(c.schema.Query, selectionSet); err != nil {
		return err
	}

	var previous interface{}

	e := Executor{}

	initial := true
	tags := map[string]string{"url": c.url, "query": subscribe.Query, "queryVariables": mustMarshalJson(subscribe.Variables), "id": id}

	c.subscriptions[id] = reactive.NewRerunner(context.Background(), func(ctx context.Context) (interface{}, error) {
		ctx = c.makeCtx(ctx)

		start := time.Now()
		c.logger.StartExecution(ctx, tags, initial)
		current, err := e.Execute(ctx, c.schema.Query, nil, selectionSet)
		c.logger.FinishedExecution(ctx, tags, time.Since(start))

		if err != nil {
			c.writeOrClose(id, "error", sanitizeError(err))
			go c.closeSubscription(id)
			if _, ok := err.(SanitizedError); !ok {
				c.logger.Error(ctx, err, tags)
			}
			return nil, err
		}

		delta, diff := Diff(previous, current)
		previous = current
		initial = false

		if diff {
			c.writeOrClose(id, "update", PrepareForMarshal(delta))
		}

		return nil, nil
	}, MinRerunInterval)

	return nil
}

func (c *conn) handleMutate(id string, mutate *mutateMessage) error {
	// TODO: deduplicate code
	c.mu.Lock()
	defer c.mu.Unlock()

	selectionSet, err := Parse(mutate.Query, mutate.Variables)
	if err != nil {
		return err
	}
	if err := PrepareQuery(c.schema.Mutation, selectionSet); err != nil {
		return err
	}

	e := Executor{}

	tags := map[string]string{"url": c.url, "query": mutate.Query, "queryVariables": mustMarshalJson(mutate.Variables), "id": id}

	c.subscriptions[id] = reactive.NewRerunner(context.Background(), func(ctx context.Context) (interface{}, error) {
		ctx = c.makeCtx(context.Background())

		start := time.Now()
		c.logger.StartExecution(ctx, tags, true)
		current, err := e.Execute(ctx, c.schema.Mutation, c.schema.Mutation, selectionSet)
		c.logger.FinishedExecution(ctx, tags, time.Since(start))

		if err != nil {
			c.writeOrClose(id, "error", sanitizeError(err))
			go c.closeSubscription(id)
			if _, ok := err.(SanitizedError); !ok {
				c.logger.Error(ctx, err, tags)
			}
			return nil, err
		}

		delta, _ := Diff(nil, current)
		c.writeOrClose(id, "result", PrepareForMarshal(delta))

		return nil, errors.New("stop")
	}, MinRerunInterval)

	return nil
}

func (c *conn) closeSubscription(id string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if runner, ok := c.subscriptions[id]; ok {
		runner.Stop()
		delete(c.subscriptions, id)
	}
}

func (c *conn) closeSubscriptions() {
	c.mu.Lock()
	defer c.mu.Unlock()

	for id, runner := range c.subscriptions {
		runner.Stop()
		delete(c.subscriptions, id)
	}
}

func (c *conn) handle(e *inEnvelope) error {
	switch e.Type {
	case "subscribe":
		var subscribe subscribeMessage
		if err := json.Unmarshal(e.Message, &subscribe); err != nil {
			return err
		}
		return c.handleSubscribe(e.ID, &subscribe)

	case "unsubscribe":
		c.closeSubscription(e.ID)
		return nil

	case "mutate":
		var mutate mutateMessage
		if err := json.Unmarshal(e.Message, &mutate); err != nil {
			return err
		}
		return c.handleMutate(e.ID, &mutate)

	case "echo":
		c.writeOrClose(e.ID, "echo", nil)
		return nil

	case "url":
		var url string
		if err := json.Unmarshal(e.Message, &url); err != nil {
			return err
		}
		c.url = url
		return nil

	default:
		return NewSafeError("unknown message type")
	}
}

type simpleLogger struct {
}

func (s *simpleLogger) StartExecution(ctx context.Context, tags map[string]string, initial bool) {
}
func (s *simpleLogger) FinishedExecution(ctx context.Context, tags map[string]string, delay time.Duration) {
}
func (s *simpleLogger) Error(ctx context.Context, err error, tags map[string]string) {
	log.Printf("error:%v\n%s", tags, err)
}

func Handler(schema *Schema) http.Handler {
	upgrader := &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		socket, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Printf("upgrader.Upgrade: %v", err)
			return
		}
		defer socket.Close()

		makeCtx := func(ctx context.Context) context.Context {
			return ctx
		}

		ServeJSONSocket(socket, schema, makeCtx, &simpleLogger{})
	})
}

func ServeJSONSocket(socket *websocket.Conn, schema *Schema, makeCtx MakeCtxFunc, logger GraphqlLogger) {
	c := &conn{
		socket: socket,

		schema:  schema,
		makeCtx: makeCtx,
		logger:  logger,

		subscriptions: make(map[string]*reactive.Rerunner),
	}

	defer c.closeSubscriptions()

	for {
		var envelope inEnvelope
		if err := c.socket.ReadJSON(&envelope); err != nil {
			if !isCloseError(err) {
				log.Println("socket.ReadJSON:", err)
			}
			return
		}

		if err := c.handle(&envelope); err != nil {
			log.Println("c.handle:", err)
			c.writeOrClose(envelope.ID, "error", sanitizeError(err))
		}
	}
}
<commit_msg>graphql: use a JSONSocket interface for the websocket in ServeJSONSocket<commit_after>package graphql

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/samsarahq/thunder/reactive"
)

const (
	MaxSubscriptions = 200
	MinRerunInterval = 1 * time.Second
)

type JSONSocket interface {
	ReadJSON(value interface{}) error
	WriteJSON(value interface{}) error
	Close() error
}

type MakeCtxFunc func(context.Context) context.Context

type GraphqlLogger interface {
	StartExecution(ctx context.Context, tags map[string]string, initial bool)
	FinishedExecution(ctx context.Context, tags map[string]string, delay time.Duration)
	Error(ctx context.Context, err error, tags map[string]string)
}

type conn struct {
	writeMu sync.Mutex
	socket  JSONSocket

	schema  *Schema
	makeCtx MakeCtxFunc
	logger  GraphqlLogger

	url string

	mu            sync.Mutex
	subscriptions map[string]*reactive.Rerunner
}

type inEnvelope struct {
	ID      string          `json:"id"`
	Type    string          `json:"type"`
	Message json.RawMessage `json:"message"`
}

type outEnvelope struct {
	ID      string      `json:"id,omitempty"`
	Type    string      `json:"type"`
	Message interface{} `json:"message,omitempty"`
}

type subscribeMessage struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables"`
}

type mutateMessage struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables"`
}

type SanitizedError interface {
	error
	SanitizedError() string
}

type SafeError struct {
	message string
}

func (e SafeError) Error() string {
	return e.message
}

func (e SafeError) SanitizedError() string {
	return e.message
}

func NewSafeError(format string, a ...interface{}) error {
	return SafeError{message: fmt.Sprintf(format, a...)}
}

func sanitizeError(err error) string {
	if sanitized, ok := err.(SanitizedError); ok {
		return sanitized.SanitizedError()
	}
	return "Internal server error"
}

func isCloseError(err error) bool {
	_, ok := err.(*websocket.CloseError)
	return ok || err == websocket.ErrCloseSent
}

func (c *conn) writeOrClose(id string, typ string, message interface{}) {
	c.writeMu.Lock()
	defer c.writeMu.Unlock()

	if err := c.socket.WriteJSON(outEnvelope{
		ID:      id,
		Type:    typ,
		Message: message,
	}); err != nil {
		if !isCloseError(err) {
			c.socket.Close()
			log.Printf("socket.WriteJSON: %s\n", err)
		}
	}
}

func mustMarshalJson(v interface{}) string {
	bytes, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}
	return string(bytes)
}

func (c *conn) handleSubscribe(id string, subscribe *subscribeMessage) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.subscriptions[id]; ok {
		return NewSafeError("duplicate subscription")
	}

	if len(c.subscriptions)+1 > MaxSubscriptions {
		return NewSafeError("too many subscriptions")
	}

	selectionSet, err := Parse(subscribe.Query, subscribe.Variables)
	if err != nil {
		return err
	}
	if err := PrepareQuery(c.schema.Query, selectionSet); err != nil {
		return err
	}

	var previous interface{}

	e := Executor{}

	initial := true
	tags := map[string]string{"url": c.url, "query": subscribe.Query, "queryVariables": mustMarshalJson(subscribe.Variables), "id": id}

	c.subscriptions[id] = reactive.NewRerunner(context.Background(), func(ctx context.Context) (interface{}, error) {
		ctx = c.makeCtx(ctx)

		start := time.Now()
		c.logger.StartExecution(ctx, tags, initial)
		current, err := e.Execute(ctx, c.schema.Query, nil, selectionSet)
		c.logger.FinishedExecution(ctx, tags, time.Since(start))

		if err != nil {
			c.writeOrClose(id, "error", sanitizeError(err))
			go c.closeSubscription(id)
			if _, ok := err.(SanitizedError); !ok {
				c.logger.Error(ctx, err, tags)
			}
			return nil, err
		}

		delta, diff := Diff(previous, current)
		previous = current
		initial = false

		if diff {
			c.writeOrClose(id, "update", PrepareForMarshal(delta))
		}

		return nil, nil
	}, MinRerunInterval)

	return nil
}

func (c *conn) handleMutate(id string, mutate *mutateMessage) error {
	// TODO: deduplicate code
	c.mu.Lock()
	defer c.mu.Unlock()

	selectionSet, err := Parse(mutate.Query, mutate.Variables)
	if err != nil {
		return err
	}
	if err := PrepareQuery(c.schema.Mutation, selectionSet); err != nil {
		return err
	}

	e := Executor{}

	tags := map[string]string{"url": c.url, "query": mutate.Query, "queryVariables": mustMarshalJson(mutate.Variables), "id": id}

	c.subscriptions[id] = reactive.NewRerunner(context.Background(), func(ctx context.Context) (interface{}, error) {
		ctx = c.makeCtx(context.Background())

		start := time.Now()
		c.logger.StartExecution(ctx, tags, true)
		current, err := e.Execute(ctx, c.schema.Mutation, c.schema.Mutation, selectionSet)
		c.logger.FinishedExecution(ctx, tags, time.Since(start))

		if err != nil {
			c.writeOrClose(id, "error", sanitizeError(err))
			go c.closeSubscription(id)
			if _, ok := err.(SanitizedError); !ok {
				c.logger.Error(ctx, err, tags)
			}
			return nil, err
		}

		delta, _ := Diff(nil, current)
		c.writeOrClose(id, "result", PrepareForMarshal(delta))

		return nil, errors.New("stop")
	}, MinRerunInterval)

	return nil
}

func (c *conn) closeSubscription(id string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if runner, ok := c.subscriptions[id]; ok {
		runner.Stop()
		delete(c.subscriptions, id)
	}
}

func (c *conn) closeSubscriptions() {
	c.mu.Lock()
	defer c.mu.Unlock()

	for id, runner := range c.subscriptions {
		runner.Stop()
		delete(c.subscriptions, id)
	}
}

func (c *conn) handle(e *inEnvelope) error {
	switch e.Type {
	case "subscribe":
		var subscribe subscribeMessage
		if err := json.Unmarshal(e.Message, &subscribe); err != nil {
			return err
		}
		return c.handleSubscribe(e.ID, &subscribe)

	case "unsubscribe":
		c.closeSubscription(e.ID)
		return nil

	case "mutate":
		var mutate mutateMessage
		if err := json.Unmarshal(e.Message, &mutate); err != nil {
			return err
		}
		return c.handleMutate(e.ID, &mutate)

	case "echo":
		c.writeOrClose(e.ID, "echo", nil)
		return nil

	case "url":
		var url string
		if err := json.Unmarshal(e.Message, &url); err != nil {
			return err
		}
		c.url = url
		return nil

	default:
		return NewSafeError("unknown message type")
	}
}

type simpleLogger struct {
}

func (s *simpleLogger) StartExecution(ctx context.Context, tags map[string]string, initial bool) {
}
func (s *simpleLogger) FinishedExecution(ctx context.Context, tags map[string]string, delay time.Duration) {
}
func (s *simpleLogger) Error(ctx context.Context, err error, tags map[string]string) {
	log.Printf("error:%v\n%s", tags, err)
}

func Handler(schema *Schema) http.Handler {
	upgrader := &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		socket, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Printf("upgrader.Upgrade: %v", err)
			return
		}
		defer socket.Close()

		makeCtx := func(ctx context.Context) context.Context {
			return ctx
		}

		ServeJSONSocket(socket, schema, makeCtx, &simpleLogger{})
	})
}

func ServeJSONSocket(socket JSONSocket, schema *Schema, makeCtx MakeCtxFunc, logger GraphqlLogger) {
	c := &conn{
		socket: socket,

		schema:  schema,
		makeCtx: makeCtx,
		logger:  logger,

		subscriptions: make(map[string]*reactive.Rerunner),
	}

	defer c.closeSubscriptions()

	for {
		var envelope inEnvelope
		if err := c.socket.ReadJSON(&envelope); err != nil {
			if !isCloseError(err) {
				log.Println("socket.ReadJSON:", err)
			}
			return
		}

		if err := c.handle(&envelope); err != nil {
			log.Println("c.handle:", err)
			c.writeOrClose(envelope.ID, "error", sanitizeError(err))
		}
	}
}
<|endoftext|>


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

package api

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/golang/glog"
	info "github.com/google/cadvisor/info/v1"
	"github.com/google/cadvisor/info/v2"
	"github.com/google/cadvisor/manager"
)

const (
	containersApi    = "containers"
	subcontainersApi = "subcontainers"
	machineApi       = "machine"
	dockerApi        = "docker"
	summaryApi       = "summary"
	statsApi         = "stats"
	specApi          = "spec"
	eventsApi        = "events"
	storageApi       = "storage"
	attributesApi    = "attributes"
	versionApi       = "version"
)

// Interface for a cAdvisor API version
type ApiVersion interface {
	// Returns the version string.
	Version() string

	// List of supported API endpoints.
	SupportedRequestTypes() []string

	// Handles a request. The second argument is the parameters after /api/<version>/<endpoint>
	HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error
}

// Gets all supported API versions.
func getApiVersions() []ApiVersion {
	v1_0 := &version1_0{}
	v1_1 := newVersion1_1(v1_0)
	v1_2 := newVersion1_2(v1_1)
	v1_3 := newVersion1_3(v1_2)
	v2_0 := newVersion2_0(v1_3)

	return []ApiVersion{v1_0, v1_1, v1_2, v1_3, v2_0}

}

// API v1.0

type version1_0 struct {
}

func (self *version1_0) Version() string {
	return "v1.0"
}

func (self *version1_0) SupportedRequestTypes() []string {
	return []string{containersApi, machineApi}
}

func (self *version1_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case machineApi:
		glog.V(2).Infof("Api - Machine")

		// Get the MachineInfo
		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}

		err = writeResult(machineInfo, w)
		if err != nil {
			return err
		}
	case containersApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Container(%s)", containerName)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		// Get the container.
		cont, err := m.GetContainerInfo(containerName, query)
		if err != nil {
			return fmt.Errorf("failed to get container %q with error: %s", containerName, err)
		}

		// Only output the container as JSON.
		err = writeResult(cont, w)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf("unknown request type %q", requestType)
	}
	return nil
}

// API v1.1

type version1_1 struct {
	baseVersion *version1_0
}

// v1.1 builds on v1.0.
func newVersion1_1(v *version1_0) *version1_1 {
	return &version1_1{
		baseVersion: v,
	}
}

func (self *version1_1) Version() string {
	return "v1.1"
}

func (self *version1_1) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), subcontainersApi)
}

func (self *version1_1) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case subcontainersApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Subcontainers(%s)", containerName)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		// Get the subcontainers.
		containers, err := m.SubcontainersInfo(containerName, query)
		if err != nil {
			return fmt.Errorf("failed to get subcontainers for container %q with error: %s", containerName, err)
		}

		// Only output the containers as JSON.
		err = writeResult(containers, w)
		if err != nil {
			return err
		}
		return nil
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v1.2

type version1_2 struct {
	baseVersion *version1_1
}

// v1.2 builds on v1.1.
func newVersion1_2(v *version1_1) *version1_2 {
	return &version1_2{
		baseVersion: v,
	}
}

func (self *version1_2) Version() string {
	return "v1.2"
}

func (self *version1_2) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), dockerApi)
}

func (self *version1_2) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case dockerApi:
		glog.V(2).Infof("Api - Docker(%v)", request)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		var containers map[string]info.ContainerInfo
		// map requests for "docker/" to "docker"
		if len(request) == 1 && len(request[0]) == 0 {
			request = request[:0]
		}
		switch len(request) {
		case 0:
			// Get all Docker containers.
			containers, err = m.AllDockerContainers(query)
			if err != nil {
				return fmt.Errorf("failed to get all Docker containers with error: %v", err)
			}
		case 1:
			// Get one Docker container.
			var cont info.ContainerInfo
			cont, err = m.DockerContainer(request[0], query)
			if err != nil {
				return fmt.Errorf("failed to get Docker container %q with error: %v", request[0], err)
			}
			containers = map[string]info.ContainerInfo{
				cont.Name: cont,
			}
		default:
			return fmt.Errorf("unknown request for Docker container %v", request)
		}

		// Only output the containers as JSON.
		err = writeResult(containers, w)
		if err != nil {
			return err
		}
		return nil
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v1.3

type version1_3 struct {
	baseVersion *version1_2
}

// v1.3 builds on v1.2.
func newVersion1_3(v *version1_2) *version1_3 {
	return &version1_3{
		baseVersion: v,
	}
}

func (self *version1_3) Version() string {
	return "v1.3"
}

func (self *version1_3) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), eventsApi)
}

func (self *version1_3) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case eventsApi:
		query, eventsFromAllTime, err := getEventRequest(r)
		if err != nil {
			return err
		}
		glog.V(2).Infof("Api - Events(%v)", query)
		if eventsFromAllTime {
			pastEvents, err := m.GetPastEvents(query)
			if err != nil {
				return err
			}
			return writeResult(pastEvents, w)
		}
		eventChannel, err := m.WatchForEvents(query)
		if err != nil {
			return err
		}
		return streamResults(eventChannel, w, r, m)
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v2.0

// v2.0 builds on v1.3
type version2_0 struct {
	baseVersion *version1_3
}

func newVersion2_0(v *version1_3) *version2_0 {
	return &version2_0{
		baseVersion: v,
	}
}

func (self *version2_0) Version() string {
	return "v2.0"
}

func (self *version2_0) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), summaryApi)
}

func (self *version2_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	opt, err := getRequestOptions(r)
	if err != nil {
		return err
	}
	switch requestType {
	case versionApi:
		glog.V(2).Infof("Api - Version")
		versionInfo, err := m.GetVersionInfo()
		if err != nil {
			return err
		}
		return writeResult(versionInfo.CadvisorVersion, w)
	case attributesApi:
		glog.V(2).Info("Api - Attributes")

		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}
		versionInfo, err := m.GetVersionInfo()
		if err != nil {
			return err
		}
		info := v2.GetAttributes(machineInfo, versionInfo)
		return writeResult(info, w)
	case machineApi:
		glog.V(2).Info("Api - Machine")

		// TODO(rjnagal): Move machineInfo from v1.
		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}
		return writeResult(machineInfo, w)
	case summaryApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Summary for container %q, options %+v", containerName, opt)

		stats, err := m.GetDerivedStats(containerName, opt)
		if err != nil {
			return err
		}
		return writeResult(stats, w)
	case statsApi:
		name := getContainerName(request)
		glog.V(2).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt)
		conts, err := m.GetRequestedContainersInfo(name, opt)
		if err != nil {
			return err
		}
		contStats := make(map[string][]v2.ContainerStats, 0)
		for name, cont := range conts {
			contStats[name] = convertStats(cont)
		}
		return writeResult(contStats, w)
	case specApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Spec for container %q, options %+v", containerName, opt)
		specs, err := m.GetContainerSpec(containerName, opt)
		if err != nil {
			return err
		}
		return writeResult(specs, w)
	case storageApi:
		var err error
		fi := []v2.FsInfo{}
		label := r.URL.Query().Get("label")
		if len(label) == 0 {
			// Get all global filesystems info.
			fi, err = m.GetFsInfo("")
			if err != nil {
				return err
			}
		} else {
			// Get a specific label.
			fi, err = m.GetFsInfo(label)
			if err != nil {
				return err
			}
		}
		return writeResult(fi, w)
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

func convertStats(cont *info.ContainerInfo) []v2.ContainerStats {
	stats := []v2.ContainerStats{}
	for _, val := range cont.Stats {
		stat := v2.ContainerStats{
			Timestamp:     val.Timestamp,
			HasCpu:        cont.Spec.HasCpu,
			HasMemory:     cont.Spec.HasMemory,
			HasNetwork:    cont.Spec.HasNetwork,
			HasFilesystem: cont.Spec.HasFilesystem,
			HasDiskIo:     cont.Spec.HasDiskIo,
		}
		if stat.HasCpu {
			stat.Cpu = val.Cpu
		}
		if stat.HasMemory {
			stat.Memory = val.Memory
		}
		if stat.HasNetwork {
			// TODO(rjnagal): Return stats about all network interfaces.
			stat.Network = append(stat.Network, val.Network)
		}
		if stat.HasFilesystem {
			stat.Filesystem = val.Filesystem
		}
		if stat.HasDiskIo {
			stat.DiskIo = val.DiskIo
		}
		// TODO(rjnagal): Handle load stats.
		stats = append(stats, stat)
	}
	return stats
}

func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
	supportedTypes := map[string]bool{
		v2.TypeName:   true,
		v2.TypeDocker: true,
	}
	// fill in the defaults.
	opt := v2.RequestOptions{
		IdType:    v2.TypeName,
		Count:     64,
		Recursive: false,
	}
	idType := r.URL.Query().Get("type")
	if len(idType) != 0 {
		if !supportedTypes[idType] {
			return opt, fmt.Errorf("unknown 'type' %q", idType)
		}
		opt.IdType = idType
	}
	count := r.URL.Query().Get("count")
	if len(count) != 0 {
		n, err := strconv.ParseUint(count, 10, 32)
		if err != nil {
			return opt, fmt.Errorf("failed to parse 'count' option: %v", count)
		}
		opt.Count = int(n)
	}
	recursive := r.URL.Query().Get("recursive")
	if recursive == "true" {
		opt.Recursive = true
	}
	return opt, nil
}
<commit_msg>v2.0 API does not depend on v1.x.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/golang/glog"
	info "github.com/google/cadvisor/info/v1"
	"github.com/google/cadvisor/info/v2"
	"github.com/google/cadvisor/manager"
)

const (
	containersApi    = "containers"
	subcontainersApi = "subcontainers"
	machineApi       = "machine"
	dockerApi        = "docker"
	summaryApi       = "summary"
	statsApi         = "stats"
	specApi          = "spec"
	eventsApi        = "events"
	storageApi       = "storage"
	attributesApi    = "attributes"
	versionApi       = "version"
)

// Interface for a cAdvisor API version
type ApiVersion interface {
	// Returns the version string.
	Version() string

	// List of supported API endpoints.
	SupportedRequestTypes() []string

	// Handles a request. The second argument is the parameters after /api/<version>/<endpoint>
	HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error
}

// Gets all supported API versions.
func getApiVersions() []ApiVersion {
	v1_0 := &version1_0{}
	v1_1 := newVersion1_1(v1_0)
	v1_2 := newVersion1_2(v1_1)
	v1_3 := newVersion1_3(v1_2)
	v2_0 := newVersion2_0()

	return []ApiVersion{v1_0, v1_1, v1_2, v1_3, v2_0}

}

// API v1.0

type version1_0 struct {
}

func (self *version1_0) Version() string {
	return "v1.0"
}

func (self *version1_0) SupportedRequestTypes() []string {
	return []string{containersApi, machineApi}
}

func (self *version1_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case machineApi:
		glog.V(2).Infof("Api - Machine")

		// Get the MachineInfo
		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}

		err = writeResult(machineInfo, w)
		if err != nil {
			return err
		}
	case containersApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Container(%s)", containerName)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		// Get the container.
		cont, err := m.GetContainerInfo(containerName, query)
		if err != nil {
			return fmt.Errorf("failed to get container %q with error: %s", containerName, err)
		}

		// Only output the container as JSON.
		err = writeResult(cont, w)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf("unknown request type %q", requestType)
	}
	return nil
}

// API v1.1

type version1_1 struct {
	baseVersion *version1_0
}

// v1.1 builds on v1.0.
func newVersion1_1(v *version1_0) *version1_1 {
	return &version1_1{
		baseVersion: v,
	}
}

func (self *version1_1) Version() string {
	return "v1.1"
}

func (self *version1_1) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), subcontainersApi)
}

func (self *version1_1) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case subcontainersApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Subcontainers(%s)", containerName)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		// Get the subcontainers.
		containers, err := m.SubcontainersInfo(containerName, query)
		if err != nil {
			return fmt.Errorf("failed to get subcontainers for container %q with error: %s", containerName, err)
		}

		// Only output the containers as JSON.
		err = writeResult(containers, w)
		if err != nil {
			return err
		}
		return nil
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v1.2

type version1_2 struct {
	baseVersion *version1_1
}

// v1.2 builds on v1.1.
func newVersion1_2(v *version1_1) *version1_2 {
	return &version1_2{
		baseVersion: v,
	}
}

func (self *version1_2) Version() string {
	return "v1.2"
}

func (self *version1_2) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), dockerApi)
}

func (self *version1_2) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case dockerApi:
		glog.V(2).Infof("Api - Docker(%v)", request)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		var containers map[string]info.ContainerInfo
		// map requests for "docker/" to "docker"
		if len(request) == 1 && len(request[0]) == 0 {
			request = request[:0]
		}
		switch len(request) {
		case 0:
			// Get all Docker containers.
			containers, err = m.AllDockerContainers(query)
			if err != nil {
				return fmt.Errorf("failed to get all Docker containers with error: %v", err)
			}
		case 1:
			// Get one Docker container.
			var cont info.ContainerInfo
			cont, err = m.DockerContainer(request[0], query)
			if err != nil {
				return fmt.Errorf("failed to get Docker container %q with error: %v", request[0], err)
			}
			containers = map[string]info.ContainerInfo{
				cont.Name: cont,
			}
		default:
			return fmt.Errorf("unknown request for Docker container %v", request)
		}

		// Only output the containers as JSON.
		err = writeResult(containers, w)
		if err != nil {
			return err
		}
		return nil
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v1.3

type version1_3 struct {
	baseVersion *version1_2
}

// v1.3 builds on v1.2.
func newVersion1_3(v *version1_2) *version1_3 {
	return &version1_3{
		baseVersion: v,
	}
}

func (self *version1_3) Version() string {
	return "v1.3"
}

func (self *version1_3) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), eventsApi)
}

func (self *version1_3) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case eventsApi:
		query, eventsFromAllTime, err := getEventRequest(r)
		if err != nil {
			return err
		}
		glog.V(2).Infof("Api - Events(%v)", query)
		if eventsFromAllTime {
			pastEvents, err := m.GetPastEvents(query)
			if err != nil {
				return err
			}
			return writeResult(pastEvents, w)
		}
		eventChannel, err := m.WatchForEvents(query)
		if err != nil {
			return err
		}
		return streamResults(eventChannel, w, r, m)
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v2.0

// v2.0 builds on v1.3
type version2_0 struct {
}

func newVersion2_0() *version2_0 {
	return &version2_0{}
}

func (self *version2_0) Version() string {
	return "v2.0"
}

func (self *version2_0) SupportedRequestTypes() []string {
	return []string{versionApi, attributesApi, machineApi, summaryApi, statsApi, specApi, storageApi}
}

func (self *version2_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	opt, err := getRequestOptions(r)
	if err != nil {
		return err
	}
	switch requestType {
	case versionApi:
		glog.V(2).Infof("Api - Version")
		versionInfo, err := m.GetVersionInfo()
		if err != nil {
			return err
		}
		return writeResult(versionInfo.CadvisorVersion, w)
	case attributesApi:
		glog.V(2).Info("Api - Attributes")

		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}
		versionInfo, err := m.GetVersionInfo()
		if err != nil {
			return err
		}
		info := v2.GetAttributes(machineInfo, versionInfo)
		return writeResult(info, w)
	case machineApi:
		glog.V(2).Info("Api - Machine")

		// TODO(rjnagal): Move machineInfo from v1.
		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}
		return writeResult(machineInfo, w)
	case summaryApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Summary for container %q, options %+v", containerName, opt)

		stats, err := m.GetDerivedStats(containerName, opt)
		if err != nil {
			return err
		}
		return writeResult(stats, w)
	case statsApi:
		name := getContainerName(request)
		glog.V(2).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt)
		conts, err := m.GetRequestedContainersInfo(name, opt)
		if err != nil {
			return err
		}
		contStats := make(map[string][]v2.ContainerStats, 0)
		for name, cont := range conts {
			contStats[name] = convertStats(cont)
		}
		return writeResult(contStats, w)
	case specApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Spec for container %q, options %+v", containerName, opt)
		specs, err := m.GetContainerSpec(containerName, opt)
		if err != nil {
			return err
		}
		return writeResult(specs, w)
	case storageApi:
		var err error
		fi := []v2.FsInfo{}
		label := r.URL.Query().Get("label")
		if len(label) == 0 {
			// Get all global filesystems info.
			fi, err = m.GetFsInfo("")
			if err != nil {
				return err
			}
		} else {
			// Get a specific label.
			fi, err = m.GetFsInfo(label)
			if err != nil {
				return err
			}
		}
		return writeResult(fi, w)
	default:
		return fmt.Errorf("unknown request type %q", requestType)
	}
}

func convertStats(cont *info.ContainerInfo) []v2.ContainerStats {
	stats := []v2.ContainerStats{}
	for _, val := range cont.Stats {
		stat := v2.ContainerStats{
			Timestamp:     val.Timestamp,
			HasCpu:        cont.Spec.HasCpu,
			HasMemory:     cont.Spec.HasMemory,
			HasNetwork:    cont.Spec.HasNetwork,
			HasFilesystem: cont.Spec.HasFilesystem,
			HasDiskIo:     cont.Spec.HasDiskIo,
		}
		if stat.HasCpu {
			stat.Cpu = val.Cpu
		}
		if stat.HasMemory {
			stat.Memory = val.Memory
		}
		if stat.HasNetwork {
			// TODO(rjnagal): Return stats about all network interfaces.
			stat.Network = append(stat.Network, val.Network)
		}
		if stat.HasFilesystem {
			stat.Filesystem = val.Filesystem
		}
		if stat.HasDiskIo {
			stat.DiskIo = val.DiskIo
		}
		// TODO(rjnagal): Handle load stats.
		stats = append(stats, stat)
	}
	return stats
}

func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
	supportedTypes := map[string]bool{
		v2.TypeName:   true,
		v2.TypeDocker: true,
	}
	// fill in the defaults.
	opt := v2.RequestOptions{
		IdType:    v2.TypeName,
		Count:     64,
		Recursive: false,
	}
	idType := r.URL.Query().Get("type")
	if len(idType) != 0 {
		if !supportedTypes[idType] {
			return opt, fmt.Errorf("unknown 'type' %q", idType)
		}
		opt.IdType = idType
	}
	count := r.URL.Query().Get("count")
	if len(count) != 0 {
		n, err := strconv.ParseUint(count, 10, 32)
		if err != nil {
			return opt, fmt.Errorf("failed to parse 'count' option: %v", count)
		}
		opt.Count = int(n)
	}
	recursive := r.URL.Query().Get("recursive")
	if recursive == "true" {
		opt.Recursive = true
	}
	return opt, nil
}
<|endoftext|>


<commit_before>// https://leetcode.com/problems/reverse-words-in-a-string/

package leetcode

/*
void reverse(char* begin, char* end) {
    while (begin < end) {
        char c = *begin;
        *begin = *end;
        *end = c;
        ++begin;
        --end;
    }
}

void reverseWords(char *s) {
    if (s == 0) return;

    int begin = 0, end = 0;
    do {
        begin = end;
        while (s[begin] == ' ') ++begin;
        end = begin;
        while (s[end] != '\0' && s[end] != ' ') ++end;
        reverse(s + begin, s + end - 1);
    } while (begin < end);

    reverse(s, s + end - 1);

    int i = 0, j = 0;
    while (i < end) {
        while (i < end && s[i] == ' ') i++;
        while (i < end && s[i] != ' ') s[j++] = s[i++];
        s[j++] = ' ';
    }
    while (--j >= 0 && (s[j] == '\0' || s[j] == ' '));
    s[j + 1] = '\0';
}
*/
import "C"

func reverseWords(s string) string {
	cs := C.CString(s)
	C.reverseWords(cs)
	return C.GoString(cs)
}
<commit_msg>Reverse Words in a String<commit_after>// https://leetcode.com/problems/reverse-words-in-a-string/

package leetcode

/*
#include <stdlib.h>

void reverse(char* begin, char* end) {
    while (begin < end) {
        char c = *begin;
        *begin = *end;
        *end = c;
        ++begin;
        --end;
    }
}

void reverseWords(char *s) {
    if (s == 0) return;

    int begin = 0, end = 0;
    do {
        begin = end;
        while (s[begin] == ' ') ++begin;
        end = begin;
        while (s[end] != '\0' && s[end] != ' ') ++end;
        reverse(s + begin, s + end - 1);
    } while (begin < end);

    reverse(s, s + end - 1);

    int i = 0, j = 0;
    while (i < end) {
        while (i < end && s[i] == ' ') i++;
        while (i < end && s[i] != ' ') s[j++] = s[i++];
        s[j++] = ' ';
    }
    while (--j >= 0 && (s[j] == '\0' || s[j] == ' '));
    s[j + 1] = '\0';
}
*/
import "C"
import "unsafe"

func reverseWords(s string) string {
	cs := C.CString(s)
    defer C.free(unsafe.Pointer(cs))
	C.reverseWords(cs)
	return C.GoString(cs)
}
<|endoftext|>


<commit_before>package master

import (
	"encoding/json"
	"log"
	"math"
	"math/rand"
	"net"
	"net/http"
	"os"
	"os/signal"
	"time"

	"github.com/aaronang/cong-the-ripper/lib"
	"github.com/aws/aws-sdk-go/service/ec2"
)

type Master struct {
	ip                string
	port              string
	svc               *ec2.EC2 // safe to be used concurrently
	instances         map[string]slave
	jobs              map[int]*job
	jobsChan          chan lib.Job
	heartbeatChan     chan heartbeat
	heartbeatMissChan chan string // represents a key to instances
	statusChan        chan chan StatusJSON
	newTasks          []*lib.Task
	scheduledTasks    []*lib.Task
	scheduleTicker    *time.Ticker // channel to instruct the main loop to schedule tasks
	controllerTicker  *time.Ticker
	controller        controller
	taskSize          int
	quit              chan bool
}

type slave struct {
	tasks         []*lib.Task
	maxSlots      int
	heartbeatChan chan<- bool
}

type controller struct {
	dt       time.Duration
	kp       float64
	kd       float64
	ki       float64
	prevErr  float64
	integral float64
}

type heartbeat struct {
	lib.Heartbeat
	addr string
}

// Init creates the master object
func Init(port, ip string) Master {
	// set some defaults
	return Master{
		ip:                ip,
		port:              port,
		svc:               nil, // initialised in Run
		instances:         make(map[string]slave),
		jobs:              make(map[int]*job),
		jobsChan:          make(chan lib.Job),
		heartbeatChan:     make(chan heartbeat),
		heartbeatMissChan: make(chan string),
		statusChan:        make(chan chan StatusJSON),
		newTasks:          make([]*lib.Task, 0),
		scheduledTasks:    make([]*lib.Task, 0),
		scheduleTicker:    nil, // initialised in Run
		controllerTicker:  nil, //initialised in Run
		controller: controller{
			dt:       time.Minute * 2,
			kp:       1,
			kd:       0,
			ki:       0,
			prevErr:  0,
			integral: 0,
		},
		taskSize: 6400 * 1000 * 1000,
		quit:     make(chan bool),
	}
}

// Run starts the master
func (m *Master) Run() {
	// initialise the nils
	m.svc = newEC2()
	m.controllerTicker = time.NewTicker(m.controller.dt)
	// TODO test how this performs when a lot of tasks get submitted.
	m.scheduleTicker = time.NewTicker(time.Duration(100/(len(m.newTasks)+1)) * time.Millisecond)

	// setup and run http
	go func() {
		http.HandleFunc(lib.JobsCreatePath, makeJobsHandler(m.jobsChan))
		http.HandleFunc(lib.HeartbeatPath, makeHeartbeatHandler(m.heartbeatChan))
		http.HandleFunc(lib.StatusPath, makeStatusHandler(m.statusChan))
		log.Println("Running master on port", m.port)
		e := http.ListenAndServe(":"+m.port, nil)
		if e != nil {
			log.Fatalln(e)
		}
	}()

	// send message to m.quit on interrupt
	go func() {
		sigchan := make(chan os.Signal, 10)
		signal.Notify(sigchan, os.Interrupt)
		<-sigchan
		m.quit <- true
	}()

	// main loop
	for {
		select {
		case <-m.controllerTicker.C:
			// run one iteration of the controller
			m.runController()
		case <-m.scheduleTicker.C:
			// we shedule the tasks when something is in this channel
			// (controller runs in the background and manages the number of instances)
			// call load balancer function to schedule the tasks
			// move tasks from `newTasks` to `scheduledTasks`
			if slaveIP := m.slaveAvailable(); slaveIP != "" {
				if tidx := m.getTaskToSchedule(); tidx != -1 {
					m.scheduleTask(tidx, slaveIP)
				}
			}
		case j := <-m.jobsChan:
			// split the job into tasks
			newJob := job{
				Job:          j,
				id:           rand.Int(),
				runningTasks: 0,
				maxTasks:     0,
			}
			newJob.splitJob(m.taskSize)

			// update `jobs` and `newTasks`
			m.jobs[newJob.id] = &newJob
			for i := range newJob.tasks {
				m.newTasks = append(m.newTasks, newJob.tasks[i])
			}
		case beat := <-m.heartbeatChan:
			// update task statuses
			// check whether a job has completed all its tasks
			m.instances[beat.addr].heartbeatChan <- true
			m.updateOnHeartbeat(beat)
		case addr := <-m.heartbeatMissChan:
			// moved the scheduled tasks back to new tasks to be re-scheduled
			for i := range m.instances[addr].tasks {
				task := m.instances[addr].tasks[i]
				m.scheduledTasks = removeTaskFrom(m.scheduledTasks, task.JobID, task.ID)
				m.newTasks = append([]*lib.Task{task}, m.newTasks...)
			}
			delete(m.instances, addr)
		case s := <-m.statusChan:
			// status handler gives us a channel,
			// we write the status into the channel and the handler serves the result
			s <- createStatusJSON(m)
		case <-m.quit:
			// release all slaves
			log.Println("Master stopping...")
			instances := instancesFromIPs(m.svc, mapToKeys(m.instances))
			_, err := terminateSlaves(m.svc, instances)
			if err != nil {
				log.Fatalln("Failed to terminate slaves on interrupt", err)
			}
			os.Exit(0)
		}
	}
}

func makeJobsHandler(c chan lib.Job) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var j lib.Job
		decoder := json.NewDecoder(r.Body)
		if err := decoder.Decode(&j); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		c <- j
	}
}

func makeHeartbeatHandler(c chan heartbeat) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var beat lib.Heartbeat
		decoder := json.NewDecoder(r.Body)
		if err := decoder.Decode(&beat); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		c <- heartbeat{
			Heartbeat: beat,
			addr:      r.RemoteAddr,
		}
	}
}

func makeStatusHandler(c chan chan StatusJSON) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		statusChan := make(chan StatusJSON)
		c <- statusChan
		res, err := json.MarshalIndent(<-statusChan, "", "\t")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		_, err = w.Write(res)
		if err != nil {
			log.Println("Failed to write status", err)
		}
	}
}

func (m *Master) updateTask(status lib.TaskStatus, ip string) {
	for i := range m.jobs[status.JobId].tasks {
		task := m.jobs[status.JobId].tasks[i]
		if task.ID == status.Id {
			if status.Status == lib.Running {
				task.Progress = status.Progress
			} else {
				if status.Status == lib.PasswordFound {
					// TODO terminate the other tasks in the same job if a password is found
					log.Printf("Password found: %v (task: %v, job, %v)\n",
						status.Password, status.Id, status.JobId)
				}
				m.removeTask(ip, status.JobId, status.Id)

				// remove the job if it's finished
				if len(m.jobs[status.JobId].tasks) == 0 {
					delete(m.jobs, status.JobId)
					log.Printf("Job %v completed", status.JobId)
				}

			}
		}
	}
}

func removeTaskFrom(tasks []*lib.Task, jobID, taskID int) []*lib.Task {
	for i := range tasks {
		if tasks[i].ID == taskID && tasks[i].JobID == jobID {
			return append(tasks[:i], tasks[i+1:]...)
		}
	}
	return nil
}

func (m *Master) removeTask(ip string, jobID, taskID int) {
	jobsRes := removeTaskFrom(m.jobs[jobID].tasks, jobID, taskID)
	scheduledRes := removeTaskFrom(m.scheduledTasks, jobID, taskID)
	instancesRes := removeTaskFrom(m.instances[ip].tasks, jobID, taskID)

	if jobsRes != nil && scheduledRes != nil && instancesRes != nil {
		log.Printf("Removed task - job: %v, task: %v", jobID, taskID)
		m.jobs[jobID].tasks = jobsRes
		m.scheduledTasks = scheduledRes

		tmp := m.instances[ip]
		tmp.tasks = instancesRes
		m.instances[ip] = tmp
	} else {

		log.Printf("Failed to removed task - job: %v, task: %v\n", jobID, taskID)
	}
}

func (m *Master) updateOnHeartbeat(beat heartbeat) {
	if _, ok := m.instances[beat.addr]; ok { // for instances that already exist
		for _, s := range beat.TaskStatus {
			m.updateTask(s, beat.addr)
		}
	} else { // for new instances
		m.instances[beat.addr] = slave{
			tasks:         make([]*lib.Task, 0),
			maxSlots:      lib.MaxSlotsPerInstance,
			heartbeatChan: heartbeatChecker(beat.addr, m.heartbeatMissChan),
		}
		log.Printf("New instance %v created.", beat.addr)
	}
}

func heartbeatChecker(addr string, missedChan chan<- string) chan<- bool {
	beatChan := make(chan bool)
	go func() {
		for {
			timeout := time.After(2 * lib.HeartbeatInterval)
			select {
			case <-timeout:
				log.Printf("Missed heartbeat for %v", addr)
				missedChan <- addr
				return
			case <-beatChan:
				// ok, do nothing
			}
		}
	}()
	return beatChan
}

func (m *Master) getTaskToSchedule() int {
	for idx, t := range m.newTasks {
		if !m.jobs[t.JobID].reachedMaxTasks() {
			return idx
		}
	}
	return -1
}

func (m *Master) slaveAvailable() string {
	minimumTasks := math.MaxInt64
	var slaveIP string
	for ip, i := range m.instances {
		if assignedTasks := len(i.tasks); assignedTasks < minimumTasks && assignedTasks < i.maxSlots {
			minimumTasks = assignedTasks
			slaveIP = ip
		}
	}
	return slaveIP
}

func (m *Master) scheduleTask(tidx int, ip string) {
	// NOTE: if sendTask takes too long then it may block the main loop
	if _, err := sendTask(m.newTasks[tidx], net.JoinHostPort(ip, m.port)); err != nil {
		log.Println("Sending task to slave did not execute correctly.", err)
	} else {
		job := m.jobs[m.newTasks[tidx].JobID]
		job.increaseRunningTasks()
		m.scheduledTasks = append(m.scheduledTasks, m.newTasks[tidx])
		m.newTasks = append(m.newTasks[:tidx], m.newTasks[tidx+1:]...)
	}
}
<commit_msg>Print message on password not found<commit_after>package master

import (
	"encoding/json"
	"log"
	"math"
	"math/rand"
	"net"
	"net/http"
	"os"
	"os/signal"
	"time"

	"github.com/aaronang/cong-the-ripper/lib"
	"github.com/aws/aws-sdk-go/service/ec2"
)

type Master struct {
	ip                string
	port              string
	svc               *ec2.EC2 // safe to be used concurrently
	instances         map[string]slave
	jobs              map[int]*job
	jobsChan          chan lib.Job
	heartbeatChan     chan heartbeat
	heartbeatMissChan chan string // represents a key to instances
	statusChan        chan chan StatusJSON
	newTasks          []*lib.Task
	scheduledTasks    []*lib.Task
	scheduleTicker    *time.Ticker // channel to instruct the main loop to schedule tasks
	controllerTicker  *time.Ticker
	controller        controller
	taskSize          int
	quit              chan bool
}

type slave struct {
	tasks         []*lib.Task
	maxSlots      int
	heartbeatChan chan<- bool
}

type controller struct {
	dt       time.Duration
	kp       float64
	kd       float64
	ki       float64
	prevErr  float64
	integral float64
}

type heartbeat struct {
	lib.Heartbeat
	addr string
}

// Init creates the master object
func Init(port, ip string) Master {
	// set some defaults
	return Master{
		ip:                ip,
		port:              port,
		svc:               nil, // initialised in Run
		instances:         make(map[string]slave),
		jobs:              make(map[int]*job),
		jobsChan:          make(chan lib.Job),
		heartbeatChan:     make(chan heartbeat),
		heartbeatMissChan: make(chan string),
		statusChan:        make(chan chan StatusJSON),
		newTasks:          make([]*lib.Task, 0),
		scheduledTasks:    make([]*lib.Task, 0),
		scheduleTicker:    nil, // initialised in Run
		controllerTicker:  nil, //initialised in Run
		controller: controller{
			dt:       time.Minute * 2,
			kp:       1,
			kd:       0,
			ki:       0,
			prevErr:  0,
			integral: 0,
		},
		taskSize: 6400 * 1000 * 1000,
		quit:     make(chan bool),
	}
}

// Run starts the master
func (m *Master) Run() {
	// initialise the nils
	m.svc = newEC2()
	m.controllerTicker = time.NewTicker(m.controller.dt)
	// TODO test how this performs when a lot of tasks get submitted.
	m.scheduleTicker = time.NewTicker(time.Duration(100/(len(m.newTasks)+1)) * time.Millisecond)

	// setup and run http
	go func() {
		http.HandleFunc(lib.JobsCreatePath, makeJobsHandler(m.jobsChan))
		http.HandleFunc(lib.HeartbeatPath, makeHeartbeatHandler(m.heartbeatChan))
		http.HandleFunc(lib.StatusPath, makeStatusHandler(m.statusChan))
		log.Println("Running master on port", m.port)
		e := http.ListenAndServe(":"+m.port, nil)
		if e != nil {
			log.Fatalln(e)
		}
	}()

	// send message to m.quit on interrupt
	go func() {
		sigchan := make(chan os.Signal, 10)
		signal.Notify(sigchan, os.Interrupt)
		<-sigchan
		m.quit <- true
	}()

	// main loop
	for {
		select {
		case <-m.controllerTicker.C:
			// run one iteration of the controller
			m.runController()
		case <-m.scheduleTicker.C:
			// we shedule the tasks when something is in this channel
			// (controller runs in the background and manages the number of instances)
			// call load balancer function to schedule the tasks
			// move tasks from `newTasks` to `scheduledTasks`
			if slaveIP := m.slaveAvailable(); slaveIP != "" {
				if tidx := m.getTaskToSchedule(); tidx != -1 {
					m.scheduleTask(tidx, slaveIP)
				}
			}
		case j := <-m.jobsChan:
			// split the job into tasks
			newJob := job{
				Job:          j,
				id:           rand.Int(),
				runningTasks: 0,
				maxTasks:     0,
			}
			newJob.splitJob(m.taskSize)

			// update `jobs` and `newTasks`
			m.jobs[newJob.id] = &newJob
			for i := range newJob.tasks {
				m.newTasks = append(m.newTasks, newJob.tasks[i])
			}
		case beat := <-m.heartbeatChan:
			// update task statuses
			// check whether a job has completed all its tasks
			m.instances[beat.addr].heartbeatChan <- true
			m.updateOnHeartbeat(beat)
		case addr := <-m.heartbeatMissChan:
			// moved the scheduled tasks back to new tasks to be re-scheduled
			for i := range m.instances[addr].tasks {
				task := m.instances[addr].tasks[i]
				m.scheduledTasks = removeTaskFrom(m.scheduledTasks, task.JobID, task.ID)
				m.newTasks = append([]*lib.Task{task}, m.newTasks...)
			}
			delete(m.instances, addr)
		case s := <-m.statusChan:
			// status handler gives us a channel,
			// we write the status into the channel and the handler serves the result
			s <- createStatusJSON(m)
		case <-m.quit:
			// release all slaves
			log.Println("Master stopping...")
			instances := instancesFromIPs(m.svc, mapToKeys(m.instances))
			_, err := terminateSlaves(m.svc, instances)
			if err != nil {
				log.Fatalln("Failed to terminate slaves on interrupt", err)
			}
			os.Exit(0)
		}
	}
}

func makeJobsHandler(c chan lib.Job) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var j lib.Job
		decoder := json.NewDecoder(r.Body)
		if err := decoder.Decode(&j); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		c <- j
	}
}

func makeHeartbeatHandler(c chan heartbeat) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var beat lib.Heartbeat
		decoder := json.NewDecoder(r.Body)
		if err := decoder.Decode(&beat); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		c <- heartbeat{
			Heartbeat: beat,
			addr:      r.RemoteAddr,
		}
	}
}

func makeStatusHandler(c chan chan StatusJSON) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		statusChan := make(chan StatusJSON)
		c <- statusChan
		res, err := json.MarshalIndent(<-statusChan, "", "\t")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		_, err = w.Write(res)
		if err != nil {
			log.Println("Failed to write status", err)
		}
	}
}

func (m *Master) updateTask(status lib.TaskStatus, ip string) {
	for i := range m.jobs[status.JobId].tasks {
		task := m.jobs[status.JobId].tasks[i]
		if task.ID == status.Id {
			if status.Status == lib.Running {
				task.Progress = status.Progress
			} else {
				if status.Status == lib.PasswordFound {
					// TODO terminate the other tasks in the same job if a password is found
					log.Printf("Password found!!!!: %v (task: %v, job, %v)\n",
						status.Password, status.Id, status.JobId)
				} else {
					log.Printf("Password not found: %v (task: %v, job, %v)\n",
						status.Password, status.Id, status.JobId)
				}

				m.removeTask(ip, status.JobId, status.Id)

				// remove the job if it's finished
				if len(m.jobs[status.JobId].tasks) == 0 {
					delete(m.jobs, status.JobId)
					log.Printf("Job %v completed", status.JobId)
				}

			}
		}
	}
}

func removeTaskFrom(tasks []*lib.Task, jobID, taskID int) []*lib.Task {
	for i := range tasks {
		if tasks[i].ID == taskID && tasks[i].JobID == jobID {
			return append(tasks[:i], tasks[i+1:]...)
		}
	}
	return nil
}

func (m *Master) removeTask(ip string, jobID, taskID int) {
	jobsRes := removeTaskFrom(m.jobs[jobID].tasks, jobID, taskID)
	scheduledRes := removeTaskFrom(m.scheduledTasks, jobID, taskID)
	instancesRes := removeTaskFrom(m.instances[ip].tasks, jobID, taskID)

	if jobsRes != nil && scheduledRes != nil && instancesRes != nil {
		log.Printf("Removed task - job: %v, task: %v", jobID, taskID)
		m.jobs[jobID].tasks = jobsRes
		m.scheduledTasks = scheduledRes

		tmp := m.instances[ip]
		tmp.tasks = instancesRes
		m.instances[ip] = tmp
	} else {

		log.Printf("Failed to removed task - job: %v, task: %v\n", jobID, taskID)
	}
}

func (m *Master) updateOnHeartbeat(beat heartbeat) {
	if _, ok := m.instances[beat.addr]; ok { // for instances that already exist
		for _, s := range beat.TaskStatus {
			m.updateTask(s, beat.addr)
		}
	} else { // for new instances
		m.instances[beat.addr] = slave{
			tasks:         make([]*lib.Task, 0),
			maxSlots:      lib.MaxSlotsPerInstance,
			heartbeatChan: heartbeatChecker(beat.addr, m.heartbeatMissChan),
		}
		log.Printf("New instance %v created.", beat.addr)
	}
}

func heartbeatChecker(addr string, missedChan chan<- string) chan<- bool {
	beatChan := make(chan bool)
	go func() {
		for {
			timeout := time.After(2 * lib.HeartbeatInterval)
			select {
			case <-timeout:
				log.Printf("Missed heartbeat for %v", addr)
				missedChan <- addr
				return
			case <-beatChan:
				// ok, do nothing
			}
		}
	}()
	return beatChan
}

func (m *Master) getTaskToSchedule() int {
	for idx, t := range m.newTasks {
		if !m.jobs[t.JobID].reachedMaxTasks() {
			return idx
		}
	}
	return -1
}

func (m *Master) slaveAvailable() string {
	minimumTasks := math.MaxInt64
	var slaveIP string
	for ip, i := range m.instances {
		if assignedTasks := len(i.tasks); assignedTasks < minimumTasks && assignedTasks < i.maxSlots {
			minimumTasks = assignedTasks
			slaveIP = ip
		}
	}
	return slaveIP
}

func (m *Master) scheduleTask(tidx int, ip string) {
	// NOTE: if sendTask takes too long then it may block the main loop
	if _, err := sendTask(m.newTasks[tidx], net.JoinHostPort(ip, m.port)); err != nil {
		log.Println("Sending task to slave did not execute correctly.", err)
	} else {
		job := m.jobs[m.newTasks[tidx].JobID]
		job.increaseRunningTasks()
		m.scheduledTasks = append(m.scheduledTasks, m.newTasks[tidx])
		m.newTasks = append(m.newTasks[:tidx], m.newTasks[tidx+1:]...)
	}
}
<|endoftext|>


<commit_before>package app

import (
	"testing"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/ubclaunchpad/cumulus/conf"
	"github.com/ubclaunchpad/cumulus/msg"
)

func init() {
	log.SetLevel(log.DebugLevel)
}

func TestKillWorkers(t *testing.T) {
	intializeQueues()
	time.Sleep(20 * time.Millisecond)
	for i := 0; i < nWorkers; i++ {
		if workers[i] != nil {
			t.FailNow()
		}
	}
	initializeWorkers()
	time.Sleep(20 * time.Millisecond)
	for i := 0; i < nWorkers; i++ {
		if workers[i] == nil {
			t.FailNow()
		}
	}
	killWorkers()
	time.Sleep(20 * time.Millisecond)
	for i := 0; i < nWorkers; i++ {
		if workers[i] != nil {
			t.FailNow()
		}
	}
}

func TestInitializeNode(t *testing.T) {
	initializeNode()
	if tpool == nil {
		t.FailNow()
	}
	if BlockWorkQueue == nil {
		t.FailNow()
	}
	if TransactionWorkQueue == nil {
		t.FailNow()
	}
	killWorkers()
}

func TestPushHandlerNewBlock(t *testing.T) {
	intializeQueues()
	push := msg.Push{ResourceType: msg.ResourceBlock}
	PushHandler(&push)
	select {
	case _, ok := <-BlockWorkQueue:
		if !ok {
			t.FailNow()
		}
	}
	// Add more here...
}

func TestPushHandlerNewTransaction(t *testing.T) {
	intializeQueues()
	push := msg.Push{ResourceType: msg.ResourceTransaction}
	PushHandler(&push)
	select {
	case _, ok := <-TransactionWorkQueue:
		if !ok {
			t.FailNow()
		}
	}
	// Add more here...
}

func TestRequestHandlerNewBlock(t *testing.T) {
	intializeQueues()
	push := msg.Request{ResourceType: msg.ResourceBlock}
	RequestHandler(&push)
	select {
	case _, ok := <-BlockWorkQueue:
		if !ok {
			t.FailNow()
		}
	}
	// Add more here...
}

func TestRequestHandlerNewTransaction(t *testing.T) {
	intializeQueues()
	push := msg.Request{ResourceType: msg.ResourceTransaction}
	RequestHandler(&push)
	select {
	case _, ok := <-TransactionWorkQueue:
		if !ok {
			t.FailNow()
		}
	}
	// Add more here...
}

func TestRun(t *testing.T) {
	cfg := conf.Config{
		Interface: "127.0.0.1",
		Verbose:   true,
		Mine:      false,
	}
	Run(cfg)
	if tpool == nil {
		t.FailNow()
	}
}
<commit_msg>remove run test<commit_after>package app

import (
	"testing"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/ubclaunchpad/cumulus/msg"
)

func init() {
	log.SetLevel(log.DebugLevel)
}

func TestKillWorkers(t *testing.T) {
	intializeQueues()
	time.Sleep(20 * time.Millisecond)
	for i := 0; i < nWorkers; i++ {
		if workers[i] != nil {
			t.FailNow()
		}
	}
	initializeWorkers()
	time.Sleep(20 * time.Millisecond)
	for i := 0; i < nWorkers; i++ {
		if workers[i] == nil {
			t.FailNow()
		}
	}
	killWorkers()
	time.Sleep(20 * time.Millisecond)
	for i := 0; i < nWorkers; i++ {
		if workers[i] != nil {
			t.FailNow()
		}
	}
}

func TestInitializeNode(t *testing.T) {
	initializeNode()
	if tpool == nil {
		t.FailNow()
	}
	if BlockWorkQueue == nil {
		t.FailNow()
	}
	if TransactionWorkQueue == nil {
		t.FailNow()
	}
	killWorkers()
}

func TestPushHandlerNewBlock(t *testing.T) {
	intializeQueues()
	push := msg.Push{ResourceType: msg.ResourceBlock}
	PushHandler(&push)
	select {
	case _, ok := <-BlockWorkQueue:
		if !ok {
			t.FailNow()
		}
	}
	// Add more here...
}

func TestPushHandlerNewTransaction(t *testing.T) {
	intializeQueues()
	push := msg.Push{ResourceType: msg.ResourceTransaction}
	PushHandler(&push)
	select {
	case _, ok := <-TransactionWorkQueue:
		if !ok {
			t.FailNow()
		}
	}
	// Add more here...
}

func TestRequestHandlerNewBlock(t *testing.T) {
	intializeQueues()
	push := msg.Request{ResourceType: msg.ResourceBlock}
	RequestHandler(&push)
	select {
	case _, ok := <-BlockWorkQueue:
		if !ok {
			t.FailNow()
		}
	}
	// Add more here...
}

func TestRequestHandlerNewTransaction(t *testing.T) {
	intializeQueues()
	push := msg.Request{ResourceType: msg.ResourceTransaction}
	RequestHandler(&push)
	select {
	case _, ok := <-TransactionWorkQueue:
		if !ok {
			t.FailNow()
		}
	}
	// Add more here...
}
<|endoftext|>


<commit_before>package app

import (
	"gnd.la/config"
	"gnd.la/signal"
)

var (
	defaultDebug         bool
	defaultTemplateDebug bool
	defaultLanguage      string
	defaultPort          int
	defaultDatabase      *config.URL
	defaultCache         *config.URL
	defaultBlobstore     *config.URL
	defaultSecret        string
	defaultEncryptionKey string
)

// DefaultDebug returns the default value for
// App.Debug in App instances.
func DefaultDebug() bool { return defaultDebug }

// DefaultTemplateDebug returns the default value for
// App.TemplateDebug in App instances.
func DefaultTemplateDebug() bool { return defaultTemplateDebug }

// DefaultLanguage returns the default value for
// App.DefaultLanguage in App instances.
func DefaultLanguage() string { return defaultLanguage }

// DefaultPort returns the default value for
// App.Port in App instances.
func DefaultPort() int { return defaultPort }

func DefaultDatabase() *config.URL { return defaultDatabase }

func DefaultCache() *config.URL { return defaultCache }

func DefaultBlobstore() *config.URL { return defaultBlobstore }

// DefaultSecret returns the default value for
// App.Secret in App instances.
func DefaultSecret() string { return defaultSecret }

// DefaultEncryptionKey returns the default value for
// App.EncryptionKey in App instances.
func DefaultEncryptionKey() string { return defaultEncryptionKey }

func init() {
	signal.MustRegister(config.SET, func(_ string, conf interface{}) {
		defaultDebug = config.BoolValue(conf, "Debug", false)
		defaultTemplateDebug = config.BoolValue(conf, "TemplateDebug", false)
		defaultLanguage = config.StringValue(conf, "Language", "")
		defaultPort = config.IntValue(conf, "Port", 8888)
		config.PointerValue(conf, "Database", &defaultDatabase)
		config.PointerValue(conf, "Cache", &defaultCache)
		config.PointerValue(conf, "Blobstore", &defaultBlobstore)
		defaultSecret = config.StringValue(conf, "Secret", "")
		defaultEncryptionKey = config.StringValue(conf, "EncryptionKey", "")
	})
}
<commit_msg>Correctly get AppDebug rather than Debug from the config<commit_after>package app

import (
	"gnd.la/config"
	"gnd.la/signal"
)

var (
	defaultDebug         bool
	defaultTemplateDebug bool
	defaultLanguage      string
	defaultPort          int
	defaultDatabase      *config.URL
	defaultCache         *config.URL
	defaultBlobstore     *config.URL
	defaultSecret        string
	defaultEncryptionKey string
)

// DefaultDebug returns the default value for
// App.Debug in App instances.
func DefaultDebug() bool { return defaultDebug }

// DefaultTemplateDebug returns the default value for
// App.TemplateDebug in App instances.
func DefaultTemplateDebug() bool { return defaultTemplateDebug }

// DefaultLanguage returns the default value for
// App.DefaultLanguage in App instances.
func DefaultLanguage() string { return defaultLanguage }

// DefaultPort returns the default value for
// App.Port in App instances.
func DefaultPort() int { return defaultPort }

func DefaultDatabase() *config.URL { return defaultDatabase }

func DefaultCache() *config.URL { return defaultCache }

func DefaultBlobstore() *config.URL { return defaultBlobstore }

// DefaultSecret returns the default value for
// App.Secret in App instances.
func DefaultSecret() string { return defaultSecret }

// DefaultEncryptionKey returns the default value for
// App.EncryptionKey in App instances.
func DefaultEncryptionKey() string { return defaultEncryptionKey }

func init() {
	signal.MustRegister(config.SET, func(_ string, conf interface{}) {
		defaultDebug = config.BoolValue(conf, "AppDebug", false)
		defaultTemplateDebug = config.BoolValue(conf, "TemplateDebug", false)
		defaultLanguage = config.StringValue(conf, "Language", "")
		defaultPort = config.IntValue(conf, "Port", 8888)
		config.PointerValue(conf, "Database", &defaultDatabase)
		config.PointerValue(conf, "Cache", &defaultCache)
		config.PointerValue(conf, "Blobstore", &defaultBlobstore)
		defaultSecret = config.StringValue(conf, "Secret", "")
		defaultEncryptionKey = config.StringValue(conf, "EncryptionKey", "")
	})
}
<|endoftext|>


<commit_before>package password

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

	"github.com/justwatchcom/gopass/fsutil"
	"github.com/justwatchcom/gopass/tree"
)

const (
	// TemplateFile is the name of a pass template
	TemplateFile = ".pass-template"
)

// LookupTemplate will lookup and return a template
func (r *RootStore) LookupTemplate(name string) ([]byte, bool) {
	store := r.getStore(name)
	return store.LookupTemplate(strings.TrimPrefix(name, store.alias))
}

// LookupTemplate will lookup and return a template
func (s *Store) LookupTemplate(name string) ([]byte, bool) {
	// chop off one path element until we find something
	for {
		if name == "" {
			break
		}
		name = filepath.Dir(name)
		tpl := filepath.Join(s.path, name, TemplateFile)
		if fsutil.IsFile(tpl) {
			if content, err := ioutil.ReadFile(tpl); err == nil {
				return content, true
			}
		}
	}
	return []byte{}, false
}

// TemplateTree returns a tree of all templates
func (r *RootStore) TemplateTree() (*tree.Folder, error) {
	root := tree.New("gopass")
	mps := r.mountPoints()
	sort.Sort(sort.Reverse(byLen(mps)))
	for _, alias := range mps {
		substore := r.mounts[alias]
		if substore == nil {
			continue
		}
		if err := root.AddMount(alias, substore.path); err != nil {
			return nil, fmt.Errorf("failed to add mount: %s", err)
		}
		for _, t := range substore.ListTemplates(alias) {
			if err := root.AddFile(t); err != nil {
				fmt.Println(err)
			}
		}
	}

	for _, t := range r.store.ListTemplates("") {
		if err := root.AddFile(t); err != nil {
			fmt.Println(err)
		}
	}

	return root, nil
}

func mkTemplateStoreWalkerFunc(alias, folder string, fn func(...string)) func(string, os.FileInfo, error) error {
	return func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() && strings.HasPrefix(info.Name(), ".") && path != folder {
			return filepath.SkipDir
		}
		if info.IsDir() {
			return nil
		}
		if info.Name() != TemplateFile {
			return nil
		}
		if path == folder {
			return nil
		}
		if info.Mode()&os.ModeSymlink != 0 {
			return nil
		}
		s := strings.TrimPrefix(path, folder+"/")
		s = strings.TrimSuffix(s, TemplateFile)
		s = strings.TrimSuffix(s, "/")
		if s == "" {
			s = "default"
		}
		if alias != "" {
			s = alias + "/" + s
		}
		fn(s)
		return nil
	}
}

// ListTemplates will list all templates in this store
func (s *Store) ListTemplates(prefix string) []string {
	lst := make([]string, 0, 10)
	addFunc := func(in ...string) {
		lst = append(lst, in...)
	}

	if err := filepath.Walk(s.path, mkTemplateStoreWalkerFunc(prefix, s.path, addFunc)); err != nil {
		fmt.Printf("Failed to list templates: %s\n", err)
	}

	return lst
}

// templatefile returns the name of the given template on disk
func (s *Store) templatefile(name string) string {
	return filepath.Join(s.path, name, TemplateFile)
}

// HasTemplate returns true if the template exists
func (r *RootStore) HasTemplate(name string) bool {
	store := r.getStore(name)
	return store.HasTemplate(strings.TrimPrefix(name, store.alias))
}

// HasTemplate returns true if the template exists
func (s *Store) HasTemplate(name string) bool {
	return fsutil.IsFile(s.templatefile(name))
}

// GetTemplate will return the content of the named template
func (r *RootStore) GetTemplate(name string) ([]byte, error) {
	store := r.getStore(name)
	return store.GetTemplate(strings.TrimPrefix(name, store.alias))
}

// GetTemplate will return the content of the named template
func (s *Store) GetTemplate(name string) ([]byte, error) {
	return ioutil.ReadFile(s.templatefile(name))
}

// SetTemplate will (over)write the content to the template file
func (r *RootStore) SetTemplate(name string, content []byte) error {
	store := r.getStore(name)
	return store.SetTemplate(strings.TrimPrefix(name, store.alias), content)
}

// SetTemplate will (over)write the content to the template file
func (s *Store) SetTemplate(name string, content []byte) error {
	return ioutil.WriteFile(s.templatefile(name), content, 0600)
}

// RemoveTemplate will delete the named template if it exists
func (r *RootStore) RemoveTemplate(name string) error {
	store := r.getStore(name)
	return store.RemoveTemplate(strings.TrimPrefix(name, store.alias))
}

// RemoveTemplate will delete the named template if it exists
func (s *Store) RemoveTemplate(name string) error {
	t := s.templatefile(name)
	if !fsutil.IsFile(t) {
		return fmt.Errorf("template not found")
	}

	return os.Remove(t)
}
<commit_msg>Fix LookupTemplate infinite recursion (#118)<commit_after>package password

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

	"github.com/justwatchcom/gopass/fsutil"
	"github.com/justwatchcom/gopass/tree"
)

const (
	// TemplateFile is the name of a pass template
	TemplateFile = ".pass-template"
)

// LookupTemplate will lookup and return a template
func (r *RootStore) LookupTemplate(name string) ([]byte, bool) {
	store := r.getStore(name)
	return store.LookupTemplate(strings.TrimPrefix(name, store.alias))
}

// LookupTemplate will lookup and return a template
func (s *Store) LookupTemplate(name string) ([]byte, bool) {
	// chop off one path element until we find something
	for {
		if name == "" || name == "/" {
			break
		}
		name = filepath.Dir(name)
		tpl := filepath.Join(s.path, name, TemplateFile)
		if fsutil.IsFile(tpl) {
			if content, err := ioutil.ReadFile(tpl); err == nil {
				return content, true
			}
		}
	}
	return []byte{}, false
}

// TemplateTree returns a tree of all templates
func (r *RootStore) TemplateTree() (*tree.Folder, error) {
	root := tree.New("gopass")
	mps := r.mountPoints()
	sort.Sort(sort.Reverse(byLen(mps)))
	for _, alias := range mps {
		substore := r.mounts[alias]
		if substore == nil {
			continue
		}
		if err := root.AddMount(alias, substore.path); err != nil {
			return nil, fmt.Errorf("failed to add mount: %s", err)
		}
		for _, t := range substore.ListTemplates(alias) {
			if err := root.AddFile(t); err != nil {
				fmt.Println(err)
			}
		}
	}

	for _, t := range r.store.ListTemplates("") {
		if err := root.AddFile(t); err != nil {
			fmt.Println(err)
		}
	}

	return root, nil
}

func mkTemplateStoreWalkerFunc(alias, folder string, fn func(...string)) func(string, os.FileInfo, error) error {
	return func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() && strings.HasPrefix(info.Name(), ".") && path != folder {
			return filepath.SkipDir
		}
		if info.IsDir() {
			return nil
		}
		if info.Name() != TemplateFile {
			return nil
		}
		if path == folder {
			return nil
		}
		if info.Mode()&os.ModeSymlink != 0 {
			return nil
		}
		s := strings.TrimPrefix(path, folder+"/")
		s = strings.TrimSuffix(s, TemplateFile)
		s = strings.TrimSuffix(s, "/")
		if s == "" {
			s = "default"
		}
		if alias != "" {
			s = alias + "/" + s
		}
		fn(s)
		return nil
	}
}

// ListTemplates will list all templates in this store
func (s *Store) ListTemplates(prefix string) []string {
	lst := make([]string, 0, 10)
	addFunc := func(in ...string) {
		lst = append(lst, in...)
	}

	if err := filepath.Walk(s.path, mkTemplateStoreWalkerFunc(prefix, s.path, addFunc)); err != nil {
		fmt.Printf("Failed to list templates: %s\n", err)
	}

	return lst
}

// templatefile returns the name of the given template on disk
func (s *Store) templatefile(name string) string {
	return filepath.Join(s.path, name, TemplateFile)
}

// HasTemplate returns true if the template exists
func (r *RootStore) HasTemplate(name string) bool {
	store := r.getStore(name)
	return store.HasTemplate(strings.TrimPrefix(name, store.alias))
}

// HasTemplate returns true if the template exists
func (s *Store) HasTemplate(name string) bool {
	return fsutil.IsFile(s.templatefile(name))
}

// GetTemplate will return the content of the named template
func (r *RootStore) GetTemplate(name string) ([]byte, error) {
	store := r.getStore(name)
	return store.GetTemplate(strings.TrimPrefix(name, store.alias))
}

// GetTemplate will return the content of the named template
func (s *Store) GetTemplate(name string) ([]byte, error) {
	return ioutil.ReadFile(s.templatefile(name))
}

// SetTemplate will (over)write the content to the template file
func (r *RootStore) SetTemplate(name string, content []byte) error {
	store := r.getStore(name)
	return store.SetTemplate(strings.TrimPrefix(name, store.alias), content)
}

// SetTemplate will (over)write the content to the template file
func (s *Store) SetTemplate(name string, content []byte) error {
	return ioutil.WriteFile(s.templatefile(name), content, 0600)
}

// RemoveTemplate will delete the named template if it exists
func (r *RootStore) RemoveTemplate(name string) error {
	store := r.getStore(name)
	return store.RemoveTemplate(strings.TrimPrefix(name, store.alias))
}

// RemoveTemplate will delete the named template if it exists
func (s *Store) RemoveTemplate(name string) error {
	t := s.templatefile(name)
	if !fsutil.IsFile(t) {
		return fmt.Errorf("template not found")
	}

	return os.Remove(t)
}
<|endoftext|>


<commit_before>package auth

import (
	// Go Builtin Packages
	"bytes"
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"

	// The Gorilla Web Toolkit
	"github.com/gorilla/mux"

	// Local Packages
	"github.com/adayoung/gae-pastebin/pastebin/models"
	"github.com/adayoung/gae-pastebin/pastebin/utils"
)

func InitRoutes(r *mux.Router) {
	r.HandleFunc("/auth/gdrive/start", utils.ExtraSugar(authGDriveStart)).Methods("GET").Name("authGDriveStart")
	r.HandleFunc("/auth/gdrive/finish", utils.ExtraSugar(authGDriveFinish)).Methods("GET").Name("authGDriveFinish")

	r.HandleFunc("/auth/login", utils.ExtraSugar(authLoginStart)).Methods("GET").Name("authLoginStart")
	r.HandleFunc("/auth/google", utils.ExtraSugar(authGoogle)).Methods("GET").Name("authGoogle")

	r.HandleFunc("/auth/github/start", utils.ExtraSugar(authGitHubStart)).Methods("GET").Name("authGitHubStart")
	r.HandleFunc("/auth/github/finish", utils.ExtraSugar(authGitHubFinish)).Methods("GET").Name("authGitHubFinish")

	r.HandleFunc("/auth/logout", utils.ExtraSugar(authLogout)).Methods("GET").Name("authLogout")
}

func authLoginStart(w http.ResponseWriter, r *http.Request) {
	oauthStart(w, r, "google", "/pastebin/auth/google", "openid", "profile")
}

func authGitHubStart(w http.ResponseWriter, r *http.Request) {
	oauthStart(w, r, "github", "/pastebin/auth/github/finish")
}

func authGitHubFinish(w http.ResponseWriter, r *http.Request) {
	_, err := oauthFinish(w, r, "github")
	if err != nil {
		log.Printf("ERROR: %v\n", err)
		http.Error(w, "Meep! We were trying to talk to GitHub but something went wrong.", http.StatusInternalServerError)
		return
	}

	if token, err := models.GetOAuthToken(r); err == nil {
		client := &http.Client{}
		var responseData bytes.Buffer
		// https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/#3-use-the-access-token-to-access-the-api
		if request, err := http.NewRequest("GET", "https://api.github.com/user", &responseData); err == nil {
			request.Header.Set("Authorization", "token "+token.AccessToken)
			if response, err := client.Do(request); err == nil {
				if response.StatusCode == 200 {
					defer response.Body.Close()
					var user struct {
						ID int `json:"id"`
					}
					if data, err := ioutil.ReadAll(response.Body); err == nil {
						if err := json.Unmarshal([]byte(data), &user); err == nil {
							if err = utils.InitAppSession(w, r, strconv.Itoa(user.ID)); err == nil {
								utils.ClearOauthCookie(w)
								http.Redirect(w, r, "/pastebin/", http.StatusFound)
							} else {
								log.Printf("ERROR: %v\n", err)
								http.Error(w, "Meep! We were trying to initialize your session but something went wrong.", http.StatusInternalServerError)
							}
						} else {
							log.Printf("ERROR: %v\n", err)
							http.Error(w, "Meep! We were trying to parse your details but something went wrong.", http.StatusInternalServerError)
						}
					} else {
						log.Printf("ERROR: %v\n", err)
						http.Error(w, "Meep! We were trying to read your details but something went wrong.", http.StatusInternalServerError)
					}
				} else {
					if data, err := ioutil.ReadAll(response.Body); err != nil {
						log.Printf("ERROR: github returned non-OK, data could not be read, %v\n", err)
					} else {
						log.Printf("ERROR: github returned non-OK, %s\n", string(data))
					}
					http.Error(w, "Meep! We were trying to fetch your details but something isn't right.", response.StatusCode)
				}
			} else {
				log.Printf("ERROR: %v\n", err)
				http.Error(w, "Meep! We were trying to fetch your details but something went wrong.", http.StatusInternalServerError)
			}
		} else {
			log.Printf("ERROR: %v\n", err)
			http.Error(w, "Meep! We were trying to build a client for your token but something went wrong.", http.StatusInternalServerError)
		}
	} else {
		log.Printf("ERROR: %v\n", err)
		http.Error(w, "Meep! We were trying to fetch your token but something went wrong.", http.StatusInternalServerError)
	}
}

func authGoogle(w http.ResponseWriter, r *http.Request) {
	stateNonce, err := oauthFinish(w, r, "google", "openid", "profile")
	if err != nil {
		log.Printf("ERROR: %v\n", err)
		http.Error(w, "Meep! We were trying to talk to Google but something went wrong.", http.StatusInternalServerError)
		return
	}

	if token, err := models.GetOAuthToken(r); err == nil {
		data := token.Extra("id_token")
		if data != nil {
			var idToken struct {
				UserID string `json:"sub"`
				Nonce  string `json:"nonce"`
			}

			if strData, ok := data.(string); ok { // wow, golang can be awkward!
				// https://github.com/googleapis/google-auth-library-python/blob/08272d89667a901d0dff6c4ba53d5b30fcc29e81/google/auth/jwt.py#L134
				if strings.Count(strData, ".") != 2 {
					http.Error(w, "Meep! We were trying to count the dots in your token but something went wrong.", http.StatusInternalServerError)
					return
				}
				encodedData := strings.Split(strData, ".")[1]

				if jsonData, err := base64.RawURLEncoding.DecodeString(encodedData); err == nil {
					if err := json.Unmarshal([]byte(jsonData), &idToken); err == nil {
						if idToken.Nonce != stateNonce {
							log.Printf("WARNING: Nonce mismatch %s != %s", idToken.Nonce, stateNonce)
							http.Error(w, "Meep! We were trying to validate your session but something went wrong (nonce mismatch).", http.StatusBadRequest)
						} else if err = utils.InitAppSession(w, r, idToken.UserID); err == nil {
							utils.ClearOauthCookie(w)
							http.Redirect(w, r, "/pastebin/", http.StatusFound)
						} else {
							log.Printf("ERROR: %v\n", err)
							http.Error(w, "Meep! We were trying to initialize your session but something went wrong.", http.StatusInternalServerError)
						}
					} else {
						log.Printf("ERROR: %v\n", err)
						http.Error(w, "Meep! We were trying to parse your token but something went wrong.", http.StatusInternalServerError)
					}
				} else {
					log.Printf("ERROR: %v\n", err)
					http.Error(w, "Meep! We were trying to decode your token but something went wrong.", http.StatusInternalServerError)
				}
			} else {
				log.Printf("ERROR: %v\n", err)
				http.Error(w, "Meep! We were trying to type-assert your token but something went wrong.", http.StatusInternalServerError)
			}
		} else {
			log.Printf("WARNING: nil id_token returned\n")
			http.Error(w, "Meep! We were trying to pick your token but something went wrong.", http.StatusInternalServerError)
		}
	} else {
		log.Printf("ERROR: %v\n", err)
		http.Error(w, "Meep! We were trying to fetch your token but something went wrong.", http.StatusInternalServerError)
	}
}

func authLogout(w http.ResponseWriter, r *http.Request) {
	utils.ClearAppSession(w)
	http.Redirect(w, r, "/pastebin/", http.StatusFound)
}
<commit_msg>Oops, that should be there not there<commit_after>package auth

import (
	// Go Builtin Packages
	"bytes"
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"

	// The Gorilla Web Toolkit
	"github.com/gorilla/mux"

	// Local Packages
	"github.com/adayoung/gae-pastebin/pastebin/models"
	"github.com/adayoung/gae-pastebin/pastebin/utils"
)

func InitRoutes(r *mux.Router) {
	r.HandleFunc("/auth/gdrive/start", utils.ExtraSugar(authGDriveStart)).Methods("GET").Name("authGDriveStart")
	r.HandleFunc("/auth/gdrive/finish", utils.ExtraSugar(authGDriveFinish)).Methods("GET").Name("authGDriveFinish")

	r.HandleFunc("/auth/login", utils.ExtraSugar(authLoginStart)).Methods("GET").Name("authLoginStart")
	r.HandleFunc("/auth/google", utils.ExtraSugar(authGoogle)).Methods("GET").Name("authGoogle")

	r.HandleFunc("/auth/github/start", utils.ExtraSugar(authGitHubStart)).Methods("GET").Name("authGitHubStart")
	r.HandleFunc("/auth/github/finish", utils.ExtraSugar(authGitHubFinish)).Methods("GET").Name("authGitHubFinish")

	r.HandleFunc("/auth/logout", utils.ExtraSugar(authLogout)).Methods("GET").Name("authLogout")
}

func authLoginStart(w http.ResponseWriter, r *http.Request) {
	oauthStart(w, r, "google", "/pastebin/auth/google", "openid", "profile")
}

func authGitHubStart(w http.ResponseWriter, r *http.Request) {
	oauthStart(w, r, "github", "/pastebin/auth/github/finish")
}

func authGitHubFinish(w http.ResponseWriter, r *http.Request) {
	_, err := oauthFinish(w, r, "github")
	if err != nil {
		log.Printf("ERROR: %v\n", err)
		http.Error(w, "Meep! We were trying to talk to GitHub but something went wrong.", http.StatusInternalServerError)
		return
	}

	if token, err := models.GetOAuthToken(r); err == nil {
		client := &http.Client{}
		var responseData bytes.Buffer
		// https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/#3-use-the-access-token-to-access-the-api
		if request, err := http.NewRequest("GET", "https://api.github.com/user", &responseData); err == nil {
			request.Header.Set("Authorization", "token "+token.AccessToken)
			if response, err := client.Do(request); err == nil {
				defer response.Body.Close()
				if response.StatusCode == 200 {
					var user struct {
						ID int `json:"id"`
					}
					if data, err := ioutil.ReadAll(response.Body); err == nil {
						if err := json.Unmarshal([]byte(data), &user); err == nil {
							if err = utils.InitAppSession(w, r, strconv.Itoa(user.ID)); err == nil {
								utils.ClearOauthCookie(w)
								http.Redirect(w, r, "/pastebin/", http.StatusFound)
							} else {
								log.Printf("ERROR: %v\n", err)
								http.Error(w, "Meep! We were trying to initialize your session but something went wrong.", http.StatusInternalServerError)
							}
						} else {
							log.Printf("ERROR: %v\n", err)
							http.Error(w, "Meep! We were trying to parse your details but something went wrong.", http.StatusInternalServerError)
						}
					} else {
						log.Printf("ERROR: %v\n", err)
						http.Error(w, "Meep! We were trying to read your details but something went wrong.", http.StatusInternalServerError)
					}
				} else {
					if data, err := ioutil.ReadAll(response.Body); err != nil {
						log.Printf("ERROR: github returned non-OK, data could not be read, %v\n", err)
					} else {
						log.Printf("ERROR: github returned non-OK, %s\n", string(data))
					}
					http.Error(w, "Meep! We were trying to fetch your details but something isn't right.", response.StatusCode)
				}
			} else {
				log.Printf("ERROR: %v\n", err)
				http.Error(w, "Meep! We were trying to fetch your details but something went wrong.", http.StatusInternalServerError)
			}
		} else {
			log.Printf("ERROR: %v\n", err)
			http.Error(w, "Meep! We were trying to build a client for your token but something went wrong.", http.StatusInternalServerError)
		}
	} else {
		log.Printf("ERROR: %v\n", err)
		http.Error(w, "Meep! We were trying to fetch your token but something went wrong.", http.StatusInternalServerError)
	}
}

func authGoogle(w http.ResponseWriter, r *http.Request) {
	stateNonce, err := oauthFinish(w, r, "google", "openid", "profile")
	if err != nil {
		log.Printf("ERROR: %v\n", err)
		http.Error(w, "Meep! We were trying to talk to Google but something went wrong.", http.StatusInternalServerError)
		return
	}

	if token, err := models.GetOAuthToken(r); err == nil {
		data := token.Extra("id_token")
		if data != nil {
			var idToken struct {
				UserID string `json:"sub"`
				Nonce  string `json:"nonce"`
			}

			if strData, ok := data.(string); ok { // wow, golang can be awkward!
				// https://github.com/googleapis/google-auth-library-python/blob/08272d89667a901d0dff6c4ba53d5b30fcc29e81/google/auth/jwt.py#L134
				if strings.Count(strData, ".") != 2 {
					http.Error(w, "Meep! We were trying to count the dots in your token but something went wrong.", http.StatusInternalServerError)
					return
				}
				encodedData := strings.Split(strData, ".")[1]

				if jsonData, err := base64.RawURLEncoding.DecodeString(encodedData); err == nil {
					if err := json.Unmarshal([]byte(jsonData), &idToken); err == nil {
						if idToken.Nonce != stateNonce {
							log.Printf("WARNING: Nonce mismatch %s != %s", idToken.Nonce, stateNonce)
							http.Error(w, "Meep! We were trying to validate your session but something went wrong (nonce mismatch).", http.StatusBadRequest)
						} else if err = utils.InitAppSession(w, r, idToken.UserID); err == nil {
							utils.ClearOauthCookie(w)
							http.Redirect(w, r, "/pastebin/", http.StatusFound)
						} else {
							log.Printf("ERROR: %v\n", err)
							http.Error(w, "Meep! We were trying to initialize your session but something went wrong.", http.StatusInternalServerError)
						}
					} else {
						log.Printf("ERROR: %v\n", err)
						http.Error(w, "Meep! We were trying to parse your token but something went wrong.", http.StatusInternalServerError)
					}
				} else {
					log.Printf("ERROR: %v\n", err)
					http.Error(w, "Meep! We were trying to decode your token but something went wrong.", http.StatusInternalServerError)
				}
			} else {
				log.Printf("ERROR: %v\n", err)
				http.Error(w, "Meep! We were trying to type-assert your token but something went wrong.", http.StatusInternalServerError)
			}
		} else {
			log.Printf("WARNING: nil id_token returned\n")
			http.Error(w, "Meep! We were trying to pick your token but something went wrong.", http.StatusInternalServerError)
		}
	} else {
		log.Printf("ERROR: %v\n", err)
		http.Error(w, "Meep! We were trying to fetch your token but something went wrong.", http.StatusInternalServerError)
	}
}

func authLogout(w http.ResponseWriter, r *http.Request) {
	utils.ClearAppSession(w)
	http.Redirect(w, r, "/pastebin/", http.StatusFound)
}
<|endoftext|>