<commit_before>package login

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/tarent/loginsrv/logging"
	"github.com/tarent/loginsrv/model"
	"github.com/tarent/loginsrv/oauth2"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

const contentTypeHTML = "text/html; charset=utf-8"
const contentTypeJWT = "application/jwt"
const contentTypePlain = "text/plain"

// Handler is the mail login handler.
// It serves the login ressource and does the authentication against the backends or oauth provider.
type Handler struct {
	backends []Backend
	oauth    oauthManager
	config   *Config
}

// NewHandler creates a login handler based on the supplied configuration.
func NewHandler(config *Config) (*Handler, error) {
	if len(config.Backends) == 0 && len(config.Oauth) == 0 {
		return nil, errors.New("No login backends or oauth provider configured")
	}

	backends := []Backend{}
	for pName, opts := range config.Backends {
		p, exist := GetProvider(pName)
		if !exist {
			return nil, fmt.Errorf("No such provider: %v", pName)
		}
		b, err := p(opts)
		if err != nil {
			return nil, err
		}
		backends = append(backends, b)
	}

	oauth := oauth2.NewManager()
	for providerName, opts := range config.Oauth {
		err := oauth.AddConfig(providerName, opts)
		if err != nil {
			return nil, err
		}
	}

	return &Handler{
		backends: backends,
		config:   config,
		oauth:    oauth,
	}, nil
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !strings.HasPrefix(r.URL.Path, h.config.LoginPath) {
		h.respondNotFound(w, r)
		return
	}

	_, err := h.oauth.GetConfigFromRequest(r)
	if err == nil {
		h.handleOauth(w, r)
		return
	}

	h.handleLogin(w, r)
	return
}

func (h *Handler) handleOauth(w http.ResponseWriter, r *http.Request) {
	startedFlow, authenticated, userInfo, err := h.oauth.Handle(w, r)

	if startedFlow {
		// the oauth flow started
		return
	}

	if err != nil {
		logging.Application(r.Header).WithError(err).Error()
		h.respondError(w, r)
		return
	}

	if authenticated {
		logging.Application(r.Header).
			WithField("username", userInfo.Sub).Info("successfully authenticated")
		h.respondAuthenticated(w, r, userInfo)
		return
	}
	logging.Application(r.Header).
		WithField("username", userInfo.Sub).Info("failed authentication")

	h.respondAuthFailure(w, r)
	return
}

func (h *Handler) handleLogin(w http.ResponseWriter, r *http.Request) {
	contentType := r.Header.Get("Content-Type")
	if !(r.Method == "GET" || r.Method == "DELETE" ||
		(r.Method == "POST" &&
			(strings.HasPrefix(contentType, "application/json") ||
				strings.HasPrefix(contentType, "application/x-www-form-urlencoded") ||
				strings.HasPrefix(contentType, contentTypeJWT) ||
				strings.HasPrefix(contentType, "multipart/form-data")))) {
		h.respondBadRequest(w, r)
		return
	}

	r.ParseForm()
	if r.Method == "DELETE" || r.FormValue("logout") == "true" {
		h.deleteToken(w)
		if h.config.LogoutURL != "" {
			w.Header().Set("Location", h.config.LogoutURL)
			w.WriteHeader(303)
			return
		}
		writeLoginForm(w,
			loginFormData{
				Config: h.config,
			})
		return
	}

	if r.Method == "GET" {
		userInfo, valid := h.getToken(r)
		writeLoginForm(w,
			loginFormData{
				Config:        h.config,
				Authenticated: valid,
				UserInfo:      userInfo,
			})
		return
	}

	if r.Method == "POST" {
		// Check if we got a valid jwt with the post (refresh)
		userInfo, valid := h.getToken(r)

		// No token found, assuming new authentication
		if userInfo.Sub == "" && userInfo.Expiry == 0 {
			h.handleAuthentication(w, r)
			return
		}

		// Refresh the jwt if it is valid
		h.handleRefresh(w, r, userInfo, valid)
		return
	}
}

func (h *Handler) handleAuthentication(w http.ResponseWriter, r *http.Request) {
	username, password, err := getCredentials(r)
	if err != nil {
		h.respondBadRequest(w, r)
		return
	}
	authenticated, userInfo, err := h.authenticate(username, password)
	if err != nil {
		logging.Application(r.Header).WithError(err).Error()
		h.respondError(w, r)
		return
	}

	if authenticated {
		logging.Application(r.Header).
			WithField("username", username).Info("successfully authenticated")
		h.respondAuthenticated(w, r, userInfo)
		return
	}
	logging.Application(r.Header).
		WithField("username", username).Info("failed authentication")

	h.respondAuthFailure(w, r)
}

func (h *Handler) handleRefresh(w http.ResponseWriter, r *http.Request, userInfo model.UserInfo, valid bool) {
	if valid {
		h.respondAuthenticated(w, r, userInfo)
		logging.Application(r.Header).WithField("username", userInfo.Sub).Info("refreshed jwt")
		return
	}
	h.respondRefreshFailure(w, r)
}

func (h *Handler) deleteToken(w http.ResponseWriter) {
	cookie := &http.Cookie{
		Name:     h.config.CookieName,
		Value:    "delete",
		HttpOnly: true,
		Expires:  time.Unix(0, 0),
		Path:     "/",
	}
	if h.config.CookieDomain != "" {
		cookie.Domain = h.config.CookieDomain
	}
	http.SetCookie(w, cookie)
}

func (h *Handler) respondAuthenticated(w http.ResponseWriter, r *http.Request, userInfo model.UserInfo) {
	userInfo.Expiry = time.Now().Add(h.config.JwtExpiry).Unix()
	token, err := h.createToken(userInfo)
	if err != nil {
		logging.Application(r.Header).WithError(err).Error()
		h.respondError(w, r)
		return
	}

	wantHTML := wantHTML(r)
	wantAndHaveJWT := wantAndHaveJWT(r)
	if wantHTML || wantAndHaveJWT {
		cookie := &http.Cookie{
			Name:     h.config.CookieName,
			Value:    token,
			HttpOnly: h.config.CookieHTTPOnly,
			Path:     "/",
		}
		if h.config.CookieExpiry != 0 {
			cookie.Expires = time.Now().Add(h.config.CookieExpiry)
		}
		if h.config.CookieDomain != "" {
			cookie.Domain = h.config.CookieDomain
		}

		http.SetCookie(w, cookie)

	}

	if wantHTML {
		w.Header().Set("Location", h.config.SuccessURL)
		w.WriteHeader(303)
		return
	}

	w.Header().Set("Content-Type", contentTypeJWT)
	w.WriteHeader(200)
	fmt.Fprintf(w, "%s", token)
}

func (h *Handler) createToken(userInfo jwt.Claims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS512, userInfo)
	return token.SignedString([]byte(h.config.JwtSecret))
}

func (h *Handler) getToken(r *http.Request) (userInfo model.UserInfo, valid bool) {
	c, err := r.Cookie(h.config.CookieName)
	if err != nil {
		return model.UserInfo{}, false
	}

	token, err := jwt.ParseWithClaims(c.Value, &model.UserInfo{}, func(*jwt.Token) (interface{}, error) {
		return []byte(h.config.JwtSecret), nil
	})
	if err != nil {
		return model.UserInfo{}, false
	}

	u, ok := token.Claims.(*model.UserInfo)
	if !ok {
		return model.UserInfo{}, false
	}

	return *u, u.Valid() == nil
}

func (h *Handler) respondError(w http.ResponseWriter, r *http.Request) {
	if wantHTML(r) {
		username, _, _ := getCredentials(r)
		writeLoginForm(w,
			loginFormData{
				Error:    true,
				Config:   h.config,
				UserInfo: model.UserInfo{Sub: username},
			})
		return
	}
	w.Header().Set("Content-Type", contentTypePlain)
	w.WriteHeader(500)
	fmt.Fprintf(w, "Internal Server Error")
}

func (h *Handler) respondBadRequest(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(400)
	fmt.Fprintf(w, "Bad Request: Method or content-type not supported")
}

func (h *Handler) respondNotFound(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(404)
	fmt.Fprintf(w, "Not Found: The requested page does not exist")
}

func (h *Handler) respondAuthFailure(w http.ResponseWriter, r *http.Request) {
	if wantHTML(r) {
		w.Header().Set("Content-Type", contentTypeHTML)
		w.WriteHeader(403)
		username, _, _ := getCredentials(r)
		writeLoginForm(w,
			loginFormData{
				Failure:  true,
				Config:   h.config,
				UserInfo: model.UserInfo{Sub: username},
			})
		return
	}

	w.Header().Set("Content-Type", contentTypePlain)
	w.WriteHeader(403)
	fmt.Fprintf(w, "Wrong credentials")
}

func (h *Handler) respondRefreshFailure(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", contentTypePlain)
	w.WriteHeader(403)
	fmt.Fprintf(w, "Expired or invalid token")
}
func wantHTML(r *http.Request) bool {
	return strings.Contains(r.Header.Get("Accept"), "text/html")
}

func wantAndHaveJWT(r *http.Request) bool {
	return strings.Contains(r.Header.Get("Accept"), contentTypeJWT) && strings.Contains(r.Header.Get("Content-Type"), contentTypeJWT)
}

func getCredentials(r *http.Request) (string, string, error) {
	if r.Header.Get("Content-Type") == "application/json" {
		m := map[string]string{}
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return "", "", err
		}
		err = json.Unmarshal(body, &m)
		if err != nil {
			return "", "", err
		}
		return m["username"], m["password"], nil
	}
	return r.PostForm.Get("username"), r.PostForm.Get("password"), nil
}

func (h *Handler) authenticate(username, password string) (bool, model.UserInfo, error) {
	for _, b := range h.backends {
		authenticated, userInfo, err := b.Authenticate(username, password)
		if err != nil {
			return false, model.UserInfo{}, err
		}
		if authenticated {
			return authenticated, userInfo, nil
		}
	}
	return false, model.UserInfo{}, nil
}

type oauthManager interface {
	Handle(w http.ResponseWriter, r *http.Request) (
		startedFlow bool,
		authenticated bool,
		userInfo model.UserInfo,
		err error)
	AddConfig(providerName string, opts map[string]string) error
	GetConfigFromRequest(r *http.Request) (oauth2.Config, error)
}
<commit_msg>Remove unnecessary validity and failure handling for jwt refreshes<commit_after>package login

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/tarent/loginsrv/logging"
	"github.com/tarent/loginsrv/model"
	"github.com/tarent/loginsrv/oauth2"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

const contentTypeHTML = "text/html; charset=utf-8"
const contentTypeJWT = "application/jwt"
const contentTypePlain = "text/plain"

// Handler is the mail login handler.
// It serves the login ressource and does the authentication against the backends or oauth provider.
type Handler struct {
	backends []Backend
	oauth    oauthManager
	config   *Config
}

// NewHandler creates a login handler based on the supplied configuration.
func NewHandler(config *Config) (*Handler, error) {
	if len(config.Backends) == 0 && len(config.Oauth) == 0 {
		return nil, errors.New("No login backends or oauth provider configured")
	}

	backends := []Backend{}
	for pName, opts := range config.Backends {
		p, exist := GetProvider(pName)
		if !exist {
			return nil, fmt.Errorf("No such provider: %v", pName)
		}
		b, err := p(opts)
		if err != nil {
			return nil, err
		}
		backends = append(backends, b)
	}

	oauth := oauth2.NewManager()
	for providerName, opts := range config.Oauth {
		err := oauth.AddConfig(providerName, opts)
		if err != nil {
			return nil, err
		}
	}

	return &Handler{
		backends: backends,
		config:   config,
		oauth:    oauth,
	}, nil
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !strings.HasPrefix(r.URL.Path, h.config.LoginPath) {
		h.respondNotFound(w, r)
		return
	}

	_, err := h.oauth.GetConfigFromRequest(r)
	if err == nil {
		h.handleOauth(w, r)
		return
	}

	h.handleLogin(w, r)
	return
}

func (h *Handler) handleOauth(w http.ResponseWriter, r *http.Request) {
	startedFlow, authenticated, userInfo, err := h.oauth.Handle(w, r)

	if startedFlow {
		// the oauth flow started
		return
	}

	if err != nil {
		logging.Application(r.Header).WithError(err).Error()
		h.respondError(w, r)
		return
	}

	if authenticated {
		logging.Application(r.Header).
			WithField("username", userInfo.Sub).Info("successfully authenticated")
		h.respondAuthenticated(w, r, userInfo)
		return
	}
	logging.Application(r.Header).
		WithField("username", userInfo.Sub).Info("failed authentication")

	h.respondAuthFailure(w, r)
	return
}

func (h *Handler) handleLogin(w http.ResponseWriter, r *http.Request) {
	contentType := r.Header.Get("Content-Type")
	if !(r.Method == "GET" || r.Method == "DELETE" ||
		(r.Method == "POST" &&
			(strings.HasPrefix(contentType, "application/json") ||
				strings.HasPrefix(contentType, "application/x-www-form-urlencoded") ||
				strings.HasPrefix(contentType, contentTypeJWT) ||
				strings.HasPrefix(contentType, "multipart/form-data")))) {
		h.respondBadRequest(w, r)
		return
	}

	r.ParseForm()
	if r.Method == "DELETE" || r.FormValue("logout") == "true" {
		h.deleteToken(w)
		if h.config.LogoutURL != "" {
			w.Header().Set("Location", h.config.LogoutURL)
			w.WriteHeader(303)
			return
		}
		writeLoginForm(w,
			loginFormData{
				Config: h.config,
			})
		return
	}

	if r.Method == "GET" {
		userInfo, valid := h.getToken(r)
		writeLoginForm(w,
			loginFormData{
				Config:        h.config,
				Authenticated: valid,
				UserInfo:      userInfo,
			})
		return
	}

	if r.Method == "POST" {
		// Check if we got a valid jwt with the post (refresh)
		userInfo, valid := h.getToken(r)

		// No token found, assuming new authentication
		if userInfo.Sub == "" && userInfo.Expiry == 0 || !valid {
			h.handleAuthentication(w, r)
			return
		}

		// Refresh the jwt if it is valid
		h.handleRefresh(w, r, userInfo)
		return
	}
}

func (h *Handler) handleAuthentication(w http.ResponseWriter, r *http.Request) {
	username, password, err := getCredentials(r)
	if err != nil {
		h.respondBadRequest(w, r)
		return
	}
	authenticated, userInfo, err := h.authenticate(username, password)
	if err != nil {
		logging.Application(r.Header).WithError(err).Error()
		h.respondError(w, r)
		return
	}

	if authenticated {
		logging.Application(r.Header).
			WithField("username", username).Info("successfully authenticated")
		h.respondAuthenticated(w, r, userInfo)
		return
	}
	logging.Application(r.Header).
		WithField("username", username).Info("failed authentication")

	h.respondAuthFailure(w, r)
}

func (h *Handler) handleRefresh(w http.ResponseWriter, r *http.Request, userInfo model.UserInfo) {
	h.respondAuthenticated(w, r, userInfo)
	logging.Application(r.Header).WithField("username", userInfo.Sub).Info("refreshed jwt")
	return
}

func (h *Handler) deleteToken(w http.ResponseWriter) {
	cookie := &http.Cookie{
		Name:     h.config.CookieName,
		Value:    "delete",
		HttpOnly: true,
		Expires:  time.Unix(0, 0),
		Path:     "/",
	}
	if h.config.CookieDomain != "" {
		cookie.Domain = h.config.CookieDomain
	}
	http.SetCookie(w, cookie)
}

func (h *Handler) respondAuthenticated(w http.ResponseWriter, r *http.Request, userInfo model.UserInfo) {
	userInfo.Expiry = time.Now().Add(h.config.JwtExpiry).Unix()
	token, err := h.createToken(userInfo)
	if err != nil {
		logging.Application(r.Header).WithError(err).Error()
		h.respondError(w, r)
		return
	}

	wantHTML := wantHTML(r)
	wantAndHaveJWT := wantAndHaveJWT(r)
	if wantHTML || wantAndHaveJWT {
		cookie := &http.Cookie{
			Name:     h.config.CookieName,
			Value:    token,
			HttpOnly: h.config.CookieHTTPOnly,
			Path:     "/",
		}
		if h.config.CookieExpiry != 0 {
			cookie.Expires = time.Now().Add(h.config.CookieExpiry)
		}
		if h.config.CookieDomain != "" {
			cookie.Domain = h.config.CookieDomain
		}

		http.SetCookie(w, cookie)

	}

	if wantHTML {
		w.Header().Set("Location", h.config.SuccessURL)
		w.WriteHeader(303)
		return
	}

	w.Header().Set("Content-Type", contentTypeJWT)
	w.WriteHeader(200)
	fmt.Fprintf(w, "%s", token)
}

func (h *Handler) createToken(userInfo jwt.Claims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS512, userInfo)
	return token.SignedString([]byte(h.config.JwtSecret))
}

func (h *Handler) getToken(r *http.Request) (userInfo model.UserInfo, valid bool) {
	c, err := r.Cookie(h.config.CookieName)
	if err != nil {
		return model.UserInfo{}, false
	}

	token, err := jwt.ParseWithClaims(c.Value, &model.UserInfo{}, func(*jwt.Token) (interface{}, error) {
		return []byte(h.config.JwtSecret), nil
	})
	if err != nil {
		return model.UserInfo{}, false
	}

	u, ok := token.Claims.(*model.UserInfo)
	if !ok {
		return model.UserInfo{}, false
	}

	return *u, u.Valid() == nil
}

func (h *Handler) respondError(w http.ResponseWriter, r *http.Request) {
	if wantHTML(r) {
		username, _, _ := getCredentials(r)
		writeLoginForm(w,
			loginFormData{
				Error:    true,
				Config:   h.config,
				UserInfo: model.UserInfo{Sub: username},
			})
		return
	}
	w.Header().Set("Content-Type", contentTypePlain)
	w.WriteHeader(500)
	fmt.Fprintf(w, "Internal Server Error")
}

func (h *Handler) respondBadRequest(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(400)
	fmt.Fprintf(w, "Bad Request: Method or content-type not supported")
}

func (h *Handler) respondNotFound(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(404)
	fmt.Fprintf(w, "Not Found: The requested page does not exist")
}

func (h *Handler) respondAuthFailure(w http.ResponseWriter, r *http.Request) {
	if wantHTML(r) {
		w.Header().Set("Content-Type", contentTypeHTML)
		w.WriteHeader(403)
		username, _, _ := getCredentials(r)
		writeLoginForm(w,
			loginFormData{
				Failure:  true,
				Config:   h.config,
				UserInfo: model.UserInfo{Sub: username},
			})
		return
	}

	w.Header().Set("Content-Type", contentTypePlain)
	w.WriteHeader(403)
	fmt.Fprintf(w, "Wrong credentials")
}

func wantHTML(r *http.Request) bool {
	return strings.Contains(r.Header.Get("Accept"), "text/html")
}

func wantAndHaveJWT(r *http.Request) bool {
	return strings.Contains(r.Header.Get("Accept"), contentTypeJWT) && strings.Contains(r.Header.Get("Content-Type"), contentTypeJWT)
}

func getCredentials(r *http.Request) (string, string, error) {
	if r.Header.Get("Content-Type") == "application/json" {
		m := map[string]string{}
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return "", "", err
		}
		err = json.Unmarshal(body, &m)
		if err != nil {
			return "", "", err
		}
		return m["username"], m["password"], nil
	}
	return r.PostForm.Get("username"), r.PostForm.Get("password"), nil
}

func (h *Handler) authenticate(username, password string) (bool, model.UserInfo, error) {
	for _, b := range h.backends {
		authenticated, userInfo, err := b.Authenticate(username, password)
		if err != nil {
			return false, model.UserInfo{}, err
		}
		if authenticated {
			return authenticated, userInfo, nil
		}
	}
	return false, model.UserInfo{}, nil
}

type oauthManager interface {
	Handle(w http.ResponseWriter, r *http.Request) (
		startedFlow bool,
		authenticated bool,
		userInfo model.UserInfo,
		err error)
	AddConfig(providerName string, opts map[string]string) error
	GetConfigFromRequest(r *http.Request) (oauth2.Config, error)
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"errors"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"

	"code.cloudfoundry.org/lager"
	"github.com/concourse/baggageclaim"
	"github.com/concourse/baggageclaim/uidgid"
	"github.com/concourse/baggageclaim/volume"
	uuid "github.com/nu7hatch/gouuid"
	"github.com/tedsuo/rata"
)

const httpUnprocessableEntity = 422

var ErrListVolumesFailed = errors.New("failed to list volumes")
var ErrGetVolumeFailed = errors.New("failed to get volume")
var ErrGetVolumeStatsFailed = errors.New("failed to get volume stats")
var ErrCreateVolumeFailed = errors.New("failed to create volume")
var ErrDestroyVolumeFailed = errors.New("failed to destroy volume")
var ErrSetPropertyFailed = errors.New("failed to set property on volume")
var ErrSetTTLFailed = errors.New("failed to set ttl on volume")
var ErrStreamInFailed = errors.New("failed to stream in to volume")
var ErrStreamOutFailed = errors.New("failed to stream out from volume")
var ErrStreamOutNotFound = errors.New("no such file or directory")

type VolumeServer struct {
	strategerizer volume.Strategerizer
	namespacer    uidgid.Namespacer
	volumeRepo    volume.Repository

	logger lager.Logger
}

func NewVolumeServer(
	logger lager.Logger,
	strategerizer volume.Strategerizer,
	namespacer uidgid.Namespacer,
	volumeRepo volume.Repository,
) *VolumeServer {
	return &VolumeServer{
		strategerizer: strategerizer,
		namespacer:    namespacer,
		volumeRepo:    volumeRepo,
		logger:        logger,
	}
}

func (vs *VolumeServer) CreateVolume(w http.ResponseWriter, req *http.Request) {
	hLog := vs.logger.Session("create-volume")

	hLog.Debug("start")
	defer hLog.Debug("done")

	var request baggageclaim.VolumeRequest
	err := json.NewDecoder(req.Body).Decode(&request)
	if err != nil {
		hLog.Error("failed-to-decode-request", err)
		RespondWithError(w, ErrCreateVolumeFailed, http.StatusBadRequest)
		return
	}

	handle := request.Handle
	if handle == "" {
		handle, err = vs.generateHandle()
		if err != nil {
			hLog.Error("failed-to-generate-handle", err)
			RespondWithError(w, ErrCreateVolumeFailed, http.StatusBadRequest)
			return
		}
	}

	hLog = hLog.WithData(lager.Data{
		"handle":     handle,
		"ttl":        request.TTLInSeconds,
		"privileged": request.Privileged,
		"strategy":   request.Strategy,
	})

	strategy, err := vs.strategerizer.StrategyFor(request)
	if err != nil {
		hLog.Error("could-not-produce-strategy", err)
		RespondWithError(w, ErrCreateVolumeFailed, httpUnprocessableEntity)
		return
	}

	hLog.Debug("creating")

	createdVolume, err := vs.volumeRepo.CreateVolume(
		handle,
		strategy,
		volume.Properties(request.Properties),
		request.TTLInSeconds,
		request.Privileged,
	)

	if err != nil {
		hLog.Error("failed-to-create", err)

		var code int
		switch err {
		case volume.ErrParentVolumeNotFound:
			code = httpUnprocessableEntity
		case volume.ErrNoParentVolumeProvided:
			code = httpUnprocessableEntity
		default:
			code = http.StatusInternalServerError
		}
		RespondWithError(w, ErrCreateVolumeFailed, code)
		return
	}

	hLog = hLog.WithData(lager.Data{
		"volume": createdVolume.Handle,
	})

	hLog.Debug("created")

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)

	if err := json.NewEncoder(w).Encode(createdVolume); err != nil {
		hLog.Error("failed-to-encode", err, lager.Data{
			"volume-path": createdVolume.Path,
		})
	}
}

func (vs *VolumeServer) DestroyVolume(w http.ResponseWriter, req *http.Request) {
	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("destroy", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	err := vs.volumeRepo.DestroyVolume(handle)
	if err != nil {
		if err == volume.ErrVolumeDoesNotExist {
			hLog.Info("volume-does-not-exist")
			RespondWithError(w, ErrSetTTLFailed, http.StatusNotFound)
		} else {
			hLog.Error("failed-to-destroy", err)
			RespondWithError(w, ErrSetTTLFailed, http.StatusInternalServerError)
		}

		return
	}

	hLog.Info("destroyed")

	w.WriteHeader(http.StatusNoContent)
}

func (vs *VolumeServer) ListVolumes(w http.ResponseWriter, req *http.Request) {
	hLog := vs.logger.Session("list-volumes")

	hLog.Debug("start")
	defer hLog.Debug("done")

	w.Header().Set("Content-Type", "application/json")

	properties, err := ConvertQueryToProperties(req.URL.Query())
	if err != nil {
		RespondWithError(w, err, httpUnprocessableEntity)
		return
	}

	volumes, _, err := vs.volumeRepo.ListVolumes(properties)
	if err != nil {
		hLog.Error("failed-to-list-volumes", err)
		RespondWithError(w, ErrListVolumesFailed, http.StatusInternalServerError)
		return
	}

	if err := json.NewEncoder(w).Encode(volumes); err != nil {
		hLog.Error("failed-to-encode", err)
	}
}

func (vs *VolumeServer) GetVolume(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("get-volume", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	vol, found, err := vs.volumeRepo.GetVolume(handle)
	if err != nil {
		hLog.Error("failed-to-get-volume", err)
		RespondWithError(w, ErrGetVolumeFailed, http.StatusInternalServerError)
		return
	}

	if !found {
		hLog.Info("volume-not-found")
		RespondWithError(w, ErrGetVolumeFailed, http.StatusNotFound)
		return
	}

	if err := json.NewEncoder(w).Encode(vol); err != nil {
		hLog.Error("failed-to-encode", err)
	}
}

func (vs *VolumeServer) GetVolumeStats(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("get-volume-stats", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	vol, found, err := vs.volumeRepo.GetVolumeStats(handle)
	if err != nil {
		hLog.Error("failed-to-get-volume-stats", err)
		RespondWithError(w, ErrGetVolumeStatsFailed, http.StatusInternalServerError)
		return
	}

	if !found {
		RespondWithError(w, ErrGetVolumeStatsFailed, http.StatusNotFound)
		return
	}

	if err := json.NewEncoder(w).Encode(vol); err != nil {
		hLog.Error("failed-to-encode", err)
	}
}

func (vs *VolumeServer) SetProperty(w http.ResponseWriter, req *http.Request) {
	handle := rata.Param(req, "handle")
	propertyName := rata.Param(req, "property")

	hLog := vs.logger.Session("set-property", lager.Data{
		"volume":   handle,
		"property": propertyName,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	var request baggageclaim.PropertyRequest
	err := json.NewDecoder(req.Body).Decode(&request)
	if err != nil {
		RespondWithError(w, ErrSetPropertyFailed, http.StatusBadRequest)
		return
	}

	propertyValue := request.Value

	hLog.Debug("setting-property")

	err = vs.volumeRepo.SetProperty(handle, propertyName, propertyValue)
	if err != nil {
		hLog.Error("failed-to-set-property", err)

		if err == volume.ErrVolumeDoesNotExist {
			RespondWithError(w, ErrSetPropertyFailed, http.StatusNotFound)
		} else {
			RespondWithError(w, ErrSetPropertyFailed, http.StatusInternalServerError)
		}

		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (vs *VolumeServer) SetTTL(w http.ResponseWriter, req *http.Request) {
	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("set-ttl", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	var request baggageclaim.TTLRequest
	err := json.NewDecoder(req.Body).Decode(&request)
	if err != nil {
		RespondWithError(w, ErrSetTTLFailed, http.StatusBadRequest)
		return
	}

	ttl := request.Value

	hLog.Debug("setting-ttl", lager.Data{"ttl": ttl})

	err = vs.volumeRepo.SetTTL(handle, ttl)
	if err != nil {
		hLog.Error("failed-to-set-ttl", err)

		if err == volume.ErrVolumeDoesNotExist {
			RespondWithError(w, ErrSetTTLFailed, http.StatusNotFound)
		} else {
			RespondWithError(w, ErrSetTTLFailed, http.StatusInternalServerError)
		}

		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (vs *VolumeServer) StreamIn(w http.ResponseWriter, req *http.Request) {
	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("stream-in", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	vol, found, err := vs.volumeRepo.GetVolume(handle)
	if err != nil {
		hLog.Error("failed-to-get-volume", err)
		RespondWithError(w, ErrStreamInFailed, http.StatusInternalServerError)
		return
	}

	if !found {
		hLog.Info("volume-not-found")
		RespondWithError(w, ErrStreamInFailed, http.StatusNotFound)
		return
	}

	var subPath string
	if queryPath, ok := req.URL.Query()["path"]; ok {
		subPath = queryPath[0]
	}

	destinationPath := filepath.Join(vol.Path, subPath)

	hLog = hLog.WithData(lager.Data{
		"sub-path":  subPath,
		"full-path": destinationPath,
	})

	err = os.MkdirAll(destinationPath, 0755)
	if err != nil {
		hLog.Error("failed-to-create-destination-path", err)
		RespondWithError(w, ErrStreamInFailed, http.StatusInternalServerError)
		return
	}

	if !vol.Privileged {
		err := vs.namespacer.NamespacePath(hLog, vol.Path)
		if err != nil {
			hLog.Error("failed-to-namespace-path", err)
			RespondWithError(w, ErrStreamInFailed, http.StatusInternalServerError)
			return
		}
	}

	badStream, err := vs.streamIn(req.Body, destinationPath, vol.Privileged)
	if err != nil {
		if badStream {
			hLog.Info("bad-stream-payload", lager.Data{"error": err.Error()})
			RespondWithError(w, ErrStreamInFailed, http.StatusBadRequest)
			return
		}

		hLog.Error("failed-to-stream-into-volume", err)
		RespondWithError(w, ErrStreamInFailed, http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (vs *VolumeServer) StreamOut(w http.ResponseWriter, req *http.Request) {
	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("stream-out", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	vol, found, err := vs.volumeRepo.GetVolume(handle)
	if err != nil {
		hLog.Error("failed-to-get-volume", err)
		RespondWithError(w, ErrStreamOutFailed, http.StatusInternalServerError)
		return
	}

	if !found {
		hLog.Error("volume-not-found", err)
		RespondWithError(w, ErrStreamOutFailed, http.StatusNotFound)
		return
	}

	var subPath string
	if queryPath, ok := req.URL.Query()["path"]; ok {
		subPath = queryPath[0]
	}

	srcPath := filepath.Join(vol.Path, subPath)

	hLog = hLog.WithData(lager.Data{
		"sub-path":  subPath,
		"full-path": srcPath,
	})

	err = vs.streamOut(w, srcPath, vol.Privileged)
	if err != nil {
		if os.IsNotExist(err) {
			hLog.Info("source-path-not-found")
			RespondWithError(w, ErrStreamOutNotFound, http.StatusNotFound)
			return
		}

		hLog.Error("failed-to-stream-out-from-volume", err)

		if _, ok := err.(*exec.ExitError); ok {
			RespondWithError(w, ErrStreamOutFailed, http.StatusBadRequest)
			return
		}

		RespondWithError(w, ErrStreamOutFailed, http.StatusInternalServerError)
		return
	}
}

func (vs *VolumeServer) generateHandle() (string, error) {
	handle, err := uuid.NewV4()
	if err != nil {
		return "", err
	}

	return handle.String(), nil
}
<commit_msg>fix error message returned by destroy<commit_after>package api

import (
	"encoding/json"
	"errors"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"

	"code.cloudfoundry.org/lager"
	"github.com/concourse/baggageclaim"
	"github.com/concourse/baggageclaim/uidgid"
	"github.com/concourse/baggageclaim/volume"
	uuid "github.com/nu7hatch/gouuid"
	"github.com/tedsuo/rata"
)

const httpUnprocessableEntity = 422

var ErrListVolumesFailed = errors.New("failed to list volumes")
var ErrGetVolumeFailed = errors.New("failed to get volume")
var ErrGetVolumeStatsFailed = errors.New("failed to get volume stats")
var ErrCreateVolumeFailed = errors.New("failed to create volume")
var ErrDestroyVolumeFailed = errors.New("failed to destroy volume")
var ErrSetPropertyFailed = errors.New("failed to set property on volume")
var ErrSetTTLFailed = errors.New("failed to set ttl on volume")
var ErrStreamInFailed = errors.New("failed to stream in to volume")
var ErrStreamOutFailed = errors.New("failed to stream out from volume")
var ErrStreamOutNotFound = errors.New("no such file or directory")

type VolumeServer struct {
	strategerizer volume.Strategerizer
	namespacer    uidgid.Namespacer
	volumeRepo    volume.Repository

	logger lager.Logger
}

func NewVolumeServer(
	logger lager.Logger,
	strategerizer volume.Strategerizer,
	namespacer uidgid.Namespacer,
	volumeRepo volume.Repository,
) *VolumeServer {
	return &VolumeServer{
		strategerizer: strategerizer,
		namespacer:    namespacer,
		volumeRepo:    volumeRepo,
		logger:        logger,
	}
}

func (vs *VolumeServer) CreateVolume(w http.ResponseWriter, req *http.Request) {
	hLog := vs.logger.Session("create-volume")

	hLog.Debug("start")
	defer hLog.Debug("done")

	var request baggageclaim.VolumeRequest
	err := json.NewDecoder(req.Body).Decode(&request)
	if err != nil {
		hLog.Error("failed-to-decode-request", err)
		RespondWithError(w, ErrCreateVolumeFailed, http.StatusBadRequest)
		return
	}

	handle := request.Handle
	if handle == "" {
		handle, err = vs.generateHandle()
		if err != nil {
			hLog.Error("failed-to-generate-handle", err)
			RespondWithError(w, ErrCreateVolumeFailed, http.StatusBadRequest)
			return
		}
	}

	hLog = hLog.WithData(lager.Data{
		"handle":     handle,
		"ttl":        request.TTLInSeconds,
		"privileged": request.Privileged,
		"strategy":   request.Strategy,
	})

	strategy, err := vs.strategerizer.StrategyFor(request)
	if err != nil {
		hLog.Error("could-not-produce-strategy", err)
		RespondWithError(w, ErrCreateVolumeFailed, httpUnprocessableEntity)
		return
	}

	hLog.Debug("creating")

	createdVolume, err := vs.volumeRepo.CreateVolume(
		handle,
		strategy,
		volume.Properties(request.Properties),
		request.TTLInSeconds,
		request.Privileged,
	)

	if err != nil {
		hLog.Error("failed-to-create", err)

		var code int
		switch err {
		case volume.ErrParentVolumeNotFound:
			code = httpUnprocessableEntity
		case volume.ErrNoParentVolumeProvided:
			code = httpUnprocessableEntity
		default:
			code = http.StatusInternalServerError
		}
		RespondWithError(w, ErrCreateVolumeFailed, code)
		return
	}

	hLog = hLog.WithData(lager.Data{
		"volume": createdVolume.Handle,
	})

	hLog.Debug("created")

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)

	if err := json.NewEncoder(w).Encode(createdVolume); err != nil {
		hLog.Error("failed-to-encode", err, lager.Data{
			"volume-path": createdVolume.Path,
		})
	}
}

func (vs *VolumeServer) DestroyVolume(w http.ResponseWriter, req *http.Request) {
	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("destroy", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	err := vs.volumeRepo.DestroyVolume(handle)
	if err != nil {
		if err == volume.ErrVolumeDoesNotExist {
			hLog.Info("volume-does-not-exist")
			RespondWithError(w, ErrDestroyVolumeFailed, http.StatusNotFound)
		} else {
			hLog.Error("failed-to-destroy", err)
			RespondWithError(w, ErrDestroyVolumeFailed, http.StatusInternalServerError)
		}

		return
	}

	hLog.Info("destroyed")

	w.WriteHeader(http.StatusNoContent)
}

func (vs *VolumeServer) ListVolumes(w http.ResponseWriter, req *http.Request) {
	hLog := vs.logger.Session("list-volumes")

	hLog.Debug("start")
	defer hLog.Debug("done")

	w.Header().Set("Content-Type", "application/json")

	properties, err := ConvertQueryToProperties(req.URL.Query())
	if err != nil {
		RespondWithError(w, err, httpUnprocessableEntity)
		return
	}

	volumes, _, err := vs.volumeRepo.ListVolumes(properties)
	if err != nil {
		hLog.Error("failed-to-list-volumes", err)
		RespondWithError(w, ErrListVolumesFailed, http.StatusInternalServerError)
		return
	}

	if err := json.NewEncoder(w).Encode(volumes); err != nil {
		hLog.Error("failed-to-encode", err)
	}
}

func (vs *VolumeServer) GetVolume(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("get-volume", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	vol, found, err := vs.volumeRepo.GetVolume(handle)
	if err != nil {
		hLog.Error("failed-to-get-volume", err)
		RespondWithError(w, ErrGetVolumeFailed, http.StatusInternalServerError)
		return
	}

	if !found {
		hLog.Info("volume-not-found")
		RespondWithError(w, ErrGetVolumeFailed, http.StatusNotFound)
		return
	}

	if err := json.NewEncoder(w).Encode(vol); err != nil {
		hLog.Error("failed-to-encode", err)
	}
}

func (vs *VolumeServer) GetVolumeStats(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("get-volume-stats", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	vol, found, err := vs.volumeRepo.GetVolumeStats(handle)
	if err != nil {
		hLog.Error("failed-to-get-volume-stats", err)
		RespondWithError(w, ErrGetVolumeStatsFailed, http.StatusInternalServerError)
		return
	}

	if !found {
		RespondWithError(w, ErrGetVolumeStatsFailed, http.StatusNotFound)
		return
	}

	if err := json.NewEncoder(w).Encode(vol); err != nil {
		hLog.Error("failed-to-encode", err)
	}
}

func (vs *VolumeServer) SetProperty(w http.ResponseWriter, req *http.Request) {
	handle := rata.Param(req, "handle")
	propertyName := rata.Param(req, "property")

	hLog := vs.logger.Session("set-property", lager.Data{
		"volume":   handle,
		"property": propertyName,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	var request baggageclaim.PropertyRequest
	err := json.NewDecoder(req.Body).Decode(&request)
	if err != nil {
		RespondWithError(w, ErrSetPropertyFailed, http.StatusBadRequest)
		return
	}

	propertyValue := request.Value

	hLog.Debug("setting-property")

	err = vs.volumeRepo.SetProperty(handle, propertyName, propertyValue)
	if err != nil {
		hLog.Error("failed-to-set-property", err)

		if err == volume.ErrVolumeDoesNotExist {
			RespondWithError(w, ErrSetPropertyFailed, http.StatusNotFound)
		} else {
			RespondWithError(w, ErrSetPropertyFailed, http.StatusInternalServerError)
		}

		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (vs *VolumeServer) SetTTL(w http.ResponseWriter, req *http.Request) {
	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("set-ttl", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	var request baggageclaim.TTLRequest
	err := json.NewDecoder(req.Body).Decode(&request)
	if err != nil {
		RespondWithError(w, ErrSetTTLFailed, http.StatusBadRequest)
		return
	}

	ttl := request.Value

	hLog.Debug("setting-ttl", lager.Data{"ttl": ttl})

	err = vs.volumeRepo.SetTTL(handle, ttl)
	if err != nil {
		hLog.Error("failed-to-set-ttl", err)

		if err == volume.ErrVolumeDoesNotExist {
			RespondWithError(w, ErrSetTTLFailed, http.StatusNotFound)
		} else {
			RespondWithError(w, ErrSetTTLFailed, http.StatusInternalServerError)
		}

		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (vs *VolumeServer) StreamIn(w http.ResponseWriter, req *http.Request) {
	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("stream-in", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	vol, found, err := vs.volumeRepo.GetVolume(handle)
	if err != nil {
		hLog.Error("failed-to-get-volume", err)
		RespondWithError(w, ErrStreamInFailed, http.StatusInternalServerError)
		return
	}

	if !found {
		hLog.Info("volume-not-found")
		RespondWithError(w, ErrStreamInFailed, http.StatusNotFound)
		return
	}

	var subPath string
	if queryPath, ok := req.URL.Query()["path"]; ok {
		subPath = queryPath[0]
	}

	destinationPath := filepath.Join(vol.Path, subPath)

	hLog = hLog.WithData(lager.Data{
		"sub-path":  subPath,
		"full-path": destinationPath,
	})

	err = os.MkdirAll(destinationPath, 0755)
	if err != nil {
		hLog.Error("failed-to-create-destination-path", err)
		RespondWithError(w, ErrStreamInFailed, http.StatusInternalServerError)
		return
	}

	if !vol.Privileged {
		err := vs.namespacer.NamespacePath(hLog, vol.Path)
		if err != nil {
			hLog.Error("failed-to-namespace-path", err)
			RespondWithError(w, ErrStreamInFailed, http.StatusInternalServerError)
			return
		}
	}

	badStream, err := vs.streamIn(req.Body, destinationPath, vol.Privileged)
	if err != nil {
		if badStream {
			hLog.Info("bad-stream-payload", lager.Data{"error": err.Error()})
			RespondWithError(w, ErrStreamInFailed, http.StatusBadRequest)
			return
		}

		hLog.Error("failed-to-stream-into-volume", err)
		RespondWithError(w, ErrStreamInFailed, http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (vs *VolumeServer) StreamOut(w http.ResponseWriter, req *http.Request) {
	handle := rata.Param(req, "handle")

	hLog := vs.logger.Session("stream-out", lager.Data{
		"volume": handle,
	})

	hLog.Debug("start")
	defer hLog.Debug("done")

	vol, found, err := vs.volumeRepo.GetVolume(handle)
	if err != nil {
		hLog.Error("failed-to-get-volume", err)
		RespondWithError(w, ErrStreamOutFailed, http.StatusInternalServerError)
		return
	}

	if !found {
		hLog.Error("volume-not-found", err)
		RespondWithError(w, ErrStreamOutFailed, http.StatusNotFound)
		return
	}

	var subPath string
	if queryPath, ok := req.URL.Query()["path"]; ok {
		subPath = queryPath[0]
	}

	srcPath := filepath.Join(vol.Path, subPath)

	hLog = hLog.WithData(lager.Data{
		"sub-path":  subPath,
		"full-path": srcPath,
	})

	err = vs.streamOut(w, srcPath, vol.Privileged)
	if err != nil {
		if os.IsNotExist(err) {
			hLog.Info("source-path-not-found")
			RespondWithError(w, ErrStreamOutNotFound, http.StatusNotFound)
			return
		}

		hLog.Error("failed-to-stream-out-from-volume", err)

		if _, ok := err.(*exec.ExitError); ok {
			RespondWithError(w, ErrStreamOutFailed, http.StatusBadRequest)
			return
		}

		RespondWithError(w, ErrStreamOutFailed, http.StatusInternalServerError)
		return
	}
}

func (vs *VolumeServer) generateHandle() (string, error) {
	handle, err := uuid.NewV4()
	if err != nil {
		return "", err
	}

	return handle.String(), nil
}
<|endoftext|>


<commit_before>/*
 * Minimal object storage library (C) 2015 Minio, Inc.
 *
 * 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 minio

// bucketHandler is an http.Handler that verifies bucket responses and validates incoming requests
import (
	"bytes"
	"io"
	"net/http"
	"strconv"
	"time"
)

type bucketHandler struct {
	resource string
}

func (h bucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch {
	case r.Method == "GET":
		switch {
		case r.URL.Path == "/":
			response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><ListAllMyBucketsResult xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"><Buckets><Bucket><Name>bucket</Name><CreationDate>2015-05-20T23:05:09.230Z</CreationDate></Bucket></Buckets><Owner><ID>minio</ID><DisplayName>minio</DisplayName></Owner></ListAllMyBucketsResult>")
			w.Header().Set("Content-Length", strconv.Itoa(len(response)))
			w.Write(response)
		case r.URL.Path == "/bucket":
			_, ok := r.URL.Query()["acl"]
			if ok {
				response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><AccessControlPolicy><Owner><ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID><DisplayName>CustomersName@amazon.com</DisplayName></Owner><AccessControlList><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\"><ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID><DisplayName>CustomersName@amazon.com</DisplayName></Grantee><Permission>FULL_CONTROL</Permission></Grant></AccessControlList></AccessControlPolicy>")
				w.Header().Set("Content-Length", strconv.Itoa(len(response)))
				w.Write(response)
				return
			}
			fallthrough
		case r.URL.Path == "/bucket":
			response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><ListBucketResult xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"><Contents><ETag>259d04a13802ae09c7e41be50ccc6baa</ETag><Key>object</Key><LastModified>2015-05-21T18:24:21.097Z</LastModified><Size>22061</Size><Owner><ID>minio</ID><DisplayName>minio</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Delimiter></Delimiter><EncodingType></EncodingType><IsTruncated>false</IsTruncated><Marker></Marker><MaxKeys>1000</MaxKeys><Name>testbucket</Name><NextMarker></NextMarker><Prefix></Prefix></ListBucketResult>")
			w.Header().Set("Content-Length", strconv.Itoa(len(response)))
			w.Write(response)
		}
	case r.Method == "PUT":
		switch {
		case r.URL.Path == h.resource:
			_, ok := r.URL.Query()["acl"]
			if ok {
				if r.Header.Get("x-amz-acl") != "public-read-write" {
					w.WriteHeader(http.StatusNotImplemented)
				}
			}
			w.WriteHeader(http.StatusOK)
		default:
			w.WriteHeader(http.StatusBadRequest)
		}
	case r.Method == "HEAD":
		switch {
		case r.URL.Path == h.resource:
			w.WriteHeader(http.StatusOK)
		default:
			w.WriteHeader(http.StatusForbidden)
		}
	case r.Method == "DELETE":
		switch {
		case r.URL.Path != h.resource:
			w.WriteHeader(http.StatusNotFound)
		default:
			h.resource = ""
			w.WriteHeader(http.StatusOK)
		}
	}
}

// objectHandler is an http.Handler that verifies object responses and validates incoming requests
type objectHandler struct {
	resource string
	data     []byte
}

func (h objectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch {
	case r.Method == "PUT":
		length, err := strconv.Atoi(r.Header.Get("Content-Length"))
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		var buffer bytes.Buffer
		_, err = io.CopyN(&buffer, r.Body, int64(length))
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		if !bytes.Equal(h.data, buffer.Bytes()) {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		w.Header().Set("ETag", "9af2f8218b150c351ad802c6f3d66abe")
		w.WriteHeader(http.StatusOK)
	case r.Method == "HEAD":
		if r.URL.Path != h.resource {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		w.Header().Set("Content-Length", strconv.Itoa(len(h.data)))
		w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
		w.Header().Set("ETag", "9af2f8218b150c351ad802c6f3d66abe")
		w.WriteHeader(http.StatusOK)
	case r.Method == "POST":
		_, ok := r.URL.Query()["uploads"]
		if ok {
			response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><InitiateMultipartUploadResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Bucket>example-bucket</Bucket><Key>object</Key><UploadId>XXBsb2FkIElEIGZvciBlbHZpbmcncyVcdS1tb3ZpZS5tMnRzEEEwbG9hZA</UploadId></InitiateMultipartUploadResult>")
			w.Header().Set("Content-Length", strconv.Itoa(len(response)))
			w.Write(response)
			return
		}
	case r.Method == "GET":
		_, ok := r.URL.Query()["uploadId"]
		if ok {
			uploadID := r.URL.Query().Get("uploadId")
			if uploadID != "XXBsb2FkIElEIGZvciBlbHZpbmcncyVcdS1tb3ZpZS5tMnRzEEEwbG9hZA" {
				w.WriteHeader(http.StatusNotFound)
				return
			}
			response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><ListPartsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Bucket>example-bucket</Bucket><Key>example-object</Key><UploadId>XXBsb2FkIElEIGZvciBlbHZpbmcncyVcdS1tb3ZpZS5tMnRzEEEwbG9hZA</UploadId><Initiator><ID>arn:aws:iam::111122223333:user/some-user-11116a31-17b5-4fb7-9df5-b288870f11xx</ID><DisplayName>umat-user-11116a31-17b5-4fb7-9df5-b288870f11xx</DisplayName></Initiator><Owner><ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID><DisplayName>someName</DisplayName></Owner><StorageClass>STANDARD</StorageClass><PartNumberMarker>1</PartNumberMarker><NextPartNumberMarker>3</NextPartNumberMarker><MaxParts>2</MaxParts><IsTruncated>true</IsTruncated><Part><PartNumber>2</PartNumber><LastModified>2010-11-10T20:48:34.000Z</LastModified><ETag>\"7778aef83f66abc1fa1e8477f296d394\"</ETag><Size>10485760</Size></Part><Part><PartNumber>3</PartNumber><LastModified>2010-11-10T20:48:33.000Z</LastModified><ETag>\"aaaa18db4cc2f85cedef654fccc4a4x8\"</ETag><Size>10485760</Size></Part></ListPartsResult>")
			w.Header().Set("Content-Length", strconv.Itoa(len(response)))
			w.Write(response)
			return
		}
		if r.URL.Path != h.resource {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		w.Header().Set("Content-Length", strconv.Itoa(len(h.data)))
		w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
		w.Header().Set("ETag", "9af2f8218b150c351ad802c6f3d66abe")
		w.WriteHeader(http.StatusOK)
		io.Copy(w, bytes.NewReader(h.data))
	case r.Method == "DELETE":
		if r.URL.Path != h.resource {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		h.resource = ""
		h.data = nil
		w.WriteHeader(http.StatusOK)
	}
}
<commit_msg>Adding missing quotes to ETag in tests<commit_after>/*
 * Minimal object storage library (C) 2015 Minio, Inc.
 *
 * 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 minio

// bucketHandler is an http.Handler that verifies bucket responses and validates incoming requests
import (
	"bytes"
	"io"
	"net/http"
	"strconv"
	"time"
)

type bucketHandler struct {
	resource string
}

func (h bucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch {
	case r.Method == "GET":
		switch {
		case r.URL.Path == "/":
			response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><ListAllMyBucketsResult xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"><Buckets><Bucket><Name>bucket</Name><CreationDate>2015-05-20T23:05:09.230Z</CreationDate></Bucket></Buckets><Owner><ID>minio</ID><DisplayName>minio</DisplayName></Owner></ListAllMyBucketsResult>")
			w.Header().Set("Content-Length", strconv.Itoa(len(response)))
			w.Write(response)
		case r.URL.Path == "/bucket":
			_, ok := r.URL.Query()["acl"]
			if ok {
				response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><AccessControlPolicy><Owner><ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID><DisplayName>CustomersName@amazon.com</DisplayName></Owner><AccessControlList><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\"><ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID><DisplayName>CustomersName@amazon.com</DisplayName></Grantee><Permission>FULL_CONTROL</Permission></Grant></AccessControlList></AccessControlPolicy>")
				w.Header().Set("Content-Length", strconv.Itoa(len(response)))
				w.Write(response)
				return
			}
			fallthrough
		case r.URL.Path == "/bucket":
			response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><ListBucketResult xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"><Contents><ETag>\"259d04a13802ae09c7e41be50ccc6baa\"</ETag><Key>object</Key><LastModified>2015-05-21T18:24:21.097Z</LastModified><Size>22061</Size><Owner><ID>minio</ID><DisplayName>minio</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Delimiter></Delimiter><EncodingType></EncodingType><IsTruncated>false</IsTruncated><Marker></Marker><MaxKeys>1000</MaxKeys><Name>testbucket</Name><NextMarker></NextMarker><Prefix></Prefix></ListBucketResult>")
			w.Header().Set("Content-Length", strconv.Itoa(len(response)))
			w.Write(response)
		}
	case r.Method == "PUT":
		switch {
		case r.URL.Path == h.resource:
			_, ok := r.URL.Query()["acl"]
			if ok {
				if r.Header.Get("x-amz-acl") != "public-read-write" {
					w.WriteHeader(http.StatusNotImplemented)
				}
			}
			w.WriteHeader(http.StatusOK)
		default:
			w.WriteHeader(http.StatusBadRequest)
		}
	case r.Method == "HEAD":
		switch {
		case r.URL.Path == h.resource:
			w.WriteHeader(http.StatusOK)
		default:
			w.WriteHeader(http.StatusForbidden)
		}
	case r.Method == "DELETE":
		switch {
		case r.URL.Path != h.resource:
			w.WriteHeader(http.StatusNotFound)
		default:
			h.resource = ""
			w.WriteHeader(http.StatusOK)
		}
	}
}

// objectHandler is an http.Handler that verifies object responses and validates incoming requests
type objectHandler struct {
	resource string
	data     []byte
}

func (h objectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch {
	case r.Method == "PUT":
		length, err := strconv.Atoi(r.Header.Get("Content-Length"))
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		var buffer bytes.Buffer
		_, err = io.CopyN(&buffer, r.Body, int64(length))
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		if !bytes.Equal(h.data, buffer.Bytes()) {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		w.Header().Set("ETag", "\"9af2f8218b150c351ad802c6f3d66abe\"")
		w.WriteHeader(http.StatusOK)
	case r.Method == "HEAD":
		if r.URL.Path != h.resource {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		w.Header().Set("Content-Length", strconv.Itoa(len(h.data)))
		w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
		w.Header().Set("ETag", "\"9af2f8218b150c351ad802c6f3d66abe\"")
		w.WriteHeader(http.StatusOK)
	case r.Method == "POST":
		_, ok := r.URL.Query()["uploads"]
		if ok {
			response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><InitiateMultipartUploadResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Bucket>example-bucket</Bucket><Key>object</Key><UploadId>XXBsb2FkIElEIGZvciBlbHZpbmcncyVcdS1tb3ZpZS5tMnRzEEEwbG9hZA</UploadId></InitiateMultipartUploadResult>")
			w.Header().Set("Content-Length", strconv.Itoa(len(response)))
			w.Write(response)
			return
		}
	case r.Method == "GET":
		_, ok := r.URL.Query()["uploadId"]
		if ok {
			uploadID := r.URL.Query().Get("uploadId")
			if uploadID != "XXBsb2FkIElEIGZvciBlbHZpbmcncyVcdS1tb3ZpZS5tMnRzEEEwbG9hZA" {
				w.WriteHeader(http.StatusNotFound)
				return
			}
			response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><ListPartsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Bucket>example-bucket</Bucket><Key>example-object</Key><UploadId>XXBsb2FkIElEIGZvciBlbHZpbmcncyVcdS1tb3ZpZS5tMnRzEEEwbG9hZA</UploadId><Initiator><ID>arn:aws:iam::111122223333:user/some-user-11116a31-17b5-4fb7-9df5-b288870f11xx</ID><DisplayName>umat-user-11116a31-17b5-4fb7-9df5-b288870f11xx</DisplayName></Initiator><Owner><ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID><DisplayName>someName</DisplayName></Owner><StorageClass>STANDARD</StorageClass><PartNumberMarker>1</PartNumberMarker><NextPartNumberMarker>3</NextPartNumberMarker><MaxParts>2</MaxParts><IsTruncated>true</IsTruncated><Part><PartNumber>2</PartNumber><LastModified>2010-11-10T20:48:34.000Z</LastModified><ETag>\"7778aef83f66abc1fa1e8477f296d394\"</ETag><Size>10485760</Size></Part><Part><PartNumber>3</PartNumber><LastModified>2010-11-10T20:48:33.000Z</LastModified><ETag>\"aaaa18db4cc2f85cedef654fccc4a4x8\"</ETag><Size>10485760</Size></Part></ListPartsResult>")
			w.Header().Set("Content-Length", strconv.Itoa(len(response)))
			w.Write(response)
			return
		}
		if r.URL.Path != h.resource {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		w.Header().Set("Content-Length", strconv.Itoa(len(h.data)))
		w.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
		w.Header().Set("ETag", "\"9af2f8218b150c351ad802c6f3d66abe\"")
		w.WriteHeader(http.StatusOK)
		io.Copy(w, bytes.NewReader(h.data))
	case r.Method == "DELETE":
		if r.URL.Path != h.resource {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		h.resource = ""
		h.data = nil
		w.WriteHeader(http.StatusOK)
	}
}
<|endoftext|>


<commit_before>package lookup

import (
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/domainr/whois"
	"github.com/gosuri/uilive"
	"github.com/gosuri/uitable"
	"github.com/fatih/color"
	"golang.org/x/text/language"
)

const (
	defaultLanguage = "en"
	defaultRegion   = "US"
)

type domaincheck struct {
	Domain, Available string
	Id                int
}

var domainExtensions = []string{"com", "net", "org", "nl", "de", "io"}

var domainscheck = []domaincheck{}

// Provider interface provides a way to build the URI
// for each provider.

// Providers tracks loaded providers.
// var Providers map[string]Provider

func init() {
	// 	Providers = make(map[string]Provider)
}

// AddProvider should be called within your provider's init() func.
// This will register the provider so it can be used.
// func AddProvider(name string, provider Provider) {
// 	Providers[name] = provider
// }

// Search builds a search URL and opens it in your browser.
func DoLookUp(p string, qwhois string, verbose bool) error {
	writer := uilive.New()
	writer.Start()

	red := color.New(color.FgRed, color.Bold).SprintFunc()

	table := uitable.New()
	table.MaxColWidth = 50
	if strings.HasSuffix(qwhois, ".*") {
		qwhois = strings.TrimSuffix(qwhois, ".*")

		for i, domain := range domainExtensions {
			domainscheck = append(domainscheck, domaincheck{fmt.Sprintf("%s.%s", qwhois, domain), "progress", i})
			table.AddRow(fmt.Sprintf("[%s]", red("☓")), fmt.Sprintf("%s.%s", qwhois, domain))
		}

		// lookup.doWhois(verbose, qwhois) // Add array support
	}

	fmt.Fprintln(writer, table)
	writer.Stop() // flush and stop rendering

	fmt.Printf(doWhois(qwhois, verbose))
	if verbose {
		fmt.Printf("%s\n", qwhois)
	}

	return nil
}

func doWhois(qwhois string, verbose bool) string {
	request, err := whois.NewRequest(qwhois)
	FatalIf(err)

	response, err := whois.DefaultClient.Fetch(request)
	FatalIf(err)

	if verbose {
		fmt.Printf("%s\n", response)
	}

	r := regexp.MustCompile(`(No match)|(^NOT FOUND)|(^Not fo|AVAILABLE)|(^No Data Fou|has not been regi|No entri)|(Status: free)|(.nl is free)`)
	if v := response.String(); r.MatchString(v) {
		return "beschikbaar\n"
	} else {
		return "☓\n"
	}
}

// Region returns the users region code.
// Eg. "US", "GB", etc
func Region() string {
	l := locale()

	tag, err := language.Parse(l)
	if err != nil {
		return defaultRegion
	}

	region, _ := tag.Region()

	return region.String()
}

// Language returns the users language code.
// Eg. "en", "es", etc
func Language() string {
	l := locale()

	tag, err := language.Parse(l)
	if err != nil {
		return defaultLanguage
	}

	base, _ := tag.Base()

	return base.String()
}

func locale() string {
	lang := os.Getenv("LANG")
	if lang == "" {
		return ""
	}

	locale := strings.Split(lang, ".")[0]

	return locale
}

func FatalIf(err error) {
	if err == nil {
		return
	}
	fmt.Fprintf(os.Stderr, "Error: %s\n", err)
	os.Exit(-1)
}
<commit_msg>Added domainchecking and made it finally works<commit_after>package lookup

import (
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/domainr/whois"
	"github.com/fatih/color"
	"github.com/gosuri/uilive"
	"github.com/gosuri/uitable"
	"golang.org/x/text/language"
)

const (
	defaultLanguage = "en"
	defaultRegion   = "US"
)

type domaincheck struct {
	Domain    string
	Available int
}

var domainExtensions = []string{"com", "net", "org", "nl", "de", "io"}

var domainscheck = []domaincheck{}

// Provider interface provides a way to build the URI
// for each provider.

// Providers tracks loaded providers.
// var Providers map[string]Provider

func init() {
	// 	Providers = make(map[string]Provider)
}

// AddProvider should be called within your provider's init() func.
// This will register the provider so it can be used.
// func AddProvider(name string, provider Provider) {
// 	Providers[name] = provider
// }

// Search builds a search URL and opens it in your browser.
func DoLookUp(p string, qwhois string, verbose bool) error {

	if strings.HasSuffix(qwhois, ".*") {
		qwhois = strings.TrimSuffix(qwhois, ".*")

		for _, domain := range domainExtensions {
			domainscheck = append(domainscheck, domaincheck{fmt.Sprintf("%s.%s", qwhois, domain), 0})
		}
	}

	createTableArray(doWhoisWithArray(domainscheck))

	if verbose {
		fmt.Printf("%s\n", qwhois)
	}
	return nil
}

func createTableArray(domainscheck []domaincheck) {
	writer := uilive.New()
	writer.Start()

	red := color.New(color.FgRed, color.Bold).SprintFunc()
	green := color.New(color.FgGreen, color.Bold).SprintFunc()

	table := uitable.New()
	table.MaxColWidth = 50

	for _, domain := range domainscheck {
		indicator := ""
		if domain.Available == 1 {
			indicator = fmt.Sprintf("[%s]", green("✓"))
		} else if domain.Available == 2 {
			indicator = fmt.Sprintf("[%s]", red("☓"))
		}
		table.AddRow(indicator, domain.Domain)
	}

	fmt.Fprintln(writer, table)
	writer.Stop() // flush and stop rendering
}

func doWhoisWithArray(qwhois []domaincheck) []domaincheck {
	domainscheck = qwhois
	for i, domain := range qwhois {
		// fmt.Printf(domain.Domain)
		domainscheck[i] = domaincheck{domain.Domain, doWhois(domain.Domain, false)}
	}
	return domainscheck
}

func doWhois(qwhois string, verbose bool) int {
	request, err := whois.NewRequest(qwhois)
	FatalIf(err)

	response, err := whois.DefaultClient.Fetch(request)
	FatalIf(err)

	if verbose {
		fmt.Printf("%s\n", response)
	}

	r := regexp.MustCompile(`(No match)|(^NOT FOUND)|(^Not fo|AVAILABLE)|(^No Data Fou|has not been regi|No entri)|(Status: free)|(.nl is free)`)
	if v := response.String(); r.MatchString(v) {
		return 1
	} else {
		return 2
	}
}

// Region returns the users region code.
// Eg. "US", "GB", etc
func Region() string {
	l := locale()

	tag, err := language.Parse(l)
	if err != nil {
		return defaultRegion
	}

	region, _ := tag.Region()

	return region.String()
}

// Language returns the users language code.
// Eg. "en", "es", etc
func Language() string {
	l := locale()

	tag, err := language.Parse(l)
	if err != nil {
		return defaultLanguage
	}

	base, _ := tag.Base()

	return base.String()
}

func locale() string {
	lang := os.Getenv("LANG")
	if lang == "" {
		return ""
	}

	locale := strings.Split(lang, ".")[0]

	return locale
}

func FatalIf(err error) {
	if err == nil {
		return
	}
	fmt.Fprintf(os.Stderr, "Error: %s\n", err)
	os.Exit(-1)
}
<|endoftext|>


<commit_before>package main

/*
typedef struct {
    char* builderType;
    char* baseDir;
    char* sqliteDir;
    char* fontDir;
    char* shapeDir;
    char* imageDir;
    _Bool relPaths;
    _Bool msNoMapBlock;
} Opts;
*/
import (
    "C"
)

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

    "github.com/omniscale/magnacarto/builder"
    "github.com/omniscale/magnacarto/builder/mapnik"
    "github.com/omniscale/magnacarto/builder/mapserver"
    "github.com/omniscale/magnacarto/config"
    "github.com/omniscale/magnacarto/mml"
)

//export buildFromString
func buildFromString(mmlStr *C.char, options C.Opts) (output, error *C.char) {
    mml := C.GoString(mmlStr)
    if mml == "" {
        return nil, C.CString("Please specify a MML string.")
    }
    baseDir := C.GoString(options.baseDir)
    if baseDir == "" {
        return nil, C.CString("Please specify a basedir path.")
    }

    return build(mml, baseDir, options)
}

//export buildFromFile
func buildFromFile(file *C.char, options C.Opts) (output, error *C.char) {
    filePath := C.GoString(file)
    if filePath == "" {
        return nil, C.CString("Please specify a file path.")
    }

    mml, err := ioutil.ReadFile(filePath)
    if err != nil {
        return nil, C.CString(fmt.Sprint("Error when reading from file ", filePath, ": ", err))
    }

    baseDir := C.GoString(options.baseDir)
    if baseDir == "" {
        baseDir = filepath.Dir(filePath)
    }

    return build(string(mml), baseDir, options)
}

func build(mmlStr string, baseDir string, options C.Opts) (output, error *C.char) {
    conf := config.Magnacarto{}
    locator := conf.Locator()

    builderType := C.GoString(options.builderType)
    if builderType == "" {
        builderType = "mapnik2"
    }
    sqliteDir := C.GoString(options.sqliteDir)
	if sqliteDir != "" {
		conf.Datasources.SQLiteDirs = []string{sqliteDir}
	}
    fontDir := C.GoString(options.fontDir)
	if fontDir != "" {
		conf.Mapnik.FontDirs = []string{fontDir}
	}
    shapeDir := C.GoString(options.shapeDir)
	if shapeDir != "" {
		conf.Datasources.ShapefileDirs = []string{shapeDir}
	}
    imageDir := C.GoString(options.imageDir)
	if imageDir != "" {
		conf.Datasources.ImageDirs = []string{imageDir}
	}
    relPaths := bool(options.relPaths)
    if relPaths {
		locator.UseRelPaths(relPaths)
	}
    msNoMapBlock := bool(options.msNoMapBlock)

    locator.SetBaseDir(baseDir)
    var m builder.MapWriter

    switch builderType {
    case "mapserver":
        mm := mapserver.New(locator)
        mm.SetNoMapBlock(msNoMapBlock)
        m = mm
    case "mapnik2":
        mm := mapnik.New(locator)
        mm.SetMapnik2(true)
        m = mm
    case "mapnik3":
        m = mapnik.New(locator)
    default:
        return nil, C.CString(fmt.Sprint("unknown builder ", builderType))
    }

    r := strings.NewReader(mmlStr)
    mmlData, err := mml.Parse(r)
    if err != nil {
        return nil, C.CString(fmt.Sprint(err))
    }

    var style bytes.Buffer
    for _, s := range mmlData.Stylesheets {
        if strings.Contains(s, ".mss") {
            r, err := os.Open(filepath.Join(baseDir, s))
            if err != nil {
                return nil, C.CString(fmt.Sprint(err))
            }
            content, err := ioutil.ReadAll(r)
            if err != nil {
                return nil, C.CString(fmt.Sprint(err))
            }
            r.Close()
            style.Write(content)
        }
    }

    builder.BuildMapFromString(m, mmlData, style.String())

    var buf bytes.Buffer
    if err := m.Write(&buf); err != nil {
        return nil, C.CString(fmt.Sprint(err))
    }

    return C.CString(buf.String()), nil
}

func main() {
}
<commit_msg>only load style from file if string ends with .mss<commit_after>package main

/*
typedef struct {
    char* builderType;
    char* baseDir;
    char* sqliteDir;
    char* fontDir;
    char* shapeDir;
    char* imageDir;
    _Bool relPaths;
    _Bool msNoMapBlock;
} Opts;
*/
import (
    "C"
)

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

    "github.com/omniscale/magnacarto/builder"
    "github.com/omniscale/magnacarto/builder/mapnik"
    "github.com/omniscale/magnacarto/builder/mapserver"
    "github.com/omniscale/magnacarto/config"
    "github.com/omniscale/magnacarto/mml"
)

//export buildFromString
func buildFromString(mmlStr *C.char, options C.Opts) (output, error *C.char) {
    mml := C.GoString(mmlStr)
    if mml == "" {
        return nil, C.CString("Please specify a MML string.")
    }
    baseDir := C.GoString(options.baseDir)
    if baseDir == "" {
        return nil, C.CString("Please specify a basedir path.")
    }

    return build(mml, baseDir, options)
}

//export buildFromFile
func buildFromFile(file *C.char, options C.Opts) (output, error *C.char) {
    filePath := C.GoString(file)
    if filePath == "" {
        return nil, C.CString("Please specify a file path.")
    }

    mml, err := ioutil.ReadFile(filePath)
    if err != nil {
        return nil, C.CString(fmt.Sprint("Error when reading from file ", filePath, ": ", err))
    }

    baseDir := C.GoString(options.baseDir)
    if baseDir == "" {
        baseDir = filepath.Dir(filePath)
    }

    return build(string(mml), baseDir, options)
}

func build(mmlStr string, baseDir string, options C.Opts) (output, error *C.char) {
    conf := config.Magnacarto{}
    locator := conf.Locator()

    builderType := C.GoString(options.builderType)
    if builderType == "" {
        builderType = "mapnik2"
    }
    sqliteDir := C.GoString(options.sqliteDir)
	if sqliteDir != "" {
		conf.Datasources.SQLiteDirs = []string{sqliteDir}
	}
    fontDir := C.GoString(options.fontDir)
	if fontDir != "" {
		conf.Mapnik.FontDirs = []string{fontDir}
	}
    shapeDir := C.GoString(options.shapeDir)
	if shapeDir != "" {
		conf.Datasources.ShapefileDirs = []string{shapeDir}
	}
    imageDir := C.GoString(options.imageDir)
	if imageDir != "" {
		conf.Datasources.ImageDirs = []string{imageDir}
	}
    relPaths := bool(options.relPaths)
    if relPaths {
		locator.UseRelPaths(relPaths)
	}
    msNoMapBlock := bool(options.msNoMapBlock)

    locator.SetBaseDir(baseDir)
    var m builder.MapWriter

    switch builderType {
    case "mapserver":
        mm := mapserver.New(locator)
        mm.SetNoMapBlock(msNoMapBlock)
        m = mm
    case "mapnik2":
        mm := mapnik.New(locator)
        mm.SetMapnik2(true)
        m = mm
    case "mapnik3":
        m = mapnik.New(locator)
    default:
        return nil, C.CString(fmt.Sprint("unknown builder ", builderType))
    }

    r := strings.NewReader(mmlStr)
    mmlData, err := mml.Parse(r)
    if err != nil {
        return nil, C.CString(fmt.Sprint(err))
    }

    var style bytes.Buffer
    for _, s := range mmlData.Stylesheets {
        if strings.HasSuffix(s, ".mss") {
            r, err := os.Open(filepath.Join(baseDir, s))
            if err != nil {
                return nil, C.CString(fmt.Sprint(err))
            }
            content, err := ioutil.ReadAll(r)
            if err != nil {
                return nil, C.CString(fmt.Sprint(err))
            }
            r.Close()
            style.Write(content)
        }
    }

    builder.BuildMapFromString(m, mmlData, style.String())

    var buf bytes.Buffer
    if err := m.Write(&buf); err != nil {
        return nil, C.CString(fmt.Sprint(err))
    }

    return C.CString(buf.String()), nil
}

func main() {
}
<|endoftext|>


<commit_before>// Copyright Project Harbor Authors
//
// 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 handler

import (
	"context"
	"fmt"
	"strings"

	"github.com/go-openapi/runtime/middleware"
	"github.com/goharbor/harbor/src/common/rbac"
	"github.com/goharbor/harbor/src/controller/scan"
	"github.com/goharbor/harbor/src/controller/scanner"
	"github.com/goharbor/harbor/src/jobservice/job"
	"github.com/goharbor/harbor/src/lib/errors"
	"github.com/goharbor/harbor/src/lib/q"
	"github.com/goharbor/harbor/src/pkg/scheduler"
	"github.com/goharbor/harbor/src/pkg/task"
	"github.com/goharbor/harbor/src/server/v2.0/handler/model"
	"github.com/goharbor/harbor/src/server/v2.0/models"
	operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/scan_all"
)

func newScanAllAPI() *scanAllAPI {
	return &scanAllAPI{
		execMgr:    task.ExecMgr,
		scanCtl:    scan.DefaultController,
		scannerCtl: scanner.DefaultController,
		scheduler:  scheduler.Sched,
	}
}

type scanAllAPI struct {
	BaseAPI
	execMgr    task.ExecutionManager
	scanCtl    scan.Controller
	scannerCtl scanner.Controller
	scheduler  scheduler.Scheduler
}

func (s *scanAllAPI) Prepare(ctx context.Context, operation string, params interface{}) middleware.Responder {
	return nil
}

func (s *scanAllAPI) CreateScanAllSchedule(ctx context.Context, params operation.CreateScanAllScheduleParams) middleware.Responder {
	if err := s.requireAccess(ctx, rbac.ActionCreate); err != nil {
		return s.SendError(ctx, err)
	}

	req := params.Schedule

	if req.Schedule.Type == ScheduleNone {
		return operation.NewCreateScanAllScheduleCreated()
	}

	if req.Schedule.Type == ScheduleManual {
		execution, err := s.getLatestScanAllExecution(ctx, task.ExecutionTriggerManual)
		if err != nil {
			return s.SendError(ctx, err)
		}

		if execution != nil && execution.IsOnGoing() {
			message := fmt.Sprintf("a previous scan all job aleady exits, its status is %s", execution.Status)
			return s.SendError(ctx, errors.ConflictError(nil).WithMessage(message))
		}

		if _, err := s.scanCtl.ScanAll(ctx, task.ExecutionTriggerManual, true); err != nil {
			return s.SendError(ctx, err)
		}
	} else {
		schedule, err := s.getScanAllSchedule(ctx)
		if err != nil {
			return s.SendError(ctx, err)
		}

		if schedule != nil {
			message := "fail to set schedule for scan all as always had one, please delete it firstly then to re-schedule"
			return s.SendError(ctx, errors.PreconditionFailedError(nil).WithMessage(message))
		}

		if _, err := s.createOrUpdateScanAllSchedule(ctx, req.Schedule.Type, req.Schedule.Cron, nil); err != nil {
			return s.SendError(ctx, err)
		}
	}

	return operation.NewCreateScanAllScheduleCreated()
}

func (s *scanAllAPI) UpdateScanAllSchedule(ctx context.Context, params operation.UpdateScanAllScheduleParams) middleware.Responder {
	if err := s.requireAccess(ctx, rbac.ActionUpdate); err != nil {
		return s.SendError(ctx, err)
	}
	req := params.Schedule

	if req.Schedule.Type == ScheduleManual {
		message := fmt.Sprintf("fail to update scan all schedule as wrong schedule type: %s", req.Schedule.Type)
		return s.SendError(ctx, errors.BadRequestError(nil).WithMessage(message))
	}

	schedule, err := s.getScanAllSchedule(ctx)
	if err != nil {
		return s.SendError(ctx, err)
	}

	if req.Schedule.Type == ScheduleNone {
		if schedule != nil {
			err = s.scheduler.UnScheduleByID(ctx, schedule.ID)
		}
	} else {
		_, err = s.createOrUpdateScanAllSchedule(ctx, req.Schedule.Type, req.Schedule.Cron, schedule)
	}

	if err != nil {
		return s.SendError(ctx, err)
	}

	return operation.NewUpdateScanAllScheduleOK()
}

func (s *scanAllAPI) GetScanAllSchedule(ctx context.Context, params operation.GetScanAllScheduleParams) middleware.Responder {
	if err := s.requireAccess(ctx, rbac.ActionRead); err != nil {
		return s.SendError(ctx, err)
	}
	schedule, err := s.getScanAllSchedule(ctx)
	if err != nil {
		return s.SendError(ctx, err)
	}

	return operation.NewGetScanAllScheduleOK().WithPayload(model.NewSchedule(schedule).ToSwagger())
}

func (s *scanAllAPI) GetLatestScanAllMetrics(ctx context.Context, params operation.GetLatestScanAllMetricsParams) middleware.Responder {
	if err := s.requireAccess(ctx, rbac.ActionRead); err != nil {
		return s.SendError(ctx, err)
	}
	stats, err := s.getMetrics(ctx)
	if err != nil {
		return s.SendError(ctx, err)
	}

	return operation.NewGetLatestScanAllMetricsOK().WithPayload(stats)
}

func (s *scanAllAPI) GetLatestScheduledScanAllMetrics(ctx context.Context, params operation.GetLatestScheduledScanAllMetricsParams) middleware.Responder {
	if err := s.requireAccess(ctx, rbac.ActionRead); err != nil {
		return s.SendError(ctx, err)
	}
	stats, err := s.getMetrics(ctx, task.ExecutionTriggerSchedule)
	if err != nil {
		return s.SendError(ctx, err)
	}

	return operation.NewGetLatestScanAllMetricsOK().WithPayload(stats)
}

func (s *scanAllAPI) createOrUpdateScanAllSchedule(ctx context.Context, cronType, cron string, previous *scheduler.Schedule) (int64, error) {
	if previous != nil {
		if cronType == previous.CRONType && cron == previous.CRON {
			return previous.ID, nil
		}

		if err := s.scheduler.UnScheduleByID(ctx, previous.ID); err != nil {
			return 0, err
		}
	}

	return s.scheduler.Schedule(ctx, job.ImageScanAllJob, 0, cronType, cron, scan.ScanAllCallback, nil, nil)
}

func (s *scanAllAPI) getScanAllSchedule(ctx context.Context) (*scheduler.Schedule, error) {
	query := q.New(q.KeyWords{"vendor_type": job.ImageScanAllJob})
	schedules, err := s.scheduler.ListSchedules(ctx, query.First("-creation_time"))
	if err != nil {
		return nil, err
	}

	if len(schedules) > 1 {
		return nil, fmt.Errorf("found more than one scheduled scan all job, please ensure that only one schedule left")
	} else if len(schedules) == 0 {
		return nil, nil
	}

	return schedules[0], nil
}

func (s *scanAllAPI) getMetrics(ctx context.Context, trigger ...string) (*models.Stats, error) {
	execution, err := s.getLatestScanAllExecution(ctx, trigger...)
	if err != nil {
		return nil, err
	}

	sts := &models.Stats{}
	if execution != nil {
		metrics := execution.Metrics
		sts.Total = metrics.TaskCount
		sts.Completed = metrics.SuccessTaskCount
		sts.Ongoing = !job.Status(execution.Status).Final() || sts.Total != sts.Completed
		sts.Trigger = strings.Title(strings.ToLower(execution.Trigger))

		if execution.Metrics != nil {
			sts.Metrics = map[string]int64{
				"Pending": metrics.PendingTaskCount,
				"Running": metrics.RunningTaskCount,
				"Success": metrics.SuccessTaskCount,
				"Error":   metrics.ErrorTaskCount,
				"Stopped": metrics.StoppedTaskCount,
			}
		} else {
			sts.Metrics = map[string]int64{
				"Pending": 0,
				"Running": 0,
				"Success": 0,
				"Error":   0,
				"Stopped": 0,
			}
		}
	}

	return sts, nil
}

func (s *scanAllAPI) getLatestScanAllExecution(ctx context.Context, trigger ...string) (*task.Execution, error) {
	query := q.New(q.KeyWords{"vendor_type": job.ImageScanAllJob})
	if len(trigger) > 0 {
		query.Keywords["trigger"] = trigger[0]
	}

	executions, err := s.execMgr.List(ctx, query.First("-start_time"))
	if err != nil {
		return nil, err
	}

	if len(executions) == 0 {
		return nil, nil
	}

	return executions[0], nil
}

func (s *scanAllAPI) requireScanEnabled(ctx context.Context) error {
	kws := make(map[string]interface{})
	kws["is_default"] = true

	query := &q.Query{
		Keywords: kws,
	}

	l, err := s.scannerCtl.ListRegistrations(query)
	if err != nil {
		return errors.Wrap(err, "check if scan is enabled")
	}

	if len(l) == 0 {
		return errors.PreconditionFailedError(nil).WithMessage("no scanner is configured, it's not possible to scan")
	}

	return nil
}

func (s *scanAllAPI) requireAccess(ctx context.Context, action rbac.Action) error {
	if err := s.RequireSystemAccess(ctx, action, rbac.ResourceScanAll); err != nil {
		return err
	}
	if err := s.requireScanEnabled(ctx); err != nil {
		return err
	}
	return nil
}
<commit_msg>fix: correct completed in the metrics of the scan all (#14003)<commit_after>// Copyright Project Harbor Authors
//
// 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 handler

import (
	"context"
	"fmt"
	"strings"

	"github.com/go-openapi/runtime/middleware"
	"github.com/goharbor/harbor/src/common/rbac"
	"github.com/goharbor/harbor/src/controller/scan"
	"github.com/goharbor/harbor/src/controller/scanner"
	"github.com/goharbor/harbor/src/jobservice/job"
	"github.com/goharbor/harbor/src/lib/errors"
	"github.com/goharbor/harbor/src/lib/q"
	"github.com/goharbor/harbor/src/pkg/scheduler"
	"github.com/goharbor/harbor/src/pkg/task"
	"github.com/goharbor/harbor/src/server/v2.0/handler/model"
	"github.com/goharbor/harbor/src/server/v2.0/models"
	operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/scan_all"
)

func newScanAllAPI() *scanAllAPI {
	return &scanAllAPI{
		execMgr:    task.ExecMgr,
		scanCtl:    scan.DefaultController,
		scannerCtl: scanner.DefaultController,
		scheduler:  scheduler.Sched,
	}
}

type scanAllAPI struct {
	BaseAPI
	execMgr    task.ExecutionManager
	scanCtl    scan.Controller
	scannerCtl scanner.Controller
	scheduler  scheduler.Scheduler
}

func (s *scanAllAPI) Prepare(ctx context.Context, operation string, params interface{}) middleware.Responder {
	return nil
}

func (s *scanAllAPI) CreateScanAllSchedule(ctx context.Context, params operation.CreateScanAllScheduleParams) middleware.Responder {
	if err := s.requireAccess(ctx, rbac.ActionCreate); err != nil {
		return s.SendError(ctx, err)
	}

	req := params.Schedule

	if req.Schedule.Type == ScheduleNone {
		return operation.NewCreateScanAllScheduleCreated()
	}

	if req.Schedule.Type == ScheduleManual {
		execution, err := s.getLatestScanAllExecution(ctx, task.ExecutionTriggerManual)
		if err != nil {
			return s.SendError(ctx, err)
		}

		if execution != nil && execution.IsOnGoing() {
			message := fmt.Sprintf("a previous scan all job aleady exits, its status is %s", execution.Status)
			return s.SendError(ctx, errors.ConflictError(nil).WithMessage(message))
		}

		if _, err := s.scanCtl.ScanAll(ctx, task.ExecutionTriggerManual, true); err != nil {
			return s.SendError(ctx, err)
		}
	} else {
		schedule, err := s.getScanAllSchedule(ctx)
		if err != nil {
			return s.SendError(ctx, err)
		}

		if schedule != nil {
			message := "fail to set schedule for scan all as always had one, please delete it firstly then to re-schedule"
			return s.SendError(ctx, errors.PreconditionFailedError(nil).WithMessage(message))
		}

		if _, err := s.createOrUpdateScanAllSchedule(ctx, req.Schedule.Type, req.Schedule.Cron, nil); err != nil {
			return s.SendError(ctx, err)
		}
	}

	return operation.NewCreateScanAllScheduleCreated()
}

func (s *scanAllAPI) UpdateScanAllSchedule(ctx context.Context, params operation.UpdateScanAllScheduleParams) middleware.Responder {
	if err := s.requireAccess(ctx, rbac.ActionUpdate); err != nil {
		return s.SendError(ctx, err)
	}
	req := params.Schedule

	if req.Schedule.Type == ScheduleManual {
		message := fmt.Sprintf("fail to update scan all schedule as wrong schedule type: %s", req.Schedule.Type)
		return s.SendError(ctx, errors.BadRequestError(nil).WithMessage(message))
	}

	schedule, err := s.getScanAllSchedule(ctx)
	if err != nil {
		return s.SendError(ctx, err)
	}

	if req.Schedule.Type == ScheduleNone {
		if schedule != nil {
			err = s.scheduler.UnScheduleByID(ctx, schedule.ID)
		}
	} else {
		_, err = s.createOrUpdateScanAllSchedule(ctx, req.Schedule.Type, req.Schedule.Cron, schedule)
	}

	if err != nil {
		return s.SendError(ctx, err)
	}

	return operation.NewUpdateScanAllScheduleOK()
}

func (s *scanAllAPI) GetScanAllSchedule(ctx context.Context, params operation.GetScanAllScheduleParams) middleware.Responder {
	if err := s.requireAccess(ctx, rbac.ActionRead); err != nil {
		return s.SendError(ctx, err)
	}
	schedule, err := s.getScanAllSchedule(ctx)
	if err != nil {
		return s.SendError(ctx, err)
	}

	return operation.NewGetScanAllScheduleOK().WithPayload(model.NewSchedule(schedule).ToSwagger())
}

func (s *scanAllAPI) GetLatestScanAllMetrics(ctx context.Context, params operation.GetLatestScanAllMetricsParams) middleware.Responder {
	if err := s.requireAccess(ctx, rbac.ActionRead); err != nil {
		return s.SendError(ctx, err)
	}
	stats, err := s.getMetrics(ctx)
	if err != nil {
		return s.SendError(ctx, err)
	}

	return operation.NewGetLatestScanAllMetricsOK().WithPayload(stats)
}

func (s *scanAllAPI) GetLatestScheduledScanAllMetrics(ctx context.Context, params operation.GetLatestScheduledScanAllMetricsParams) middleware.Responder {
	if err := s.requireAccess(ctx, rbac.ActionRead); err != nil {
		return s.SendError(ctx, err)
	}
	stats, err := s.getMetrics(ctx, task.ExecutionTriggerSchedule)
	if err != nil {
		return s.SendError(ctx, err)
	}

	return operation.NewGetLatestScanAllMetricsOK().WithPayload(stats)
}

func (s *scanAllAPI) createOrUpdateScanAllSchedule(ctx context.Context, cronType, cron string, previous *scheduler.Schedule) (int64, error) {
	if previous != nil {
		if cronType == previous.CRONType && cron == previous.CRON {
			return previous.ID, nil
		}

		if err := s.scheduler.UnScheduleByID(ctx, previous.ID); err != nil {
			return 0, err
		}
	}

	return s.scheduler.Schedule(ctx, job.ImageScanAllJob, 0, cronType, cron, scan.ScanAllCallback, nil, nil)
}

func (s *scanAllAPI) getScanAllSchedule(ctx context.Context) (*scheduler.Schedule, error) {
	query := q.New(q.KeyWords{"vendor_type": job.ImageScanAllJob})
	schedules, err := s.scheduler.ListSchedules(ctx, query.First("-creation_time"))
	if err != nil {
		return nil, err
	}

	if len(schedules) > 1 {
		return nil, fmt.Errorf("found more than one scheduled scan all job, please ensure that only one schedule left")
	} else if len(schedules) == 0 {
		return nil, nil
	}

	return schedules[0], nil
}

func (s *scanAllAPI) getMetrics(ctx context.Context, trigger ...string) (*models.Stats, error) {
	execution, err := s.getLatestScanAllExecution(ctx, trigger...)
	if err != nil {
		return nil, err
	}

	sts := &models.Stats{}
	if execution != nil {
		if execution.Metrics != nil {
			metrics := execution.Metrics

			sts.Total = metrics.TaskCount
			sts.Completed = metrics.SuccessTaskCount + metrics.ErrorTaskCount + metrics.StoppedTaskCount
			sts.Metrics = map[string]int64{
				"Pending": metrics.PendingTaskCount,
				"Running": metrics.RunningTaskCount,
				"Success": metrics.SuccessTaskCount,
				"Error":   metrics.ErrorTaskCount,
				"Stopped": metrics.StoppedTaskCount,
			}
		} else {
			sts.Total = 0
			sts.Completed = 0
			sts.Metrics = map[string]int64{
				"Pending": 0,
				"Running": 0,
				"Success": 0,
				"Error":   0,
				"Stopped": 0,
			}
		}

		sts.Ongoing = !job.Status(execution.Status).Final() || sts.Total != sts.Completed
		sts.Trigger = strings.Title(strings.ToLower(execution.Trigger))
	}

	return sts, nil
}

func (s *scanAllAPI) getLatestScanAllExecution(ctx context.Context, trigger ...string) (*task.Execution, error) {
	query := q.New(q.KeyWords{"vendor_type": job.ImageScanAllJob})
	if len(trigger) > 0 {
		query.Keywords["trigger"] = trigger[0]
	}

	executions, err := s.execMgr.List(ctx, query.First("-start_time"))
	if err != nil {
		return nil, err
	}

	if len(executions) == 0 {
		return nil, nil
	}

	return executions[0], nil
}

func (s *scanAllAPI) requireScanEnabled(ctx context.Context) error {
	kws := make(map[string]interface{})
	kws["is_default"] = true

	query := &q.Query{
		Keywords: kws,
	}

	l, err := s.scannerCtl.ListRegistrations(query)
	if err != nil {
		return errors.Wrap(err, "check if scan is enabled")
	}

	if len(l) == 0 {
		return errors.PreconditionFailedError(nil).WithMessage("no scanner is configured, it's not possible to scan")
	}

	return nil
}

func (s *scanAllAPI) requireAccess(ctx context.Context, action rbac.Action) error {
	if err := s.RequireSystemAccess(ctx, action, rbac.ResourceScanAll); err != nil {
		return err
	}
	if err := s.requireScanEnabled(ctx); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package linux

import (
	"errors"
	"io/ioutil"
	"strconv"
	"strings"
)

type LoadAvg struct {
	Last1Min       float64 `json:"last1min"`
	Last5Min       float64 `json:"last5min"`
	Last15Min      float64 `json:"last15min"`
	ProcessRunning uint64  `json:"process_running"`
	ProcessTotal   uint64  `json:"process_total"`
	LastPID        uint64  `json:"last_pid"`
}

func ReadLoadAvg(path string) (*LoadAvg, error) {

	b, err := ioutil.ReadFile(path)

	if err != nil {
		return nil, err
	}

	content := strings.TrimSpace(string(b))
	fields := strings.Fields(content)

	if len(fields) < 5 {
		return nil, errors.New("Cannot parse loadavg: " + content)
	}

	process := strings.Split(fields[3], "/")

	if len(process) != 2 {
		return nil, errors.New("Cannot parse loadavg: " + content)
	}

	loadavg := LoadAvg{}

	if loadavg.Last1Min, err = strconv.ParseFloat(fields[0], 64); err != nil {
		return nil, err
	}

	if loadavg.Last5Min, err = strconv.ParseFloat(fields[1], 64); err != nil {
		return nil, err
	}

	if loadavg.Last15Min, err = strconv.ParseFloat(fields[2], 64); err != nil {
		return nil, err
	}

	if loadavg.ProcessRunning, err = strconv.ParseUint(process[0], 10, 64); err != nil {
		return nil, err
	}

	if loadavg.ProcessTotal, err = strconv.ParseUint(process[1], 10, 64); err != nil {
		return nil, err
	}

	if loadavg.LastPID, err = strconv.ParseUint(fields[4], 10, 64); err != nil {
		return nil, err
	}

	return &loadavg, nil
}
<commit_msg>updates<commit_after>package linux

import (
	"bytes"
	"fmt"
	"io/ioutil"
)

type LoadAvg struct {
	Last1Min       float64 `json:"last1min"`
	Last5Min       float64 `json:"last5min"`
	Last15Min      float64 `json:"last15min"`
	ProcessRunning uint64  `json:"process_running"`
	ProcessTotal   uint64  `json:"process_total"`
	LastPID        uint64  `json:"last_pid"`
}

func ReadLoadAvgFromBytes(data []byte) (*LoadAvg, error) {

	loadavg := LoadAvg{}

	if _, err := fmt.Fscan(
		bytes.NewReader(data),
		&loadavg.Last1Min,
		&loadavg.Last5Min,
		&loadavg.Last15Min,
		&loadavg.ProcessRunning,
		&loadavg.ProcessTotal,
		&loadavg.LastPID,
	); err != nil {
		return nil, err
	}

	return &loadavg, nil
}

func ReadLoadAvg(path string) (*LoadAvg, error) {

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

	return ReadLoadAvgFromBytes(data)

}
<|endoftext|>


<commit_before>package editor

import (
	"image"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/components"
)

const maxImageEntries = 70

// LowResolutionAnimeImages ...
func LowResolutionAnimeImages(ctx *aero.Context) string {
	basePath := path.Join(arn.Root, "images/anime/original/")
	files, err := ioutil.ReadDir(basePath)

	if err != nil {
		return ctx.Error(http.StatusInternalServerError, "Error reading anime images directory", err)
	}

	lowResAnime := []*arn.Anime{}

	for _, file := range files {
		if file.IsDir() || strings.HasPrefix(file.Name(), ".") {
			continue
		}

		fullPath := path.Join(basePath, file.Name())
		width, height, _ := getImageDimensions(fullPath)

		if width < arn.AnimeImageLargeWidth*2 || height < arn.AnimeImageLargeHeight*2 {
			animeID := file.Name()
			animeID = strings.TrimSuffix(animeID, filepath.Ext(animeID))

			anime, err := arn.GetAnime(animeID)

			if err == nil {
				lowResAnime = append(lowResAnime, anime)
			}
		}
	}

	count := len(lowResAnime)

	if count > maxImageEntries {
		lowResAnime = lowResAnime[:maxImageEntries]
	}

	return ctx.HTML(components.AnimeEditorListFull(
		"Anime with low resolution images",
		lowResAnime,
		count,
		"/editor/anime/missing/hiresimage",
		func(anime *arn.Anime) string {
			return "https://www.google.com/search?q=" + anime.Title.Canonical + "&tbm=isch"
		},
	))
}

// getImageDimensions retrieves the dimensions for the given file path.
func getImageDimensions(imagePath string) (int, int, error) {
	file, err := os.Open(imagePath)
	defer file.Close()

	if err != nil {
		return 0, 0, err
	}

	image, _, err := image.DecodeConfig(file)

	if err != nil {
		return 0, 0, err
	}

	return image.Width, image.Height, nil
}
<commit_msg>Added sort to low res filter<commit_after>package editor

import (
	"image"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/components"
)

const maxImageEntries = 70

// LowResolutionAnimeImages ...
func LowResolutionAnimeImages(ctx *aero.Context) string {
	basePath := path.Join(arn.Root, "images/anime/original/")
	files, err := ioutil.ReadDir(basePath)

	if err != nil {
		return ctx.Error(http.StatusInternalServerError, "Error reading anime images directory", err)
	}

	lowResAnime := []*arn.Anime{}

	for _, file := range files {
		if file.IsDir() || strings.HasPrefix(file.Name(), ".") {
			continue
		}

		fullPath := path.Join(basePath, file.Name())
		width, height, _ := getImageDimensions(fullPath)

		if width < arn.AnimeImageLargeWidth*2 || height < arn.AnimeImageLargeHeight*2 {
			animeID := file.Name()
			animeID = strings.TrimSuffix(animeID, filepath.Ext(animeID))

			anime, err := arn.GetAnime(animeID)

			if err == nil {
				lowResAnime = append(lowResAnime, anime)
			}
		}
	}

	// Sort
	arn.SortAnimeByQuality(lowResAnime)

	// Limit
	count := len(lowResAnime)

	if count > maxImageEntries {
		lowResAnime = lowResAnime[:maxImageEntries]
	}

	return ctx.HTML(components.AnimeEditorListFull(
		"Anime with low resolution images",
		lowResAnime,
		count,
		"/editor/anime/missing/hiresimage",
		func(anime *arn.Anime) string {
			return "https://www.google.com/search?q=" + anime.Title.Canonical + "&tbm=isch"
		},
	))
}

// getImageDimensions retrieves the dimensions for the given file path.
func getImageDimensions(imagePath string) (int, int, error) {
	file, err := os.Open(imagePath)
	defer file.Close()

	if err != nil {
		return 0, 0, err
	}

	image, _, err := image.DecodeConfig(file)

	if err != nil {
		return 0, 0, err
	}

	return image.Width, image.Height, nil
}
<|endoftext|>