<commit_before>package views

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"
	"unicode"

	"github.com/antihax/goesi"
	"golang.org/x/oauth2"

	"github.com/antihax/evedata/services/conservator"
	"github.com/antihax/evedata/services/vanguard"
	"github.com/antihax/evedata/services/vanguard/models"
)

func init() {
	vanguard.AddRoute("account", "GET", "/account",
		func(w http.ResponseWriter, r *http.Request) {
			p := newPage(r, "Account Information")
			p["ScopeGroups"] = models.GetCharacterScopeGroups()
			renderTemplate(w, "account.html", time.Hour*24*31, p)
		})

	vanguard.AddAuthRoute("account", "GET", "/X/accountInfo", accountInfo)
	vanguard.AddAuthRoute("account", "POST", "/X/cursorChar", cursorChar)

	vanguard.AddAuthRoute("account", "GET", "/U/crestTokens", apiGetCRESTTokens)
	vanguard.AddAuthRoute("account", "DELETE", "/U/crestTokens", apiDeleteCRESTToken)

	vanguard.AddAuthRoute("account", "GET", "/U/integrationTokens", apiGetIntegrationTokens)
	vanguard.AddAuthRoute("account", "DELETE", "/U/integrationTokens", apiDeleteIntegrationToken)

	vanguard.AddAuthRoute("account", "POST", "/U/toggleAuth", apiToggleAuth)

	vanguard.AddAuthRoute("account", "GET", "/U/accessableIntegrations", apiAccessableIntegrations)
	vanguard.AddAuthRoute("account", "POST", "/U/joinIntegration", apiJoinIntegration)

	vanguard.AddAuthRoute("account", "POST", "/U/setMailPassword", apiSetMailPassword)

}

func apiToggleAuth(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())
	if s == nil {
		httpErrCode(w, errors.New("Cannot find session"), http.StatusInternalServerError)
		return
	}
	g := vanguard.GlobalsFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for toggle auth"), http.StatusUnauthorized)
		return
	}

	// Parse the characterID
	tokenCharacterID, err := strconv.ParseInt(r.FormValue("tokenCharacterID"), 10, 64)
	if err != nil {
		httpErrCode(w, err, http.StatusForbidden)
		return
	}

	_, err = g.Db.Exec("UPDATE evedata.crestTokens SET authCharacter = ! authCharacter WHERE characterID = ? and tokenCharacterID = ?", characterID, tokenCharacterID)
	if err != nil {
		httpErrCode(w, err, http.StatusInternalServerError)
		return
	}
}

func accountInfo(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		// Silently fail.
		return
	}

	char, ok := s.Values["character"].(goesi.VerifyResponse)
	if !ok {
		httpErrCode(w, errors.New("could not find verify response"), http.StatusForbidden)
		log.Printf("%+v\n", s.Values["character"])
		return
	}

	accountInfo, ok := s.Values["accountInfo"].([]byte)
	if !ok {
		if err := updateAccountInfo(s, int32(characterID), char.CharacterOwnerHash, char.CharacterName); err != nil {
			httpErr(w, err)
			return
		}

		if err := s.Save(r, w); err != nil {
			httpErr(w, err)
			return
		}
	}

	w.Write(accountInfo)
}

func cursorChar(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for cursor"), http.StatusUnauthorized)
		return
	}

	// Parse the cursorCharacterID
	cursorCharacterID, err := strconv.ParseInt(r.FormValue("cursorCharacterID"), 10, 64)
	if err != nil {
		httpErrCode(w, err, http.StatusForbidden)
		return
	}

	// Set our new cursor
	err = models.SetCursorCharacter(characterID, int32(cursorCharacterID))
	if err != nil {
		httpErr(w, err)
		return
	}

	char, ok := s.Values["character"].(goesi.VerifyResponse)
	if !ok {
		httpErrCode(w, errors.New("could not find verify for cursor"), http.StatusForbidden)
		return
	}

	// Update the account information in redis
	if err = updateAccountInfo(s, characterID, char.CharacterOwnerHash, char.CharacterName); err != nil {
		httpErr(w, err)
		return
	}

	if err = s.Save(r, w); err != nil {
		httpErr(w, err)
		return
	}
}

func apiGetCRESTTokens(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	char, ok := s.Values["character"].(goesi.VerifyResponse)
	if !ok {
		httpErrCode(w, errors.New("could not find character response"), http.StatusForbidden)
		log.Printf("%+v\n", s.Values["character"])
		return
	}

	v, err := models.GetCRESTTokens(char.CharacterID, char.CharacterOwnerHash)
	if err != nil {
		httpErr(w, err)
		return
	}

	// Change scopes to groups
	for i := range v {
		v[i].Scopes = models.GetCharacterGroupsByScopesString(v[i].Scopes)
	}

	renderJSON(w, v, 0)

	if err = s.Save(r, w); err != nil {
		httpErr(w, err)
		return
	}
}

func apiDeleteCRESTToken(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())
	g := vanguard.GlobalsFromContext(r.Context())
	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID to delete"), http.StatusUnauthorized)
		return
	}

	cid, err := strconv.ParseInt(r.FormValue("tokenCharacterID"), 10, 64)
	if err != nil {
		httpErrCode(w, err, http.StatusNotFound)
		return
	}

	if err := models.DeleteCRESTToken(characterID, int32(cid)); err != nil {
		httpErrCode(w, err, http.StatusConflict)
		return
	}

	char, ok := s.Values["character"].(goesi.VerifyResponse)
	if !ok {
		httpErrCode(w, errors.New("could not find verify response to delete"), http.StatusForbidden)
		return
	}

	if err = updateAccountInfo(s, characterID, char.CharacterOwnerHash, char.CharacterName); err != nil {
		httpErr(w, err)
		return
	}

	key := fmt.Sprintf("EVEDATA_TOKENSTORE_%d_%d", characterID, cid)
	red := g.Cache.Get()
	defer red.Close()
	red.Do("DEL", key)

	if err = s.Save(r, w); err != nil {
		httpErr(w, err)
		return
	}
}

func apiAccessableIntegrations(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for integration token"), http.StatusUnauthorized)
		return
	}

	v, err := models.GetAvailableIntegrations(characterID)
	if err != nil {
		httpErr(w, err)
		return
	}

	renderJSON(w, v, 0)

	if err = s.Save(r, w); err != nil {
		httpErr(w, err)
		return
	}
}

func apiJoinIntegration(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())
	g := vanguard.GlobalsFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for integration token"), http.StatusUnauthorized)
		return
	}

	integrationID, err := strconv.ParseInt(r.FormValue("integrationID"), 10, 64)
	if err != nil {
		httpErrCode(w, err, http.StatusNotFound)
		return
	}

	i, err := models.GetIntegrationsForCharacter(characterID, int32(integrationID))
	if err != nil {
		httpErr(w, err)
		return
	}

	token := &oauth2.Token{
		Expiry:       i.Expiry,
		AccessToken:  i.AccessToken,
		RefreshToken: i.RefreshToken,
		TokenType:    "Bearer",
	}

	// refresh the token if it expired
	if token.Expiry.After(time.Now()) {
		src, err := g.DiscordAuthenticator.TokenSource(token)
		if err != nil {
			httpErr(w, err)
			return
		}
		token, err = src.Token()
		if err != nil {
			httpErr(w, err)
			return
		}
	}

	if err := g.RPCall("Conservator.JoinUser", conservator.JoinUser{
		IntegrationID: i.IntegrationID,
		AccessToken:   token.AccessToken,
		UserID:        i.IntegrationUserID,
		CharacterName: i.CharacterName,
		CharacterID:   i.TokenCharacterID,
	}, &ok); err != nil || !ok {
		httpErr(w, err)
		return
	}

}

func apiGetIntegrationTokens(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for integration token"), http.StatusUnauthorized)
		return
	}

	v, err := models.GetIntegrationTokens(characterID)
	if err != nil {
		httpErr(w, err)
		return
	}

	renderJSON(w, v, 0)

	if err = s.Save(r, w); err != nil {
		httpErr(w, err)
		return
	}
}

func apiDeleteIntegrationToken(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for integration token to delete"), http.StatusUnauthorized)
		return
	}

	if err := models.DeleteIntegrationToken(r.FormValue("type"), characterID, r.FormValue("userID")); err != nil {
		httpErrCode(w, err, http.StatusConflict)
		return
	}
}

func verifyPassword(s string) bool {
	var upper, lower, number bool
	for _, s := range s {
		switch {
		case unicode.IsNumber(s):
			number = true
		case unicode.IsUpper(s):
			upper = true
		case unicode.IsLower(s):
			lower = true
		}
	}
	if upper && lower && number && len(s) >= 12 {
		return true
	}
	return false
}

func apiSetMailPassword(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())
	if s == nil {
		httpErrCode(w, errors.New("could not find session"), http.StatusUnauthorized)
		return
	}

	char, ok := s.Values["character"].(goesi.VerifyResponse)
	if !ok {
		httpErrCode(w, errors.New("could not find verify response to change mail password"), http.StatusForbidden)
		return
	}

	tokenCharacterID, err := strconv.ParseInt(r.FormValue("tokenCharacterID"), 10, 32)
	if err != nil {
		httpErrCode(w, errors.New("invalid tokenCharacterID"), http.StatusBadRequest)
		return
	}

	if !verifyPassword(r.FormValue("password")) {
		httpErrCode(w, errors.New("Password must be at least 12 characters with one uppercase, one lowercase, and one number"), http.StatusBadRequest)
		return
	}

	if err := models.SetMailPassword(char.CharacterID, int32(tokenCharacterID), char.CharacterOwnerHash, r.FormValue("password")); err != nil {
		httpErr(w, err)
		return
	}
}
<commit_msg>revoke tokens on delete<commit_after>package views

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"
	"unicode"

	"github.com/antihax/goesi"
	"golang.org/x/oauth2"

	"github.com/antihax/evedata/services/conservator"
	"github.com/antihax/evedata/services/vanguard"
	"github.com/antihax/evedata/services/vanguard/models"
)

func init() {
	vanguard.AddRoute("account", "GET", "/account",
		func(w http.ResponseWriter, r *http.Request) {
			p := newPage(r, "Account Information")
			p["ScopeGroups"] = models.GetCharacterScopeGroups()
			renderTemplate(w, "account.html", time.Hour*24*31, p)
		})

	vanguard.AddAuthRoute("account", "GET", "/X/accountInfo", accountInfo)
	vanguard.AddAuthRoute("account", "POST", "/X/cursorChar", cursorChar)

	vanguard.AddAuthRoute("account", "GET", "/U/crestTokens", apiGetCRESTTokens)
	vanguard.AddAuthRoute("account", "DELETE", "/U/crestTokens", apiDeleteCRESTToken)

	vanguard.AddAuthRoute("account", "GET", "/U/integrationTokens", apiGetIntegrationTokens)
	vanguard.AddAuthRoute("account", "DELETE", "/U/integrationTokens", apiDeleteIntegrationToken)

	vanguard.AddAuthRoute("account", "POST", "/U/toggleAuth", apiToggleAuth)

	vanguard.AddAuthRoute("account", "GET", "/U/accessableIntegrations", apiAccessableIntegrations)
	vanguard.AddAuthRoute("account", "POST", "/U/joinIntegration", apiJoinIntegration)

	vanguard.AddAuthRoute("account", "POST", "/U/setMailPassword", apiSetMailPassword)

}

func apiToggleAuth(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())
	if s == nil {
		httpErrCode(w, errors.New("Cannot find session"), http.StatusInternalServerError)
		return
	}
	g := vanguard.GlobalsFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for toggle auth"), http.StatusUnauthorized)
		return
	}

	// Parse the characterID
	tokenCharacterID, err := strconv.ParseInt(r.FormValue("tokenCharacterID"), 10, 64)
	if err != nil {
		httpErrCode(w, err, http.StatusForbidden)
		return
	}

	_, err = g.Db.Exec("UPDATE evedata.crestTokens SET authCharacter = ! authCharacter WHERE characterID = ? and tokenCharacterID = ?", characterID, tokenCharacterID)
	if err != nil {
		httpErrCode(w, err, http.StatusInternalServerError)
		return
	}
}

func accountInfo(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		// Silently fail.
		return
	}

	char, ok := s.Values["character"].(goesi.VerifyResponse)
	if !ok {
		httpErrCode(w, errors.New("could not find verify response"), http.StatusForbidden)
		log.Printf("%+v\n", s.Values["character"])
		return
	}

	accountInfo, ok := s.Values["accountInfo"].([]byte)
	if !ok {
		if err := updateAccountInfo(s, int32(characterID), char.CharacterOwnerHash, char.CharacterName); err != nil {
			httpErr(w, err)
			return
		}

		if err := s.Save(r, w); err != nil {
			httpErr(w, err)
			return
		}
	}

	w.Write(accountInfo)
}

func cursorChar(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for cursor"), http.StatusUnauthorized)
		return
	}

	// Parse the cursorCharacterID
	cursorCharacterID, err := strconv.ParseInt(r.FormValue("cursorCharacterID"), 10, 64)
	if err != nil {
		httpErrCode(w, err, http.StatusForbidden)
		return
	}

	// Set our new cursor
	err = models.SetCursorCharacter(characterID, int32(cursorCharacterID))
	if err != nil {
		httpErr(w, err)
		return
	}

	char, ok := s.Values["character"].(goesi.VerifyResponse)
	if !ok {
		httpErrCode(w, errors.New("could not find verify for cursor"), http.StatusForbidden)
		return
	}

	// Update the account information in redis
	if err = updateAccountInfo(s, characterID, char.CharacterOwnerHash, char.CharacterName); err != nil {
		httpErr(w, err)
		return
	}

	if err = s.Save(r, w); err != nil {
		httpErr(w, err)
		return
	}
}

func apiGetCRESTTokens(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	char, ok := s.Values["character"].(goesi.VerifyResponse)
	if !ok {
		httpErrCode(w, errors.New("could not find character response"), http.StatusForbidden)
		log.Printf("%+v\n", s.Values["character"])
		return
	}

	v, err := models.GetCRESTTokens(char.CharacterID, char.CharacterOwnerHash)
	if err != nil {
		httpErr(w, err)
		return
	}

	// Change scopes to groups
	for i := range v {
		v[i].Scopes = models.GetCharacterGroupsByScopesString(v[i].Scopes)
	}

	renderJSON(w, v, 0)

	if err = s.Save(r, w); err != nil {
		httpErr(w, err)
		return
	}
}

func apiDeleteCRESTToken(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())
	g := vanguard.GlobalsFromContext(r.Context())

	// Get the sessions main characterID
	char, ok := s.Values["character"].(goesi.VerifyResponse)
	if !ok {
		httpErrCode(w, errors.New("could not find verify response to delete"), http.StatusForbidden)
		return
	}

	cid, err := strconv.ParseInt(r.FormValue("tokenCharacterID"), 10, 64)
	if err != nil {
		httpErrCode(w, err, http.StatusNotFound)
		return
	}

	// Revoke the token before we delete. Do not error out if this fails.
	if tok, err := models.GetCRESTToken(char.CharacterID, char.CharacterOwnerHash, int32(cid)); err != nil {
		log.Println(err)
		return
	} else {
		err = g.TokenAuthenticator.TokenRevoke(tok.RefreshToken)
		if err != nil {
			log.Println(err)
			return
		}
	}

	if err := models.DeleteCRESTToken(char.CharacterID, int32(cid)); err != nil {
		httpErrCode(w, err, http.StatusConflict)
		return
	}

	if err = updateAccountInfo(s, char.CharacterID, char.CharacterOwnerHash, char.CharacterName); err != nil {
		httpErr(w, err)
		return
	}

	key := fmt.Sprintf("EVEDATA_TOKENSTORE_%d_%d", char.CharacterID, cid)
	red := g.Cache.Get()
	defer red.Close()
	red.Do("DEL", key)

	if err = s.Save(r, w); err != nil {
		httpErr(w, err)
		return
	}
}

func apiAccessableIntegrations(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for integration token"), http.StatusUnauthorized)
		return
	}

	v, err := models.GetAvailableIntegrations(characterID)
	if err != nil {
		httpErr(w, err)
		return
	}

	renderJSON(w, v, 0)

	if err = s.Save(r, w); err != nil {
		httpErr(w, err)
		return
	}
}

func apiJoinIntegration(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())
	g := vanguard.GlobalsFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for integration token"), http.StatusUnauthorized)
		return
	}

	integrationID, err := strconv.ParseInt(r.FormValue("integrationID"), 10, 64)
	if err != nil {
		httpErrCode(w, err, http.StatusNotFound)
		return
	}

	i, err := models.GetIntegrationsForCharacter(characterID, int32(integrationID))
	if err != nil {
		httpErr(w, err)
		return
	}

	token := &oauth2.Token{
		Expiry:       i.Expiry,
		AccessToken:  i.AccessToken,
		RefreshToken: i.RefreshToken,
		TokenType:    "Bearer",
	}

	// refresh the token if it expired
	if token.Expiry.After(time.Now()) {
		src, err := g.DiscordAuthenticator.TokenSource(token)
		if err != nil {
			httpErr(w, err)
			return
		}
		token, err = src.Token()
		if err != nil {
			httpErr(w, err)
			return
		}
	}

	if err := g.RPCall("Conservator.JoinUser", conservator.JoinUser{
		IntegrationID: i.IntegrationID,
		AccessToken:   token.AccessToken,
		UserID:        i.IntegrationUserID,
		CharacterName: i.CharacterName,
		CharacterID:   i.TokenCharacterID,
	}, &ok); err != nil || !ok {
		httpErr(w, err)
		return
	}

}

func apiGetIntegrationTokens(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for integration token"), http.StatusUnauthorized)
		return
	}

	v, err := models.GetIntegrationTokens(characterID)
	if err != nil {
		httpErr(w, err)
		return
	}

	renderJSON(w, v, 0)

	if err = s.Save(r, w); err != nil {
		httpErr(w, err)
		return
	}
}

func apiDeleteIntegrationToken(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())

	// Get the sessions main characterID
	characterID, ok := s.Values["characterID"].(int32)
	if !ok {
		httpErrCode(w, errors.New("could not find character ID for integration token to delete"), http.StatusUnauthorized)
		return
	}

	if err := models.DeleteIntegrationToken(r.FormValue("type"), characterID, r.FormValue("userID")); err != nil {
		httpErrCode(w, err, http.StatusConflict)
		return
	}
}

func verifyPassword(s string) bool {
	var upper, lower, number bool
	for _, s := range s {
		switch {
		case unicode.IsNumber(s):
			number = true
		case unicode.IsUpper(s):
			upper = true
		case unicode.IsLower(s):
			lower = true
		}
	}
	if upper && lower && number && len(s) >= 12 {
		return true
	}
	return false
}

func apiSetMailPassword(w http.ResponseWriter, r *http.Request) {
	s := vanguard.SessionFromContext(r.Context())
	if s == nil {
		httpErrCode(w, errors.New("could not find session"), http.StatusUnauthorized)
		return
	}

	char, ok := s.Values["character"].(goesi.VerifyResponse)
	if !ok {
		httpErrCode(w, errors.New("could not find verify response to change mail password"), http.StatusForbidden)
		return
	}

	tokenCharacterID, err := strconv.ParseInt(r.FormValue("tokenCharacterID"), 10, 32)
	if err != nil {
		httpErrCode(w, errors.New("invalid tokenCharacterID"), http.StatusBadRequest)
		return
	}

	if !verifyPassword(r.FormValue("password")) {
		httpErrCode(w, errors.New("Password must be at least 12 characters with one uppercase, one lowercase, and one number"), http.StatusBadRequest)
		return
	}

	if err := models.SetMailPassword(char.CharacterID, int32(tokenCharacterID), char.CharacterOwnerHash, r.FormValue("password")); err != nil {
		httpErr(w, err)
		return
	}
}
<|endoftext|>


<commit_before>package web

import (
	"net/http"
	"reflect"
)

type Request struct {
	*http.Request

	// This map exists if you have wildcards in your URL that you need to capture.
	// Eg, /users/:id/tickets/:ticket_id and /users/1/tickets/33 would yield the map {id: "3", ticket_id: "33"}
	PathParams map[string]string

	// The actual route that got invoked
	route *Route

	rootContext   reflect.Value // Root context. Set immediately.
	targetContext reflect.Value // The target context corresponding to the route. Not set until root middleware is done.
}
<commit_msg>Expose the routing path to actions if they want it<commit_after>package web

import (
	"net/http"
	"reflect"
)

type Request struct {
	*http.Request

	// This map exists if you have wildcards in your URL that you need to capture.
	// Eg, /users/:id/tickets/:ticket_id and /users/1/tickets/33 would yield the map {id: "3", ticket_id: "33"}
	PathParams map[string]string

	// The actual route that got invoked
	route *Route

	rootContext   reflect.Value // Root context. Set immediately.
	targetContext reflect.Value // The target context corresponding to the route. Not set until root middleware is done.
}

func (r *Request) RoutePath() string {
	return r.route.Path
}<|endoftext|>


<commit_before>package pwd

import (
	"fmt"
	"log"
	"math"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/play-with-docker/play-with-docker/config"
	"github.com/play-with-docker/play-with-docker/docker"
	"github.com/twinj/uuid"
)

type sessionBuilderWriter struct {
	sessionId string
	broadcast BroadcastApi
}

func (s *sessionBuilderWriter) Write(p []byte) (n int, err error) {
	s.broadcast.BroadcastTo(s.sessionId, "session builder out", string(p))
	return len(p), nil
}

type SessionSetupConf struct {
	Instances []SessionSetupInstanceConf `json:"instances"`
}

type SessionSetupInstanceConf struct {
	Image          string `json:"image"`
	Hostname       string `json:"hostname"`
	IsSwarmManager bool   `json:"is_swarm_manager"`
	IsSwarmWorker  bool   `json:"is_swarm_worker"`
}

type Session struct {
	rw           sync.Mutex
	Id           string               `json:"id"`
	Instances    map[string]*Instance `json:"instances"`
	CreatedAt    time.Time            `json:"created_at"`
	ExpiresAt    time.Time            `json:"expires_at"`
	PwdIpAddress string               `json:"pwd_ip_address"`
	Ready        bool                 `json:"ready"`
	Stack        string               `json:"stack"`
	StackName    string               `json:"stack_name"`
	closingTimer *time.Timer          `json:"-"`
	scheduled    bool                 `json:"-"`
	clients      []*Client            `json:"-"`
	ticker       *time.Ticker         `json:"-"`
}

func (p *pwd) SessionNew(duration time.Duration, stack, stackName string) (*Session, error) {
	sessionsMutex.Lock()
	defer sessionsMutex.Unlock()

	s := &Session{}
	s.Id = uuid.NewV4().String()
	s.Instances = map[string]*Instance{}
	s.CreatedAt = time.Now()
	s.ExpiresAt = s.CreatedAt.Add(duration)
	s.Ready = true
	s.Stack = stack

	if s.Stack != "" {
		s.Ready = false
	}
	if stackName == "" {
		stackName = "pwd"
	}
	s.StackName = stackName

	log.Printf("NewSession id=[%s]\n", s.Id)

	if err := p.docker.CreateNetwork(s.Id); err != nil {
		log.Println("ERROR NETWORKING")
		return nil, err
	}
	log.Printf("Network [%s] created for session [%s]\n", s.Id, s.Id)

	if err := p.prepareSession(s); err != nil {
		log.Println(err)
		return nil, err
	}

	sessions[s.Id] = s
	if err := p.storage.Save(); err != nil {
		log.Println(err)
		return nil, err
	}

	setGauges()

	return s, nil
}

func (p *pwd) SessionClose(s *Session) error {
	s.rw.Lock()
	defer s.rw.Unlock()

	if s.ticker != nil {
		s.ticker.Stop()
	}
	p.broadcast.BroadcastTo(s.Id, "session end")
	p.broadcast.BroadcastTo(s.Id, "disconnect")
	log.Printf("Starting clean up of session [%s]\n", s.Id)
	for _, i := range s.Instances {
		err := p.InstanceDelete(s, i)
		if err != nil {
			log.Println(err)
			return err
		}
	}
	// Disconnect PWD daemon from the network
	if err := p.docker.DisconnectNetwork(config.PWDContainerName, s.Id); err != nil {
		if !strings.Contains(err.Error(), "is not connected to the network") {
			log.Println("ERROR NETWORKING")
			return err
		}
	}
	log.Printf("Disconnected pwd from network [%s]\n", s.Id)
	if err := p.docker.DeleteNetwork(s.Id); err != nil {
		if !strings.Contains(err.Error(), "not found") {
			log.Println(err)
			return err
		}
	}
	delete(sessions, s.Id)

	// We store sessions as soon as we delete one
	if err := p.storage.Save(); err != nil {
		return err
	}
	setGauges()
	log.Printf("Cleaned up session [%s]\n", s.Id)
	return nil

}

func (p *pwd) SessionGetSmallestViewPort(s *Session) ViewPort {
	minRows := s.clients[0].viewPort.Rows
	minCols := s.clients[0].viewPort.Cols

	for _, c := range s.clients {
		minRows = uint(math.Min(float64(minRows), float64(c.viewPort.Rows)))
		minCols = uint(math.Min(float64(minCols), float64(c.viewPort.Cols)))
	}

	return ViewPort{Rows: minRows, Cols: minCols}
}

func (p *pwd) SessionDeployStack(s *Session) error {
	if s.Ready {
		// a stack was already deployed on this session, just ignore
		return nil
	}

	s.Ready = false
	p.broadcast.BroadcastTo(s.Id, "session ready", false)
	i, err := p.InstanceNew(s, InstanceConfig{})
	if err != nil {
		log.Printf("Error creating instance for stack [%s]: %s\n", s.Stack, err)
		return err
	}
	err = p.InstanceUploadFromUrl(i, s.Stack)
	if err != nil {
		log.Printf("Error uploading stack file [%s]: %s\n", s.Stack, err)
		return err
	}

	fileName := path.Base(s.Stack)
	file := fmt.Sprintf("/var/run/pwd/uploads/%s", fileName)
	cmd := fmt.Sprintf("docker swarm init --advertise-addr eth0 && docker-compose -f %s pull && docker stack deploy -c %s %s", file, file, s.StackName)

	w := sessionBuilderWriter{sessionId: s.Id, broadcast: p.broadcast}
	code, err := p.docker.ExecAttach(i.Name, []string{"sh", "-c", cmd}, &w)
	if err != nil {
		log.Printf("Error executing stack [%s]: %s\n", s.Stack, err)
		return err
	}

	log.Printf("Stack execution finished with code %d\n", code)
	s.Ready = true
	p.broadcast.BroadcastTo(s.Id, "session ready", true)
	if err := p.storage.Save(); err != nil {
		return err
	}
	return nil
}

func (p *pwd) SessionGet(sessionId string) *Session {
	s := sessions[sessionId]
	return s
}

func (p *pwd) SessionLoadAndPrepare() error {
	err := p.storage.Load()
	if err != nil {
		return err
	}

	wg := sync.WaitGroup{}
	for _, s := range sessions {
		// Connect PWD daemon to the new network
		if s.PwdIpAddress == "" {
			return fmt.Errorf("Cannot load stored sessions as they don't have the pwd ip address stored with them")
		}
		wg.Add(1)
		go func(s *Session) {
			s.rw.Lock()
			defer s.rw.Unlock()
			defer wg.Done()

			err := p.prepareSession(s)
			if err != nil {
				log.Println(err)
			}
			for _, i := range s.Instances {
				// wire the session back to the instance
				i.session = s
				go p.InstanceAttachTerminal(i)
			}
		}(s)
	}

	wg.Wait()
	setGauges()

	return nil
}

func (p *pwd) SessionSetup(session *Session, conf SessionSetupConf) error {
	var tokens *docker.SwarmTokens = nil
	var firstSwarmManager *Instance = nil

	// first look for a swarm manager and create it
	for _, conf := range conf.Instances {
		if conf.IsSwarmManager {
			instanceConf := InstanceConfig{
				ImageName: conf.Image,
				Hostname:  conf.Hostname,
			}
			i, err := p.InstanceNew(session, instanceConf)
			if err != nil {
				return err
			}
			if i.docker == nil {
				dock, err := p.docker.New(i.IP, i.Cert, i.Key)
				if err != nil {
					return err
				}
				i.docker = dock
			}
			tkns, err := i.docker.SwarmInit()
			if err != nil {
				return err
			}
			tokens = tkns
			firstSwarmManager = i
			break
		}
	}

	// now create the rest in parallel

	wg := sync.WaitGroup{}
	for _, c := range conf.Instances {
		if firstSwarmManager != nil && c.Hostname != firstSwarmManager.Hostname {
			wg.Add(1)
			go func(c SessionSetupInstanceConf) {
				defer wg.Done()
				instanceConf := InstanceConfig{
					ImageName: c.Image,
					Hostname:  c.Hostname,
				}
				i, err := p.InstanceNew(session, instanceConf)
				if err != nil {
					log.Println(err)
					return
				}
				if c.IsSwarmManager || c.IsSwarmWorker {
					// check if we have connection to the daemon, if not, create it
					if i.docker == nil {
						dock, err := p.docker.New(i.IP, i.Cert, i.Key)
						if err != nil {
							log.Println(err)
							return
						}
						i.docker = dock
					}
				}

				if firstSwarmManager != nil {
					if c.IsSwarmManager {
						// this is a swarm manager
						// cluster has already been initiated, join as manager
						err := i.docker.SwarmJoin(fmt.Sprintf("%s:2377", firstSwarmManager.IP), tokens.Manager)
						if err != nil {
							log.Println(err)
							return
						}
					}
					if c.IsSwarmWorker {
						// this is a swarm worker
						err := i.docker.SwarmJoin(fmt.Sprintf("%s:2377", firstSwarmManager.IP), tokens.Worker)
						if err != nil {
							log.Println(err)
							return
						}
					}
				}
			}(c)
		}
	}
	wg.Wait()

	return nil
}

// This function should be called any time a session needs to be prepared:
// 1. Like when it is created
// 2. When it was loaded from storage
func (p *pwd) prepareSession(session *Session) error {
	p.scheduleSessionClose(session)

	// Connect PWD daemon to the new network
	if err := p.connectToNetwork(session); err != nil {
		return err
	}

	// Schedule periodic tasks
	p.tasks.Schedule(session)

	return nil
}

func (p *pwd) scheduleSessionClose(s *Session) {
	timeLeft := s.ExpiresAt.Sub(time.Now())
	s.closingTimer = time.AfterFunc(timeLeft, func() {
		p.SessionClose(s)
	})
}

func (p *pwd) connectToNetwork(s *Session) error {
	ip, err := p.docker.ConnectNetwork(config.PWDContainerName, s.Id, s.PwdIpAddress)
	if err != nil {
		log.Println("ERROR NETWORKING")
		return err
	}
	s.PwdIpAddress = ip
	log.Printf("Connected %s to network [%s]\n", config.PWDContainerName, s.Id)
	return nil
}
<commit_msg>Avoid stopping the world<commit_after>package pwd

import (
	"fmt"
	"log"
	"math"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/play-with-docker/play-with-docker/config"
	"github.com/play-with-docker/play-with-docker/docker"
	"github.com/twinj/uuid"
)

type sessionBuilderWriter struct {
	sessionId string
	broadcast BroadcastApi
}

func (s *sessionBuilderWriter) Write(p []byte) (n int, err error) {
	s.broadcast.BroadcastTo(s.sessionId, "session builder out", string(p))
	return len(p), nil
}

type SessionSetupConf struct {
	Instances []SessionSetupInstanceConf `json:"instances"`
}

type SessionSetupInstanceConf struct {
	Image          string `json:"image"`
	Hostname       string `json:"hostname"`
	IsSwarmManager bool   `json:"is_swarm_manager"`
	IsSwarmWorker  bool   `json:"is_swarm_worker"`
}

type Session struct {
	rw           sync.Mutex
	Id           string               `json:"id"`
	Instances    map[string]*Instance `json:"instances"`
	CreatedAt    time.Time            `json:"created_at"`
	ExpiresAt    time.Time            `json:"expires_at"`
	PwdIpAddress string               `json:"pwd_ip_address"`
	Ready        bool                 `json:"ready"`
	Stack        string               `json:"stack"`
	StackName    string               `json:"stack_name"`
	closingTimer *time.Timer          `json:"-"`
	scheduled    bool                 `json:"-"`
	clients      []*Client            `json:"-"`
	ticker       *time.Ticker         `json:"-"`
}

func (p *pwd) SessionNew(duration time.Duration, stack, stackName string) (*Session, error) {
	sessionsMutex.Lock()
	defer sessionsMutex.Unlock()

	s := &Session{}
	s.Id = uuid.NewV4().String()
	s.Instances = map[string]*Instance{}
	s.CreatedAt = time.Now()
	s.ExpiresAt = s.CreatedAt.Add(duration)
	s.Ready = true
	s.Stack = stack

	if s.Stack != "" {
		s.Ready = false
	}
	if stackName == "" {
		stackName = "pwd"
	}
	s.StackName = stackName

	log.Printf("NewSession id=[%s]\n", s.Id)

	if err := p.docker.CreateNetwork(s.Id); err != nil {
		log.Println("ERROR NETWORKING")
		return nil, err
	}
	log.Printf("Network [%s] created for session [%s]\n", s.Id, s.Id)

	if err := p.prepareSession(s); err != nil {
		log.Println(err)
		return nil, err
	}

	sessions[s.Id] = s
	if err := p.storage.Save(); err != nil {
		log.Println(err)
		return nil, err
	}

	setGauges()

	return s, nil
}

func (p *pwd) SessionClose(s *Session) error {
	s.rw.Lock()
	defer s.rw.Unlock()

	if s.ticker != nil {
		s.ticker.Stop()
	}
	p.broadcast.BroadcastTo(s.Id, "session end")
	p.broadcast.BroadcastTo(s.Id, "disconnect")
	log.Printf("Starting clean up of session [%s]\n", s.Id)
	for _, i := range s.Instances {
		err := p.InstanceDelete(s, i)
		if err != nil {
			log.Println(err)
			return err
		}
	}
	// Disconnect PWD daemon from the network
	if err := p.docker.DisconnectNetwork(config.PWDContainerName, s.Id); err != nil {
		if !strings.Contains(err.Error(), "is not connected to the network") {
			log.Println("ERROR NETWORKING")
			return err
		}
	}
	log.Printf("Disconnected pwd from network [%s]\n", s.Id)
	if err := p.docker.DeleteNetwork(s.Id); err != nil {
		if !strings.Contains(err.Error(), "not found") {
			log.Println(err)
			return err
		}
	}
	delete(sessions, s.Id)

	// We store sessions as soon as we delete one
	if err := p.storage.Save(); err != nil {
		return err
	}
	setGauges()
	log.Printf("Cleaned up session [%s]\n", s.Id)
	return nil

}

func (p *pwd) SessionGetSmallestViewPort(s *Session) ViewPort {
	minRows := s.clients[0].viewPort.Rows
	minCols := s.clients[0].viewPort.Cols

	for _, c := range s.clients {
		minRows = uint(math.Min(float64(minRows), float64(c.viewPort.Rows)))
		minCols = uint(math.Min(float64(minCols), float64(c.viewPort.Cols)))
	}

	return ViewPort{Rows: minRows, Cols: minCols}
}

func (p *pwd) SessionDeployStack(s *Session) error {
	if s.Ready {
		// a stack was already deployed on this session, just ignore
		return nil
	}

	s.Ready = false
	p.broadcast.BroadcastTo(s.Id, "session ready", false)
	i, err := p.InstanceNew(s, InstanceConfig{})
	if err != nil {
		log.Printf("Error creating instance for stack [%s]: %s\n", s.Stack, err)
		return err
	}
	err = p.InstanceUploadFromUrl(i, s.Stack)
	if err != nil {
		log.Printf("Error uploading stack file [%s]: %s\n", s.Stack, err)
		return err
	}

	fileName := path.Base(s.Stack)
	file := fmt.Sprintf("/var/run/pwd/uploads/%s", fileName)
	cmd := fmt.Sprintf("docker swarm init --advertise-addr eth0 && docker-compose -f %s pull && docker stack deploy -c %s %s", file, file, s.StackName)

	w := sessionBuilderWriter{sessionId: s.Id, broadcast: p.broadcast}
	code, err := p.docker.ExecAttach(i.Name, []string{"sh", "-c", cmd}, &w)
	if err != nil {
		log.Printf("Error executing stack [%s]: %s\n", s.Stack, err)
		return err
	}

	log.Printf("Stack execution finished with code %d\n", code)
	s.Ready = true
	p.broadcast.BroadcastTo(s.Id, "session ready", true)
	if err := p.storage.Save(); err != nil {
		return err
	}
	return nil
}

func (p *pwd) SessionGet(sessionId string) *Session {
	s := sessions[sessionId]
	return s
}

func (p *pwd) SessionLoadAndPrepare() error {
	err := p.storage.Load()
	if err != nil {
		return err
	}

	wg := sync.WaitGroup{}
	for _, s := range sessions {
		// Connect PWD daemon to the new network
		if s.PwdIpAddress == "" {
			log.Printf("Cannot load store session [%s] as they don't have the pwd ip address stored with them\n", s.PwdIpAddress)
			continue
		}
		wg.Add(1)
		go func(s *Session) {
			s.rw.Lock()
			defer s.rw.Unlock()
			defer wg.Done()

			err := p.prepareSession(s)
			if err != nil {
				log.Println(err)
			}
			for _, i := range s.Instances {
				// wire the session back to the instance
				i.session = s
				go p.InstanceAttachTerminal(i)
			}
		}(s)
	}

	wg.Wait()
	setGauges()

	return nil
}

func (p *pwd) SessionSetup(session *Session, conf SessionSetupConf) error {
	var tokens *docker.SwarmTokens = nil
	var firstSwarmManager *Instance = nil

	// first look for a swarm manager and create it
	for _, conf := range conf.Instances {
		if conf.IsSwarmManager {
			instanceConf := InstanceConfig{
				ImageName: conf.Image,
				Hostname:  conf.Hostname,
			}
			i, err := p.InstanceNew(session, instanceConf)
			if err != nil {
				return err
			}
			if i.docker == nil {
				dock, err := p.docker.New(i.IP, i.Cert, i.Key)
				if err != nil {
					return err
				}
				i.docker = dock
			}
			tkns, err := i.docker.SwarmInit()
			if err != nil {
				return err
			}
			tokens = tkns
			firstSwarmManager = i
			break
		}
	}

	// now create the rest in parallel

	wg := sync.WaitGroup{}
	for _, c := range conf.Instances {
		if firstSwarmManager != nil && c.Hostname != firstSwarmManager.Hostname {
			wg.Add(1)
			go func(c SessionSetupInstanceConf) {
				defer wg.Done()
				instanceConf := InstanceConfig{
					ImageName: c.Image,
					Hostname:  c.Hostname,
				}
				i, err := p.InstanceNew(session, instanceConf)
				if err != nil {
					log.Println(err)
					return
				}
				if c.IsSwarmManager || c.IsSwarmWorker {
					// check if we have connection to the daemon, if not, create it
					if i.docker == nil {
						dock, err := p.docker.New(i.IP, i.Cert, i.Key)
						if err != nil {
							log.Println(err)
							return
						}
						i.docker = dock
					}
				}

				if firstSwarmManager != nil {
					if c.IsSwarmManager {
						// this is a swarm manager
						// cluster has already been initiated, join as manager
						err := i.docker.SwarmJoin(fmt.Sprintf("%s:2377", firstSwarmManager.IP), tokens.Manager)
						if err != nil {
							log.Println(err)
							return
						}
					}
					if c.IsSwarmWorker {
						// this is a swarm worker
						err := i.docker.SwarmJoin(fmt.Sprintf("%s:2377", firstSwarmManager.IP), tokens.Worker)
						if err != nil {
							log.Println(err)
							return
						}
					}
				}
			}(c)
		}
	}
	wg.Wait()

	return nil
}

// This function should be called any time a session needs to be prepared:
// 1. Like when it is created
// 2. When it was loaded from storage
func (p *pwd) prepareSession(session *Session) error {
	p.scheduleSessionClose(session)

	// Connect PWD daemon to the new network
	if err := p.connectToNetwork(session); err != nil {
		return err
	}

	// Schedule periodic tasks
	p.tasks.Schedule(session)

	return nil
}

func (p *pwd) scheduleSessionClose(s *Session) {
	timeLeft := s.ExpiresAt.Sub(time.Now())
	s.closingTimer = time.AfterFunc(timeLeft, func() {
		p.SessionClose(s)
	})
}

func (p *pwd) connectToNetwork(s *Session) error {
	ip, err := p.docker.ConnectNetwork(config.PWDContainerName, s.Id, s.PwdIpAddress)
	if err != nil {
		log.Println("ERROR NETWORKING")
		return err
	}
	s.PwdIpAddress = ip
	log.Printf("Connected %s to network [%s]\n", config.PWDContainerName, s.Id)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/ant0ine/go-json-rest/rest"
    "log"
    "strconv"
    "net/http"
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)

func main() {
    api := Api{}
    api.InitDB()

    handler := rest.ResourceHandler{}
    err := handler.SetRoutes(
        &rest.Route{"GET", "/api/:session", api.GetSession},
        &rest.Route{"GET", "/api/topic/:topic", api.GetTopic},
        &rest.Route{"POST", "/api/summary", api.SetSummary},
    )
    if err != nil {
        log.Fatal(err)
    }
    log.Fatal(http.ListenAndServe(":8080", &handler))
}

type Tweet struct {
    Id   string `json:"id"`
    Text string `json:"text"`
}

type TweetList struct {
    TopicId int `json:"topic_id" bson:"t_id"`
    Topic string `json:"topic" bson:"t_name"`
    Desc string `json:"desc" bson:"desc"`
    Tweets []*Tweet `json:"tweets"`
}

type Summary struct {
    TopicId int `json:"topic_id" bson:"t_id"`
    SessionId string `json:"session_id" bson:"s_id"`
    Topic string `json:"topic" bson:"t_name"`
    Tweets []*Tweet `json:"tweets"`
}

type Session struct {
    SessionId string `json:"session_id" bson:"s_id"`
    Topics []int `json:"topics"`
}

type Api struct {
    DB *mgo.Database
}

func (api *Api) InitDB() {
    session, err := mgo.Dial("localhost")
    if err != nil {
        panic(err)
    }
    api.DB = session.DB("tweets")
}

func (api *Api) GetSession(writer rest.ResponseWriter, req *rest.Request) {
    session := req.PathParam("session")
    //TODO: filter already submitted sessions
    var result Session
    api.DB.C("sessions").Find(bson.M{"s_id": session}).One(&result)
    writer.WriteJson(result)
}

func (api *Api) GetTopic(writer rest.ResponseWriter, req *rest.Request) {
    id, err := strconv.Atoi(req.PathParam("topic"))
    if err != nil {
        rest.NotFound(writer, req)
        return
    }
    var result TweetList
    err = api.DB.C("topics").Find(bson.M{"t_id": id}).One(&result)
    if err != nil {
        rest.Error(writer, err.Error(), http.StatusInternalServerError)
        return
    }
    writer.WriteJson(result)
}

func (api *Api) SetSummary(writer rest.ResponseWriter, req *rest.Request) {
    var summary Summary
    if err := req.DecodeJsonPayload(&summary); err != nil {
        rest.NotFound(writer, req)
        return
    }
    // TODO: check if the topic belongs to the session
    err := api.DB.C("summaries").Insert(summary)
    if err != nil {
        log.Fatal(err)
    }
    writer.WriteHeader(http.StatusOK)
}
<commit_msg>Use go fmt to format go code<commit_after>package main

import (
	"github.com/ant0ine/go-json-rest/rest"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"log"
	"net/http"
	"strconv"
)

func main() {
	api := Api{}
	api.InitDB()

	handler := rest.ResourceHandler{}
	err := handler.SetRoutes(
		&rest.Route{"GET", "/api/:session", api.GetSession},
		&rest.Route{"GET", "/api/topic/:topic", api.GetTopic},
		&rest.Route{"POST", "/api/summary", api.SetSummary},
	)
	if err != nil {
		log.Fatal(err)
	}
	log.Fatal(http.ListenAndServe(":8080", &handler))
}

type Tweet struct {
	Id   string `json:"id"`
	Text string `json:"text"`
}

type TweetList struct {
	TopicId int      `json:"topic_id" bson:"t_id"`
	Topic   string   `json:"topic" bson:"t_name"`
	Desc    string   `json:"desc" bson:"desc"`
	Tweets  []*Tweet `json:"tweets"`
}

type Summary struct {
	TopicId   int      `json:"topic_id" bson:"t_id"`
	SessionId string   `json:"session_id" bson:"s_id"`
	Topic     string   `json:"topic" bson:"t_name"`
	Tweets    []*Tweet `json:"tweets"`
}

type Session struct {
	SessionId string `json:"session_id" bson:"s_id"`
	Topics    []int  `json:"topics"`
}

type Api struct {
	DB *mgo.Database
}

func (api *Api) InitDB() {
	session, err := mgo.Dial("localhost")
	if err != nil {
		panic(err)
	}
	api.DB = session.DB("tweets")
}

func (api *Api) GetSession(writer rest.ResponseWriter, req *rest.Request) {
	session := req.PathParam("session")
	//TODO: filter already submitted sessions
	var result Session
	api.DB.C("sessions").Find(bson.M{"s_id": session}).One(&result)
	writer.WriteJson(result)
}

func (api *Api) GetTopic(writer rest.ResponseWriter, req *rest.Request) {
	id, err := strconv.Atoi(req.PathParam("topic"))
	if err != nil {
		rest.NotFound(writer, req)
		return
	}
	var result TweetList
	err = api.DB.C("topics").Find(bson.M{"t_id": id}).One(&result)
	if err != nil {
		rest.Error(writer, err.Error(), http.StatusInternalServerError)
		return
	}
	writer.WriteJson(result)
}

func (api *Api) SetSummary(writer rest.ResponseWriter, req *rest.Request) {
	var summary Summary
	if err := req.DecodeJsonPayload(&summary); err != nil {
		rest.NotFound(writer, req)
		return
	}
	// TODO: check if the topic belongs to the session
	err := api.DB.C("summaries").Insert(summary)
	if err != nil {
		log.Fatal(err)
	}
	writer.WriteHeader(http.StatusOK)
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"net/http"

	"code.google.com/p/go.net/context"

	"github.com/livegrep/livegrep/client"
	"github.com/livegrep/livegrep/server/api"
	"github.com/livegrep/livegrep/server/backend"
	"github.com/livegrep/livegrep/server/log"
)

func replyJSON(ctx context.Context, w http.ResponseWriter, status int, obj interface{}) {
	w.WriteHeader(status)
	enc := json.NewEncoder(w)
	if err := enc.Encode(obj); err != nil {
		log.Printf(ctx, "writing http response, data=%s err=%s",
			asJSON{obj},
			err.Error())
	}
}

func writeError(ctx context.Context, w http.ResponseWriter, status int, code, message string) {
	log.Printf(ctx, "query error status=%d code=%s message=%s",
		status, code, message)
	replyJSON(ctx, w, status, &api.ReplyError{Err: api.InnerError{Code: code, Message: message}})
}

func writeQueryError(ctx context.Context, w http.ResponseWriter, err error) {
	if qe, ok := err.(client.QueryError); ok {
		writeError(ctx, w, 400, "query_error", qe.Err)
	} else {
		writeError(ctx, w, 500, "internal_error",
			fmt.Sprintf("Talking to backend: %s", err.Error()))
	}
	return
}

func parseQuery(r *http.Request) client.Query {
	params := r.URL.Query()
	return client.Query{
		Line:     params.Get("line"),
		File:     params.Get("file"),
		Repo:     params.Get("repo"),
		FoldCase: params.Get("fold_case") != "",
	}
}

const MaxRetries = 8

func (s *server) ServeAPISearch(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	backendName := r.URL.Query().Get(":backend")
	var backend *backend.Backend
	if backendName != "" {
		backend = s.bk[backendName]
		if backend == nil {
			writeError(ctx, w, 400, "bad_backend",
				fmt.Sprintf("Unknown backend: %s", backendName))
			return
		}
	} else {
		for _, backend = range s.bk {
			break
		}
	}

	q := parseQuery(r)

	if q.Line == "" {
		writeError(ctx, w, 400, "bad_query",
			"You must specify a 'line' regex.")
		return
	}

	var cl client.Client
	var search client.Search
	var err error

	for tries := 0; tries < MaxRetries; tries++ {
		select {
		case cl = <-backend.Clients:
		case <-ctx.Done():
			writeError(ctx, w, 500, "timed_out", "timed out talking to backend")
			return
		}
		defer backend.CheckIn(cl)

		search, err = cl.Query(&q)
		if err == nil {
			break
		}
		log.Printf(ctx,
			"error talking to backend try=%d err=%s", tries, err)
		if _, ok := err.(client.QueryError); ok {
			writeQueryError(ctx, w, err)
			return
		}
	}

	reply := &api.ReplySearch{Results: make([]*client.Result, 0)}

	for r := range search.Results() {
		reply.Results = append(reply.Results, r)
	}

	reply.Info, err = search.Close()
	if err != nil {
		writeQueryError(ctx, w, err)
		return
	}

	log.Printf(ctx,
		"responding success results=%d why=%s",
		len(reply.Results),
		reply.Info.ExitReason)

	replyJSON(ctx, w, 200, reply)
}
<commit_msg>Don't overload "query error"<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"net/http"

	"code.google.com/p/go.net/context"

	"github.com/livegrep/livegrep/client"
	"github.com/livegrep/livegrep/server/api"
	"github.com/livegrep/livegrep/server/backend"
	"github.com/livegrep/livegrep/server/log"
)

func replyJSON(ctx context.Context, w http.ResponseWriter, status int, obj interface{}) {
	w.WriteHeader(status)
	enc := json.NewEncoder(w)
	if err := enc.Encode(obj); err != nil {
		log.Printf(ctx, "writing http response, data=%s err=%s",
			asJSON{obj},
			err.Error())
	}
}

func writeError(ctx context.Context, w http.ResponseWriter, status int, code, message string) {
	log.Printf(ctx, "error status=%d code=%s message=%s",
		status, code, message)
	replyJSON(ctx, w, status, &api.ReplyError{Err: api.InnerError{Code: code, Message: message}})
}

func writeQueryError(ctx context.Context, w http.ResponseWriter, err error) {
	if qe, ok := err.(client.QueryError); ok {
		writeError(ctx, w, 400, "query_error", qe.Err)
	} else {
		writeError(ctx, w, 500, "internal_error",
			fmt.Sprintf("Talking to backend: %s", err.Error()))
	}
	return
}

func parseQuery(r *http.Request) client.Query {
	params := r.URL.Query()
	return client.Query{
		Line:     params.Get("line"),
		File:     params.Get("file"),
		Repo:     params.Get("repo"),
		FoldCase: params.Get("fold_case") != "",
	}
}

const MaxRetries = 8

func (s *server) ServeAPISearch(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	backendName := r.URL.Query().Get(":backend")
	var backend *backend.Backend
	if backendName != "" {
		backend = s.bk[backendName]
		if backend == nil {
			writeError(ctx, w, 400, "bad_backend",
				fmt.Sprintf("Unknown backend: %s", backendName))
			return
		}
	} else {
		for _, backend = range s.bk {
			break
		}
	}

	q := parseQuery(r)

	if q.Line == "" {
		writeError(ctx, w, 400, "bad_query",
			"You must specify a 'line' regex.")
		return
	}

	var cl client.Client
	var search client.Search
	var err error

	for tries := 0; tries < MaxRetries; tries++ {
		select {
		case cl = <-backend.Clients:
		case <-ctx.Done():
			writeError(ctx, w, 500, "timed_out", "timed out talking to backend")
			return
		}
		defer backend.CheckIn(cl)

		search, err = cl.Query(&q)
		if err == nil {
			break
		}
		log.Printf(ctx,
			"error talking to backend try=%d err=%s", tries, err)
		if _, ok := err.(client.QueryError); ok {
			writeQueryError(ctx, w, err)
			return
		}
	}

	reply := &api.ReplySearch{Results: make([]*client.Result, 0)}

	for r := range search.Results() {
		reply.Results = append(reply.Results, r)
	}

	reply.Info, err = search.Close()
	if err != nil {
		writeQueryError(ctx, w, err)
		return
	}

	log.Printf(ctx,
		"responding success results=%d why=%s",
		len(reply.Results),
		reply.Info.ExitReason)

	replyJSON(ctx, w, 200, reply)
}
<|endoftext|>


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

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/jacobsa/comeback/internal/blob"
	"github.com/jacobsa/comeback/internal/restore"

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

var cmdRestore = &Command{
	Name: "restore",
	Run:  runRestore,
}

func runRestore(ctx context.Context, args []string) (err error) {
	// Extract and parse arguments.
	if len(args) != 2 {
		err = fmt.Errorf("Usage: %s restore dst_dir score", os.Args[0])
		return
	}

	dstDir := args[0]
	score, err := blob.ParseHexScore(args[1])
	if err != nil {
		err = fmt.Errorf("ParseHexScore(%q): %v", args[1], err)
		return
	}

	// Grab dependencies.
	blobStore := getBlobStore(ctx)

	// Make sure the target doesn't exist.
	err = os.RemoveAll(dstDir)
	if err != nil {
		err = fmt.Errorf("os.RemoveAll: %v", err)
		return
	}

	// Create the destination.
	err = os.Mkdir(dstDir, 0700)
	if err != nil {
		err = fmt.Errorf("os.Mkdir: %v", err)
		return
	}

	// Attempt a restore.
	err = restore.Restore(
		ctx,
		dstDir,
		score,
		blobStore,
		log.New(os.Stderr, "Restore progress: ", 0),
	)

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

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

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/jacobsa/comeback/internal/blob"
	"github.com/jacobsa/comeback/internal/restore"

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

var cmdRestore = &Command{
	Name: "restore",
	Run:  runRestore,
}

func runRestore(ctx context.Context, args []string) (err error) {
	// Extract and parse arguments.
	if len(args) != 2 {
		err = fmt.Errorf("Usage: %s restore dst_dir score", os.Args[0])
		return
	}

	dstDir := args[0]
	score, err := blob.ParseHexScore(args[1])
	if err != nil {
		err = fmt.Errorf("ParseHexScore(%q): %v", args[1], err)
		return
	}

	// Grab dependencies.
	blobStore := getBlobStore(ctx)

	// Make sure the target doesn't exist.
	err = os.RemoveAll(dstDir)
	if err != nil {
		err = fmt.Errorf("os.RemoveAll: %v", err)
		return
	}

	// Create the destination.
	err = os.Mkdir(dstDir, 0700)
	if err != nil {
		err = fmt.Errorf("os.Mkdir: %v", err)
		return
	}

	// Attempt a restore.
	err = restore.Restore(
		ctx,
		dstDir,
		score,
		blobStore,
		log.New(os.Stderr, "Restore progress: ", 0),
	)

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

	log.Printf("Successfully restored to %s", dstDir)
	return
}
<|endoftext|>


<commit_before>//
// Author João Nuno.
//
// joaonrb@gmail.com
//
package lily

import (
	"github.com/valyala/fasthttp"
	"strings"
)

type Response struct {
	Status           int
	Headers          map[string]string
	Body             string
}

var (
	HttpError = func(status int) *Response { return &Response{Status: status, Body: fasthttp.StatusMessage(status)} }
	http400   = HttpError(fasthttp.StatusBadRequest)
	http404   = HttpError(fasthttp.StatusNotFound)
	http405   = HttpError(fasthttp.StatusMethodNotAllowed)
	http500   = HttpError(fasthttp.StatusInternalServerError)
	Http400   = func() *Response { return http400 }
	Http404   = func() *Response { return http404 }
	Http405   = func() *Response { return http405 }
	Http500   = func() *Response { return http500}
)

func sendResponse(ctx *fasthttp.RequestCtx, response *Response) {
	ctx.SetStatusCode(response.Status)
	for header, value := range response.Headers {
		ctx.Response.Header.Add(header, value)
	}
	ctx.SetBodyString(response.Body)
}

type IController interface {
	Handle(IController, *fasthttp.RequestCtx, map[string]string)
	Start(*fasthttp.RequestCtx, map[string]string)
	Finish(*Response)
	Get(*fasthttp.RequestCtx, map[string]string) *Response
	Head(*fasthttp.RequestCtx, map[string]string) *Response
	Post(*fasthttp.RequestCtx, map[string]string) *Response
	Put(*fasthttp.RequestCtx, map[string]string) *Response
	Patch(*fasthttp.RequestCtx, map[string]string) *Response
	Delete(*fasthttp.RequestCtx, map[string]string) *Response
	Trace(*fasthttp.RequestCtx, map[string]string) *Response
}

type BaseController struct {}

// Only touch Handle method if you understand what you are doing.
func (self *BaseController) Handle(controller IController, ctx *fasthttp.RequestCtx, args map[string]string) {
	controller.Start(ctx, args)
	var response *Response
	defer func() {
		if recovery := recover(); recovery != nil {
			Error("Unexpected error on call %s %s: %v", ctx.Method(), ctx.Path(), recovery)
			response = Http500()
		}
		controller.Finish(response)
		sendResponse(ctx, response)
	}()
	switch strings.ToUpper(ctx.Method()) {
	case "GET":
		response = controller.Get(ctx, args)
	case "POST":
		response = controller.Post(ctx, args)
	case "PUT":
		response = controller.Put(ctx, args)
	case "PATCH":
		response = controller.Patch(ctx, args)
	case "DELETE":
		response = controller.Delete(ctx, args)
	case "HEAD":
		response = controller.Head(ctx, args)
	case "TRACE":
		response = controller.Trace(ctx, args)
	default:
		response = Http405()
	}
}

func (self *BaseController) Start(*fasthttp.RequestCtx, map[string]string) {}

func (self *BaseController) Finish(*Response) {}

func (self *BaseController) Get(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Head(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Post(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Put(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Patch(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Delete(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Trace(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

<commit_msg>Fix string and byte array confusion<commit_after>//
// Author João Nuno.
//
// joaonrb@gmail.com
//
package lily

import (
	"github.com/valyala/fasthttp"
	"bytes"
	"strings"
)

type Response struct {
	Status           int
	Headers          map[string]string
	Body             string
}

var (
	HttpError = func(status int) *Response { return &Response{Status: status, Body: fasthttp.StatusMessage(status)} }
	http400   = HttpError(fasthttp.StatusBadRequest)
	http404   = HttpError(fasthttp.StatusNotFound)
	http405   = HttpError(fasthttp.StatusMethodNotAllowed)
	http500   = HttpError(fasthttp.StatusInternalServerError)
	Http400   = func() *Response { return http400 }
	Http404   = func() *Response { return http404 }
	Http405   = func() *Response { return http405 }
	Http500   = func() *Response { return http500}
)

func sendResponse(ctx *fasthttp.RequestCtx, response *Response) {
	ctx.SetStatusCode(response.Status)
	for header, value := range response.Headers {
		ctx.Response.Header.Add(header, value)
	}
	ctx.SetBodyString(response.Body)
}

type IController interface {
	Handle(IController, *fasthttp.RequestCtx, map[string]string)
	Start(*fasthttp.RequestCtx, map[string]string)
	Finish(*Response)
	Get(*fasthttp.RequestCtx, map[string]string) *Response
	Head(*fasthttp.RequestCtx, map[string]string) *Response
	Post(*fasthttp.RequestCtx, map[string]string) *Response
	Put(*fasthttp.RequestCtx, map[string]string) *Response
	Patch(*fasthttp.RequestCtx, map[string]string) *Response
	Delete(*fasthttp.RequestCtx, map[string]string) *Response
	Trace(*fasthttp.RequestCtx, map[string]string) *Response
}

type BaseController struct {}

// Only touch Handle method if you understand what you are doing.
func (self *BaseController) Handle(controller IController, ctx *fasthttp.RequestCtx, args map[string]string) {
	controller.Start(ctx, args)
	var response *Response
	defer func() {
		if recovery := recover(); recovery != nil {
			Error("Unexpected error on call %s %s: %v", ctx.Method(), ctx.Path(), recovery)
			response = Http500()
		}
		controller.Finish(response)
		sendResponse(ctx, response)
	}()
	switch string(bytes.ToUpper(ctx.Method())) {
	case "GET":
		response = controller.Get(ctx, args)
	case "POST":
		response = controller.Post(ctx, args)
	case "PUT":
		response = controller.Put(ctx, args)
	case "PATCH":
		response = controller.Patch(ctx, args)
	case "DELETE":
		response = controller.Delete(ctx, args)
	case "HEAD":
		response = controller.Head(ctx, args)
	case "TRACE":
		response = controller.Trace(ctx, args)
	default:
		response = Http405()
	}
}

func (self *BaseController) Start(*fasthttp.RequestCtx, map[string]string) {}

func (self *BaseController) Finish(*Response) {}

func (self *BaseController) Get(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Head(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Post(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Put(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Patch(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Delete(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

func (self *BaseController) Trace(request *fasthttp.Request, args map[string]string) *Response {
	return Http405()
}

<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"github.com/gin-gonic/gin"
	"net/http"
	"sync"

	"github.com/eirka/eirka-libs/config"
	"github.com/eirka/eirka-libs/db"
	e "github.com/eirka/eirka-libs/errors"
)

var (
	sitemap = make(map[string]*SiteData)
	mu      = new(sync.RWMutex)
)

// SiteData holds imageboard settings
type SiteData struct {
	Ib          uint
	Api         string
	Img         string
	Title       string
	Desc        string
	Nsfw        bool
	Style       string
	Logo        string
	Imageboards []Imageboard
}

type Imageboard struct {
	Title   string
	Address string
}

// Details gets the imageboard settings from the request for the page handler variables
func Details() gin.HandlerFunc {
	return func(c *gin.Context) {

		host := c.Request.Host

		mu.RLock()
		// check the sitemap to see if its cached
		site := sitemap[host]
		mu.RUnlock()

		// if not query the database
		if site == nil {

			sitedata := &SiteData{}

			// Get Database handle
			dbase, err := db.GetDb()
			if err != nil {
				c.JSON(e.ErrorMessage(e.ErrInternalError))
				c.Error(err).SetMeta("Details.GetDb")
				c.Abort()
				return
			}

			// get the info about the imageboard
			err = dbase.QueryRow(`SELECT ib_id,ib_title,ib_description,ib_nsfw,ib_api,ib_img,ib_style,ib_logo FROM imageboards WHERE ib_domain = ?`, host).Scan(&sitedata.Ib, &sitedata.Title, &sitedata.Desc, &sitedata.Nsfw, &sitedata.Api, &sitedata.Img, &sitedata.Style, &sitedata.Logo)
			if err == sql.ErrNoRows {
				c.JSON(e.ErrorMessage(e.ErrNotFound))
				c.Error(err).SetMeta("Details.QueryRow")
				return
			} else if err != nil {
				c.JSON(e.ErrorMessage(e.ErrInternalError))
				c.Error(err).SetMeta("Details.QueryRow")
				return
			}

			// collect the links to the other imageboards for nav menu
			rows, err := dbase.Query(`SELECT ib_title,ib_domain FROM imageboards WHERE ib_id != ?`, sitedata.Ib)
			if err != nil {
				c.JSON(e.ErrorMessage(e.ErrInternalError))
				c.Error(err).SetMeta("Details.Query")
				c.Abort()
				return
			}
			defer rows.Close()

			for rows.Next() {

				ib := Imageboard{}

				err := rows.Scan(&ib.Title, &ib.Address)
				if err != nil {
					return
				}

				sitedata.Imageboards = append(sitedata.Imageboards, ib)
			}
			if rows.Err() != nil {
				c.JSON(e.ErrorMessage(e.ErrInternalError))
				c.Error(err).SetMeta("Details.Query")
				c.Abort()
				return
			}

			mu.Lock()
			sitemap[host] = sitedata
			mu.Unlock()

		}

		c.Next()

	}

}

// IndexController generates pages for angularjs frontend
func IndexController(c *gin.Context) {

	// Get parameters from csrf middleware
	csrf_token := c.MustGet("csrf_token").(string)

	host := c.Request.Host

	mu.RLock()
	site := sitemap[host]
	mu.RUnlock()

	c.HTML(http.StatusOK, "index", gin.H{
		"primjs":      config.Settings.Prim.Js,
		"primcss":     config.Settings.Prim.Css,
		"ib":          site.Ib,
		"apisrv":      site.Api,
		"imgsrv":      site.Img,
		"title":       site.Title,
		"desc":        site.Desc,
		"nsfw":        site.Nsfw,
		"style":       site.Style,
		"logo":        site.Logo,
		"imageboards": site.Imageboards,
		"csrf":        csrf_token,
	})

	return

}

// ErrorController generates pages and a 404 response
func ErrorController(c *gin.Context) {

	// Get parameters from csrf middleware
	csrf_token := c.MustGet("csrf_token").(string)

	host := c.Request.Host

	mu.RLock()
	site := sitemap[host]
	mu.RUnlock()

	c.HTML(http.StatusNotFound, "index", gin.H{
		"primjs":      config.Settings.Prim.Js,
		"primcss":     config.Settings.Prim.Css,
		"ib":          site.Ib,
		"apisrv":      site.Api,
		"imgsrv":      site.Img,
		"title":       site.Title,
		"desc":        site.Desc,
		"nsfw":        site.Nsfw,
		"style":       site.Style,
		"logo":        site.Logo,
		"imageboards": site.Imageboards,
		"csrf":        csrf_token,
	})

	return

}
<commit_msg>debug<commit_after>package main

import (
	"database/sql"
	"github.com/gin-gonic/gin"
	"net/http"
	"sync"

	"github.com/eirka/eirka-libs/config"
	"github.com/eirka/eirka-libs/db"
	e "github.com/eirka/eirka-libs/errors"
)

var (
	sitemap = make(map[string]*SiteData)
	mu      = new(sync.RWMutex)
)

// SiteData holds imageboard settings
type SiteData struct {
	Ib          uint
	Api         string
	Img         string
	Title       string
	Desc        string
	Nsfw        bool
	Style       string
	Logo        string
	Imageboards []Imageboard
}

type Imageboard struct {
	Title   string
	Address string
}

// Details gets the imageboard settings from the request for the page handler variables
func Details() gin.HandlerFunc {
	return func(c *gin.Context) {

		host := c.Request.Host

		c.Error(host)

		mu.RLock()
		// check the sitemap to see if its cached
		site := sitemap[host]
		mu.RUnlock()

		// if not query the database
		if site == nil {

			sitedata := &SiteData{}

			// Get Database handle
			dbase, err := db.GetDb()
			if err != nil {
				c.JSON(e.ErrorMessage(e.ErrInternalError))
				c.Error(err).SetMeta("Details.GetDb")
				c.Abort()
				return
			}

			// get the info about the imageboard
			err = dbase.QueryRow(`SELECT ib_id,ib_title,ib_description,ib_nsfw,ib_api,ib_img,ib_style,ib_logo FROM imageboards WHERE ib_domain = ?`, host).Scan(&sitedata.Ib, &sitedata.Title, &sitedata.Desc, &sitedata.Nsfw, &sitedata.Api, &sitedata.Img, &sitedata.Style, &sitedata.Logo)
			if err == sql.ErrNoRows {
				c.JSON(e.ErrorMessage(e.ErrNotFound))
				c.Error(err).SetMeta("Details.QueryRow")
				return
			} else if err != nil {
				c.JSON(e.ErrorMessage(e.ErrInternalError))
				c.Error(err).SetMeta("Details.QueryRow")
				return
			}

			// collect the links to the other imageboards for nav menu
			rows, err := dbase.Query(`SELECT ib_title,ib_domain FROM imageboards WHERE ib_id != ?`, sitedata.Ib)
			if err != nil {
				c.JSON(e.ErrorMessage(e.ErrInternalError))
				c.Error(err).SetMeta("Details.Query")
				c.Abort()
				return
			}
			defer rows.Close()

			for rows.Next() {

				ib := Imageboard{}

				err := rows.Scan(&ib.Title, &ib.Address)
				if err != nil {
					return
				}

				sitedata.Imageboards = append(sitedata.Imageboards, ib)
			}
			if rows.Err() != nil {
				c.JSON(e.ErrorMessage(e.ErrInternalError))
				c.Error(err).SetMeta("Details.Query")
				c.Abort()
				return
			}

			mu.Lock()
			sitemap[host] = sitedata
			mu.Unlock()

		}

		c.Next()

	}

}

// IndexController generates pages for angularjs frontend
func IndexController(c *gin.Context) {

	// Get parameters from csrf middleware
	csrf_token := c.MustGet("csrf_token").(string)

	host := c.Request.Host

	mu.RLock()
	site := sitemap[host]
	mu.RUnlock()

	c.HTML(http.StatusOK, "index", gin.H{
		"primjs":      config.Settings.Prim.Js,
		"primcss":     config.Settings.Prim.Css,
		"ib":          site.Ib,
		"apisrv":      site.Api,
		"imgsrv":      site.Img,
		"title":       site.Title,
		"desc":        site.Desc,
		"nsfw":        site.Nsfw,
		"style":       site.Style,
		"logo":        site.Logo,
		"imageboards": site.Imageboards,
		"csrf":        csrf_token,
	})

	return

}

// ErrorController generates pages and a 404 response
func ErrorController(c *gin.Context) {

	// Get parameters from csrf middleware
	csrf_token := c.MustGet("csrf_token").(string)

	host := c.Request.Host

	mu.RLock()
	site := sitemap[host]
	mu.RUnlock()

	c.HTML(http.StatusNotFound, "index", gin.H{
		"primjs":      config.Settings.Prim.Js,
		"primcss":     config.Settings.Prim.Css,
		"ib":          site.Ib,
		"apisrv":      site.Api,
		"imgsrv":      site.Img,
		"title":       site.Title,
		"desc":        site.Desc,
		"nsfw":        site.Nsfw,
		"style":       site.Style,
		"logo":        site.Logo,
		"imageboards": site.Imageboards,
		"csrf":        csrf_token,
	})

	return

}
<|endoftext|>


<commit_before>package bild

import (
	"fmt"
	"image"
	"math"
)

// ConvolutionMatrix interface for use as an image Kernel.
type ConvolutionMatrix interface {
	At(x, y int) float64
	Normalized() ConvolutionMatrix
	Length() int
}

// NewKernel returns a kernel of the provided size.
func NewKernel(diameter int) *Kernel {
	matrix := make([][]float64, diameter)
	for i := 0; i < diameter; i++ {
		matrix[i] = make([]float64, diameter)
	}
	return &Kernel{matrix}
}

// Kernel is used as a convolution matrix.
type Kernel struct {
	Matrix [][]float64
}

// Normalized returns a new Kernel with normalized values.
func (k *Kernel) Normalized() ConvolutionMatrix {
	sum := absum(k)
	length := k.Length()
	nk := NewKernel(length)

	// avoid division by 0
	if sum == 0 {
		sum = 1
	}

	for x := 0; x < length; x++ {
		for y := 0; y < length; y++ {
			nk.Matrix[x][y] = k.Matrix[x][y] / sum
		}
	}

	return nk
}

// Length returns the row/column length for the kernel.
func (k *Kernel) Length() int {
	return len(k.Matrix)
}

// At returns the matrix value at position x, y.
func (k *Kernel) At(x, y int) float64 {
	return k.Matrix[x][y]
}

// String returns the string representation of the matrix.
func (k *Kernel) String() string {
	result := ""
	length := k.Length()
	for x := 0; x < length; x++ {
		result += fmt.Sprintf("\n")
		for y := 0; y < length; y++ {
			result += fmt.Sprintf("%-8.4f", k.Matrix[x][y])
		}
	}
	return result
}

// Convolute applies a convolution matrix (kernel) to an image.
// If wrap is set to true, indices outside of image dimensions will be taken from the opposite side,
// otherwise the pixel at that index will be skipped.
func Convolute(img image.Image, k ConvolutionMatrix, bias float64, wrap bool) *image.RGBA {
	bounds := img.Bounds()
	src := CloneAsRGBA(img)
	dst := image.NewRGBA(bounds)

	w, h := bounds.Max.X, bounds.Max.Y
	kernelLength := k.Length()

	parallelize(h, func(start, end int) {
		for x := 0; x < w; x++ {
			for y := start; y < end; y++ {

				var r, g, b, a float64
				for kx := 0; kx < kernelLength; kx++ {
					for ky := 0; ky < kernelLength; ky++ {
						var ix, iy int
						if wrap {
							ix = (x - kernelLength/2 + kx + w) % w
							iy = (y - kernelLength/2 + ky + h) % h
						} else {
							ix = x - kernelLength/2 + kx
							iy = y - kernelLength/2 + ky

							if ix < 0 || ix >= w || iy < 0 || iy >= h {
								continue
							}
						}

						ipos := iy*dst.Stride + ix*4
						kvalue := k.At(kx, ky)
						r += float64(src.Pix[ipos+0]) * kvalue
						g += float64(src.Pix[ipos+1]) * kvalue
						b += float64(src.Pix[ipos+2]) * kvalue
						a += float64(src.Pix[ipos+3]) * kvalue
					}
				}

				pos := y*dst.Stride + x*4

				dst.Pix[pos+0] = uint8(math.Max(math.Min(r+bias, 255), 0))
				dst.Pix[pos+1] = uint8(math.Max(math.Min(g+bias, 255), 0))
				dst.Pix[pos+2] = uint8(math.Max(math.Min(b+bias, 255), 0))
				dst.Pix[pos+3] = uint8(math.Max(math.Min(a+bias, 255), 0))
			}
		}
	})

	return dst
}

// absum returns the absolute cumulative value of the matrix.
func absum(k *Kernel) float64 {
	var sum float64
	length := k.Length()
	for x := 0; x < length; x++ {
		for y := 0; y < length; y++ {
			sum += math.Abs(k.Matrix[x][y])
		}
	}
	return sum
}
<commit_msg>switched to carry alpha<commit_after>package bild

import (
	"fmt"
	"image"
	"math"
)

// ConvolutionMatrix interface for use as an image Kernel.
type ConvolutionMatrix interface {
	At(x, y int) float64
	Normalized() ConvolutionMatrix
	Length() int
}

// NewKernel returns a kernel of the provided size.
func NewKernel(diameter int) *Kernel {
	matrix := make([][]float64, diameter)
	for i := 0; i < diameter; i++ {
		matrix[i] = make([]float64, diameter)
	}
	return &Kernel{matrix}
}

// Kernel is used as a convolution matrix.
type Kernel struct {
	Matrix [][]float64
}

// Normalized returns a new Kernel with normalized values.
func (k *Kernel) Normalized() ConvolutionMatrix {
	sum := absum(k)
	length := k.Length()
	nk := NewKernel(length)

	// avoid division by 0
	if sum == 0 {
		sum = 1
	}

	for x := 0; x < length; x++ {
		for y := 0; y < length; y++ {
			nk.Matrix[x][y] = k.Matrix[x][y] / sum
		}
	}

	return nk
}

// Length returns the row/column length for the kernel.
func (k *Kernel) Length() int {
	return len(k.Matrix)
}

// At returns the matrix value at position x, y.
func (k *Kernel) At(x, y int) float64 {
	return k.Matrix[x][y]
}

// String returns the string representation of the matrix.
func (k *Kernel) String() string {
	result := ""
	length := k.Length()
	for x := 0; x < length; x++ {
		result += fmt.Sprintf("\n")
		for y := 0; y < length; y++ {
			result += fmt.Sprintf("%-8.4f", k.Matrix[x][y])
		}
	}
	return result
}

// Convolute applies a convolution matrix (kernel) to an image.
// If wrap is set to true, indices outside of image dimensions will be taken from the opposite side,
// otherwise the pixel at that index will be skipped.
func Convolute(img image.Image, k ConvolutionMatrix, bias float64, wrap bool) *image.RGBA {
	bounds := img.Bounds()
	src := CloneAsRGBA(img)
	dst := image.NewRGBA(bounds)

	w, h := bounds.Max.X, bounds.Max.Y
	kernelLength := k.Length()

	parallelize(h, func(start, end int) {
		for x := 0; x < w; x++ {
			for y := start; y < end; y++ {

				var r, g, b float64
				for kx := 0; kx < kernelLength; kx++ {
					for ky := 0; ky < kernelLength; ky++ {
						var ix, iy int
						if wrap {
							ix = (x - kernelLength/2 + kx + w) % w
							iy = (y - kernelLength/2 + ky + h) % h
						} else {
							ix = x - kernelLength/2 + kx
							iy = y - kernelLength/2 + ky

							if ix < 0 || ix >= w || iy < 0 || iy >= h {
								continue
							}
						}

						ipos := iy*dst.Stride + ix*4
						kvalue := k.At(kx, ky)
						r += float64(src.Pix[ipos+0]) * kvalue
						g += float64(src.Pix[ipos+1]) * kvalue
						b += float64(src.Pix[ipos+2]) * kvalue
					}
				}

				pos := y*dst.Stride + x*4

				dst.Pix[pos+0] = uint8(math.Max(math.Min(r+bias, 255), 0))
				dst.Pix[pos+1] = uint8(math.Max(math.Min(g+bias, 255), 0))
				dst.Pix[pos+2] = uint8(math.Max(math.Min(b+bias, 255), 0))
				dst.Pix[pos+3] = src.Pix[pos+3]
			}
		}
	})

	return dst
}

// absum returns the absolute cumulative value of the matrix.
func absum(k *Kernel) float64 {
	var sum float64
	length := k.Length()
	for x := 0; x < length; x++ {
		for y := 0; y < length; y++ {
			sum += math.Abs(k.Matrix[x][y])
		}
	}
	return sum
}
<|endoftext|>


<commit_before>package cienv

import (
	"os"
	"reflect"
	"testing"
)

func setupEnvs() (cleanup func()) {
	var cleanEnvs = []string{
		"TRAVIS_PULL_REQUEST",
		"TRAVIS_REPO_SLUG",
		"TRAVIS_PULL_REQUEST_SHA",
		"CIRCLE_PR_NUMBER",
		"CIRCLE_PROJECT_USERNAME",
		"CIRCLE_PROJECT_REPONAME",
		"CIRCLE_SHA1",
		"DRONE_PULL_REQUEST",
		"DRONE_REPO",
		"DRONE_REPO_OWNER",
		"DRONE_REPO_NAME",
		"DRONE_COMMIT",
		"CI_PULL_REQUEST",
		"CI_COMMIT",
		"CI_REPO_OWNER",
		"CI_REPO_NAME",
	}
	saveEnvs := make(map[string]string)
	for _, key := range cleanEnvs {
		saveEnvs[key] = os.Getenv(key)
		os.Unsetenv(key)
	}
	return func() {
		for key, value := range saveEnvs {
			os.Setenv(key, value)
		}
	}
}

func TestGetPullRequestInfo_travis(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	_, isPR, err := GetPullRequestInfo()
	if err != nil {
		t.Errorf("got unexpected error: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "str")

	_, isPR, err = GetPullRequestInfo()
	if err != nil {
		t.Errorf("got unexpected error: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "1")

	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("TRAVIS_REPO_SLUG", "invalid repo slug")

	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("TRAVIS_REPO_SLUG", "haya14busa/reviewdog")

	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("TRAVIS_PULL_REQUEST_SHA", "sha")

	_, isPR, err = GetPullRequestInfo()
	if err != nil {
		t.Errorf("got unexpected err: %v", err)
	}
	if !isPR {
		t.Errorf("isPR = %v, want true", isPR)
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "false")

	_, isPR, err = GetPullRequestInfo()
	if err != nil {
		t.Errorf("got unexpected err: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}
}

func TestGetPullRequestInfo_circleci(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetPullRequestInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("CIRCLE_PR_NUMBER", "1")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_PROJECT_USERNAME", "haya14busa")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_PROJECT_REPONAME", "reviewdog")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_SHA1", "sha1")
	g, isPR, err := GetPullRequestInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &PullRequestInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}

func TestGetPullRequestInfo_droneio(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetPullRequestInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("DRONE_PULL_REQUEST", "1")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	// Drone <= 0.4 without valid repo
	os.Setenv("DRONE_REPO", "invalid")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}
	os.Unsetenv("DRONE_REPO")

	// Drone > 0.4 without DRONE_REPO_NAME
	os.Setenv("DRONE_REPO_OWNER", "haya14busa")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}
	os.Unsetenv("DRONE_REPO_OWNER")

	// Drone > 0.4 without DRONE_REPO_OWNER
	os.Setenv("DRONE_REPO_NAME", "reviewdog")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	// Drone > 0.4 have valid variables
	os.Setenv("DRONE_REPO_NAME", "reviewdog")
	os.Setenv("DRONE_REPO_OWNER", "haya14busa")

	os.Setenv("DRONE_COMMIT", "sha1")
	g, isPR, err := GetPullRequestInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &PullRequestInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}

func TestGetPullRequestInfo_common(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetPullRequestInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("CI_PULL_REQUEST", "1")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_REPO_OWNER", "haya14busa")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_REPO_NAME", "reviewdog")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_COMMIT", "sha1")
	g, isPR, err := GetPullRequestInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &PullRequestInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}
<commit_msg>fix test<commit_after>package cienv

import (
	"os"
	"reflect"
	"testing"
)

func setupEnvs() (cleanup func()) {
	var cleanEnvs = []string{
		"TRAVIS_PULL_REQUEST",
		"TRAVIS_REPO_SLUG",
		"TRAVIS_PULL_REQUEST_SHA",
		"CIRCLE_PR_NUMBER",
		"CIRCLE_PROJECT_USERNAME",
		"CIRCLE_PROJECT_REPONAME",
		"CIRCLE_SHA1",
		"DRONE_PULL_REQUEST",
		"DRONE_REPO",
		"DRONE_REPO_OWNER",
		"DRONE_REPO_NAME",
		"DRONE_COMMIT",
		"CI_PULL_REQUEST",
		"CI_COMMIT",
		"CI_REPO_OWNER",
		"CI_REPO_NAME",
		"CI_BRANCH",
		"TRAVIS_PULL_REQUEST_BRANCH",
		"CIRCLE_BRANCH",
		"DRONE_COMMIT_BRANCH",
	}
	saveEnvs := make(map[string]string)
	for _, key := range cleanEnvs {
		saveEnvs[key] = os.Getenv(key)
		os.Unsetenv(key)
	}
	return func() {
		for key, value := range saveEnvs {
			os.Setenv(key, value)
		}
	}
}

func TestGetPullRequestInfo_travis(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	_, isPR, err := GetPullRequestInfo()
	if err != nil {
		t.Errorf("got unexpected error: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "str")

	_, isPR, err = GetPullRequestInfo()
	if err != nil {
		t.Errorf("got unexpected error: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "1")

	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("TRAVIS_REPO_SLUG", "invalid repo slug")

	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("TRAVIS_REPO_SLUG", "haya14busa/reviewdog")

	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("TRAVIS_PULL_REQUEST_SHA", "sha")

	_, isPR, err = GetPullRequestInfo()
	if err != nil {
		t.Errorf("got unexpected err: %v", err)
	}
	if !isPR {
		t.Errorf("isPR = %v, want true", isPR)
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "false")

	_, isPR, err = GetPullRequestInfo()
	if err != nil {
		t.Errorf("got unexpected err: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}
}

func TestGetPullRequestInfo_circleci(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetPullRequestInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("CIRCLE_PR_NUMBER", "1")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_PROJECT_USERNAME", "haya14busa")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_PROJECT_REPONAME", "reviewdog")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_SHA1", "sha1")
	g, isPR, err := GetPullRequestInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &PullRequestInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}

func TestGetPullRequestInfo_droneio(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetPullRequestInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("DRONE_PULL_REQUEST", "1")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	// Drone <= 0.4 without valid repo
	os.Setenv("DRONE_REPO", "invalid")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}
	os.Unsetenv("DRONE_REPO")

	// Drone > 0.4 without DRONE_REPO_NAME
	os.Setenv("DRONE_REPO_OWNER", "haya14busa")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}
	os.Unsetenv("DRONE_REPO_OWNER")

	// Drone > 0.4 without DRONE_REPO_OWNER
	os.Setenv("DRONE_REPO_NAME", "reviewdog")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	// Drone > 0.4 have valid variables
	os.Setenv("DRONE_REPO_NAME", "reviewdog")
	os.Setenv("DRONE_REPO_OWNER", "haya14busa")

	os.Setenv("DRONE_COMMIT", "sha1")
	g, isPR, err := GetPullRequestInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &PullRequestInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}

func TestGetPullRequestInfo_common(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetPullRequestInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("CI_PULL_REQUEST", "1")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_REPO_OWNER", "haya14busa")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_REPO_NAME", "reviewdog")
	if _, _, err := GetPullRequestInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_COMMIT", "sha1")
	g, isPR, err := GetPullRequestInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &PullRequestInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"bufio"
	"errors"
	"github.com/siddontang/go-log/log"
	"github.com/siddontang/ledisdb/ledis"
	"io"
	"net"
	"runtime"
	"strconv"
	"strings"
)

var errReadRequest = errors.New("invalid request protocol")

type respClient struct {
	app *App
	ldb *ledis.Ledis
	db  *ledis.DB

	conn net.Conn
	rb   *bufio.Reader

	req *requestContext
}

type respWriter struct {
	buff *bufio.Writer
}

func newClientRESP(conn net.Conn, app *App) {
	c := new(respClient)

	c.app = app
	c.conn = conn
	c.ldb = app.ldb
	c.db, _ = app.ldb.Select(0)

	c.rb = bufio.NewReaderSize(conn, 256)

	c.req = newRequestContext(app)
	c.req.resp = newWriterRESP(conn)
	c.req.remoteAddr = conn.RemoteAddr().String()

	go c.run()
}

func (c *respClient) run() {
	defer func() {
		if e := recover(); e != nil {
			buf := make([]byte, 4096)
			n := runtime.Stack(buf, false)
			buf = buf[0:n]

			log.Fatal("client run panic %s:%v", buf, e)
		}

		c.conn.Close()
	}()

	for {
		reqData, err := c.readRequest()
		if err != nil {
			return
		}

		c.handleRequest(reqData)
	}
}

func (c *respClient) readLine() ([]byte, error) {
	return ReadLine(c.rb)
}

//A client sends to the Redis server a RESP Array consisting of just Bulk Strings.
func (c *respClient) readRequest() ([][]byte, error) {
	l, err := c.readLine()
	if err != nil {
		return nil, err
	} else if len(l) == 0 || l[0] != '*' {
		return nil, errReadRequest
	}

	var nparams int
	if nparams, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
		return nil, err
	} else if nparams <= 0 {
		return nil, errReadRequest
	}

	req := make([][]byte, 0, nparams)
	var n int
	for i := 0; i < nparams; i++ {
		if l, err = c.readLine(); err != nil {
			return nil, err
		}

		if len(l) == 0 {
			return nil, errReadRequest
		} else if l[0] == '$' {
			//handle resp string
			if n, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
				return nil, err
			} else if n == -1 {
				req = append(req, nil)
			} else {
				buf := make([]byte, n)
				if _, err = io.ReadFull(c.rb, buf); err != nil {
					return nil, err
				}

				if l, err = c.readLine(); err != nil {
					return nil, err
				} else if len(l) != 0 {
					return nil, errors.New("bad bulk string format")
				}

				req = append(req, buf)

			}

		} else {
			return nil, errReadRequest
		}
	}

	return req, nil
}

func (c *respClient) handleRequest(reqData [][]byte) {
	req := c.req

	if len(reqData) == 0 {
		c.req.cmd = ""
		c.req.args = reqData[0:0]
	} else {
		c.req.cmd = strings.ToLower(ledis.String(reqData[0]))
		c.req.args = reqData[1:]
	}
	if c.req.cmd == "quit" {
		c.req.resp.writeStatus(OK)
		c.req.resp.flush()
		c.conn.Close()
	}

	req.db = c.db

	c.req.perform()

	c.db = req.db // "SELECT"

	return
}

//	response writer

func newWriterRESP(conn net.Conn) *respWriter {
	w := new(respWriter)
	w.buff = bufio.NewWriterSize(conn, 256)
	return w
}

func (w *respWriter) writeError(err error) {
	w.buff.Write(ledis.Slice("-ERR"))
	if err != nil {
		w.buff.WriteByte(' ')
		w.buff.Write(ledis.Slice(err.Error()))
	}
	w.buff.Write(Delims)
}

func (w *respWriter) writeStatus(status string) {
	w.buff.WriteByte('+')
	w.buff.Write(ledis.Slice(status))
	w.buff.Write(Delims)
}

func (w *respWriter) writeInteger(n int64) {
	w.buff.WriteByte(':')
	w.buff.Write(ledis.StrPutInt64(n))
	w.buff.Write(Delims)
}

func (w *respWriter) writeBulk(b []byte) {
	w.buff.WriteByte('$')
	if b == nil {
		w.buff.Write(NullBulk)
	} else {
		w.buff.Write(ledis.Slice(strconv.Itoa(len(b))))
		w.buff.Write(Delims)
		w.buff.Write(b)
	}

	w.buff.Write(Delims)
}

func (w *respWriter) writeArray(lst []interface{}) {
	w.buff.WriteByte('*')
	if lst == nil {
		w.buff.Write(NullArray)
		w.buff.Write(Delims)
	} else {
		w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
		w.buff.Write(Delims)

		for i := 0; i < len(lst); i++ {
			switch v := lst[i].(type) {
			case []interface{}:
				w.writeArray(v)
			case []byte:
				w.writeBulk(v)
			case nil:
				w.writeBulk(nil)
			case int64:
				w.writeInteger(v)
			default:
				panic("invalid array type")
			}
		}
	}
}

func (w *respWriter) writeSliceArray(lst [][]byte) {
	w.buff.WriteByte('*')
	if lst == nil {
		w.buff.Write(NullArray)
		w.buff.Write(Delims)
	} else {
		w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
		w.buff.Write(Delims)

		for i := 0; i < len(lst); i++ {
			w.writeBulk(lst[i])
		}
	}
}

func (w *respWriter) writeFVPairArray(lst []ledis.FVPair) {
	w.buff.WriteByte('*')
	if lst == nil {
		w.buff.Write(NullArray)
		w.buff.Write(Delims)
	} else {
		w.buff.Write(ledis.Slice(strconv.Itoa(len(lst) * 2)))
		w.buff.Write(Delims)

		for i := 0; i < len(lst); i++ {
			w.writeBulk(lst[i].Field)
			w.writeBulk(lst[i].Value)
		}
	}
}

func (w *respWriter) writeScorePairArray(lst []ledis.ScorePair, withScores bool) {
	w.buff.WriteByte('*')
	if lst == nil {
		w.buff.Write(NullArray)
		w.buff.Write(Delims)
	} else {
		if withScores {
			w.buff.Write(ledis.Slice(strconv.Itoa(len(lst) * 2)))
			w.buff.Write(Delims)
		} else {
			w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
			w.buff.Write(Delims)

		}

		for i := 0; i < len(lst); i++ {
			w.writeBulk(lst[i].Member)

			if withScores {
				w.writeBulk(ledis.StrPutInt64(lst[i].Score))
			}
		}
	}
}

func (w *respWriter) writeBulkFrom(n int64, rb io.Reader) {
	w.buff.WriteByte('$')
	w.buff.Write(ledis.Slice(strconv.FormatInt(n, 10)))
	w.buff.Write(Delims)

	io.Copy(w.buff, rb)
	w.buff.Write(Delims)
}

func (w *respWriter) flush() {
	w.buff.Flush()
}
<commit_msg>add return statement<commit_after>package server

import (
	"bufio"
	"errors"
	"github.com/siddontang/go-log/log"
	"github.com/siddontang/ledisdb/ledis"
	"io"
	"net"
	"runtime"
	"strconv"
	"strings"
)

var errReadRequest = errors.New("invalid request protocol")

type respClient struct {
	app *App
	ldb *ledis.Ledis
	db  *ledis.DB

	conn net.Conn
	rb   *bufio.Reader

	req *requestContext
}

type respWriter struct {
	buff *bufio.Writer
}

func newClientRESP(conn net.Conn, app *App) {
	c := new(respClient)

	c.app = app
	c.conn = conn
	c.ldb = app.ldb
	c.db, _ = app.ldb.Select(0)

	c.rb = bufio.NewReaderSize(conn, 256)

	c.req = newRequestContext(app)
	c.req.resp = newWriterRESP(conn)
	c.req.remoteAddr = conn.RemoteAddr().String()

	go c.run()
}

func (c *respClient) run() {
	defer func() {
		if e := recover(); e != nil {
			buf := make([]byte, 4096)
			n := runtime.Stack(buf, false)
			buf = buf[0:n]

			log.Fatal("client run panic %s:%v", buf, e)
		}

		c.conn.Close()
	}()

	for {
		reqData, err := c.readRequest()
		if err != nil {
			return
		}

		c.handleRequest(reqData)
	}
}

func (c *respClient) readLine() ([]byte, error) {
	return ReadLine(c.rb)
}

//A client sends to the Redis server a RESP Array consisting of just Bulk Strings.
func (c *respClient) readRequest() ([][]byte, error) {
	l, err := c.readLine()
	if err != nil {
		return nil, err
	} else if len(l) == 0 || l[0] != '*' {
		return nil, errReadRequest
	}

	var nparams int
	if nparams, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
		return nil, err
	} else if nparams <= 0 {
		return nil, errReadRequest
	}

	req := make([][]byte, 0, nparams)
	var n int
	for i := 0; i < nparams; i++ {
		if l, err = c.readLine(); err != nil {
			return nil, err
		}

		if len(l) == 0 {
			return nil, errReadRequest
		} else if l[0] == '$' {
			//handle resp string
			if n, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
				return nil, err
			} else if n == -1 {
				req = append(req, nil)
			} else {
				buf := make([]byte, n)
				if _, err = io.ReadFull(c.rb, buf); err != nil {
					return nil, err
				}

				if l, err = c.readLine(); err != nil {
					return nil, err
				} else if len(l) != 0 {
					return nil, errors.New("bad bulk string format")
				}

				req = append(req, buf)

			}

		} else {
			return nil, errReadRequest
		}
	}

	return req, nil
}

func (c *respClient) handleRequest(reqData [][]byte) {
	req := c.req

	if len(reqData) == 0 {
		c.req.cmd = ""
		c.req.args = reqData[0:0]
	} else {
		c.req.cmd = strings.ToLower(ledis.String(reqData[0]))
		c.req.args = reqData[1:]
	}
	if c.req.cmd == "quit" {
		c.req.resp.writeStatus(OK)
		c.req.resp.flush()
		c.conn.Close()
		return
	}

	req.db = c.db

	c.req.perform()

	c.db = req.db // "SELECT"

	return
}

//	response writer

func newWriterRESP(conn net.Conn) *respWriter {
	w := new(respWriter)
	w.buff = bufio.NewWriterSize(conn, 256)
	return w
}

func (w *respWriter) writeError(err error) {
	w.buff.Write(ledis.Slice("-ERR"))
	if err != nil {
		w.buff.WriteByte(' ')
		w.buff.Write(ledis.Slice(err.Error()))
	}
	w.buff.Write(Delims)
}

func (w *respWriter) writeStatus(status string) {
	w.buff.WriteByte('+')
	w.buff.Write(ledis.Slice(status))
	w.buff.Write(Delims)
}

func (w *respWriter) writeInteger(n int64) {
	w.buff.WriteByte(':')
	w.buff.Write(ledis.StrPutInt64(n))
	w.buff.Write(Delims)
}

func (w *respWriter) writeBulk(b []byte) {
	w.buff.WriteByte('$')
	if b == nil {
		w.buff.Write(NullBulk)
	} else {
		w.buff.Write(ledis.Slice(strconv.Itoa(len(b))))
		w.buff.Write(Delims)
		w.buff.Write(b)
	}

	w.buff.Write(Delims)
}

func (w *respWriter) writeArray(lst []interface{}) {
	w.buff.WriteByte('*')
	if lst == nil {
		w.buff.Write(NullArray)
		w.buff.Write(Delims)
	} else {
		w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
		w.buff.Write(Delims)

		for i := 0; i < len(lst); i++ {
			switch v := lst[i].(type) {
			case []interface{}:
				w.writeArray(v)
			case []byte:
				w.writeBulk(v)
			case nil:
				w.writeBulk(nil)
			case int64:
				w.writeInteger(v)
			default:
				panic("invalid array type")
			}
		}
	}
}

func (w *respWriter) writeSliceArray(lst [][]byte) {
	w.buff.WriteByte('*')
	if lst == nil {
		w.buff.Write(NullArray)
		w.buff.Write(Delims)
	} else {
		w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
		w.buff.Write(Delims)

		for i := 0; i < len(lst); i++ {
			w.writeBulk(lst[i])
		}
	}
}

func (w *respWriter) writeFVPairArray(lst []ledis.FVPair) {
	w.buff.WriteByte('*')
	if lst == nil {
		w.buff.Write(NullArray)
		w.buff.Write(Delims)
	} else {
		w.buff.Write(ledis.Slice(strconv.Itoa(len(lst) * 2)))
		w.buff.Write(Delims)

		for i := 0; i < len(lst); i++ {
			w.writeBulk(lst[i].Field)
			w.writeBulk(lst[i].Value)
		}
	}
}

func (w *respWriter) writeScorePairArray(lst []ledis.ScorePair, withScores bool) {
	w.buff.WriteByte('*')
	if lst == nil {
		w.buff.Write(NullArray)
		w.buff.Write(Delims)
	} else {
		if withScores {
			w.buff.Write(ledis.Slice(strconv.Itoa(len(lst) * 2)))
			w.buff.Write(Delims)
		} else {
			w.buff.Write(ledis.Slice(strconv.Itoa(len(lst))))
			w.buff.Write(Delims)

		}

		for i := 0; i < len(lst); i++ {
			w.writeBulk(lst[i].Member)

			if withScores {
				w.writeBulk(ledis.StrPutInt64(lst[i].Score))
			}
		}
	}
}

func (w *respWriter) writeBulkFrom(n int64, rb io.Reader) {
	w.buff.WriteByte('$')
	w.buff.Write(ledis.Slice(strconv.FormatInt(n, 10)))
	w.buff.Write(Delims)

	io.Copy(w.buff, rb)
	w.buff.Write(Delims)
}

func (w *respWriter) flush() {
	w.buff.Flush()
}
<|endoftext|>


<commit_before>package query

import (
	"errors"
)

// Constructor for row selectors; individual RowSelectors should only
// be used in a single threaded context.
type MakeRowSelector func() RowSelector

// A RowSelector extracts the next value from an sql result set
type RowSelector interface {
	Scan(src RowScanner) (interface{}, error)
}

// common interface between sql.Row and sql.Rows
type RowScanner interface {
	Scan(res ...interface{}) error
}

// CompileQuery compiles a query to sql.
// Returns the compiled sql query and a selector constructor for extracting
// values from an sql.Rows result set (or a single Row)
func CompileQuery(q *Query) (string, MakeRowSelector, error) {
	return "", nil, errors.New("CompileQuery: Implement Me!")
}
<commit_msg>query: remove RowSelector from interface, keep it simple for now.<commit_after>package query

import (
	"errors"
)

// A RowSelector extracts the next value from an sql result set
type RowSelector interface {
	Scan(src RowScanner) (interface{}, error)
}

// Common Scan interface between sql.Row and sql.Rows
type RowScanner interface {
	Scan(res ...interface{}) error
}

// CompileQuery compiles a query to sql.
// Returns the compiled sql query and a selector for extracting
// values from an sql result set
// Note: The row selector should be used in single-threaded context
func CompileQuery(q *Query) (string, RowSelector, error) {
	return "", nil, errors.New("CompileQuery: Implement Me!")
}
<|endoftext|>