<commit_before>package thumbnailer

import (
	"crypto/sha1"
	"fmt"
	"github.com/abustany/gollery/monitor"
	"github.com/abustany/gollery/utils"
	"github.com/gographics/imagick/imagick"
	"github.com/robfig/revel"
	"io"
	"os"
	"path"
	"runtime"
	"strings"
	"sync"
	"time"
)

const (
	THUMBNAILER_SIZE_PX             = 200
	THUMBNAILER_COMPRESSION_QUALITY = 75
	THUMBNAILER_CLEANUP_JOB_ID      = "@cleanup"
)

type Thumbnailer struct {
	RootDir           string
	CacheDir          string
	thumbnailingQueue chan string
	queuedItems       map[string]bool
	queuedItemsMutex  sync.Mutex
	monitor           *monitor.Monitor
	monitorEvents     chan monitor.Event
}

func init() {
	imagick.Initialize()
}

func makeCacheKey(path string) string {
	h := sha1.New()
	io.WriteString(h, path)
	key := fmt.Sprintf("%.0x", h.Sum(nil))
	return key
}

// returns the list of thumb keys from this directory
func (t *Thumbnailer) checkCacheDir(dirPath string) ([]string, error) {
	dirFd, err := os.Open(dirPath)

	revel.INFO.Printf("Cleaning cache for directory '%s'", dirPath)

	if err != nil {
		return nil, utils.WrapError(err, "Cannot open directory '%s'", dirPath)
	}

	defer dirFd.Close()

	fis, err := dirFd.Readdir(-1)

	if err == io.EOF {
		return nil, nil
	}

	if err != nil {
		return nil, utils.WrapError(err, "Cannot read directory '%s'", dirPath)
	}

	thumbsToCreate := []string{}
	allThumbKeys := []string{}

	for _, f := range fis {
		fPath := path.Join(dirPath, f.Name())

		if f.IsDir() {
			childThumbKeys, err := t.checkCacheDir(fPath)

			if err != nil {
				revel.WARN.Printf("Cannot clean cache directory '%s': %s (skipping)", fPath, err)
			}

			allThumbKeys = append(allThumbKeys, childThumbKeys...)

			continue
		}

		fId := fPath[1+len(t.RootDir):]

		revel.TRACE.Printf("Checking thumbnail for %s", fId)

		cacheKey := makeCacheKey(fId)

		allThumbKeys = append(allThumbKeys, cacheKey)

		cacheFilePath := path.Join(t.CacheDir, cacheKey)

		_, err := os.Stat(cacheFilePath)

		if os.IsNotExist(err) {
			thumbsToCreate = append(thumbsToCreate, fPath)
			continue
		}

		if err != nil {
			revel.WARN.Printf("Error while checking thumbnail for '%s': %s (skipping)", fPath, err)
			continue
		}
	}

	for _, x := range thumbsToCreate {
		t.ScheduleThumbnail(x)
	}

	return allThumbKeys, nil
}

func (t *Thumbnailer) monitorEventsRoutine() {
	for x := range t.monitorEvents {
		if ev, ok := x.(*monitor.DeleteEvent); ok {
			if ev.IsDirectory {
				t.ScheduleThumbnail(THUMBNAILER_CLEANUP_JOB_ID)
				continue
			}

			t.DeleteThumbnail(ev.Path())
			continue
		}

		if ev, ok := x.(*monitor.CreateEvent); ok {
			if ev.Info.Mode().IsRegular() {
				t.ScheduleThumbnail(ev.Path())
			} else if ev.Info.IsDir() {
				err := t.monitor.Watch(ev.Path())

				if err != nil {
					revel.ERROR.Printf("Cannot setup a file monitor on %s: %s", ev.Path(), err)
					continue
				}

				_, err = t.checkCacheDir(ev.Path())

				if err != nil {
					revel.ERROR.Printf("Cannot create thumbnails for directory '%s': %s", ev.Path(), err)
				}
			}
			continue
		}
	}
}

func (t *Thumbnailer) thumbnailQueueRoutine() {
	for filePath := range t.thumbnailingQueue {
		t.queuedItemsMutex.Lock()
		delete(t.queuedItems, filePath)
		t.queuedItemsMutex.Unlock()

		if filePath == THUMBNAILER_CLEANUP_JOB_ID {
			t.CheckCache()
			continue
		}

		err := t.CreateThumbnail(filePath)

		if err != nil {
			revel.ERROR.Printf("Couldn't create thumbnail for file '%s': %s", filePath, err)
		}

		revel.INFO.Printf("The thumbnailing queue now has %d items", len(t.thumbnailingQueue))
	}
}

func NewThumbnailer(rootDir string, cacheDir string, mon *monitor.Monitor) (*Thumbnailer, error) {
	t := &Thumbnailer{
		RootDir:           rootDir,
		CacheDir:          cacheDir,
		thumbnailingQueue: make(chan string, 256),
		queuedItems:       make(map[string]bool, 256),
		monitor:           mon,
		monitorEvents:     make(chan monitor.Event, 256),
	}

	revel.INFO.Printf("Starting %d thumbnailer routines", runtime.NumCPU())

	for i := 0; i < runtime.NumCPU(); i++ {
		go t.thumbnailQueueRoutine()
	}

	mon.Listen(t.monitorEvents)

	go t.monitorEventsRoutine()

	return t, nil
}

// Creates missing thumbnails
func (t *Thumbnailer) CheckCache() error {
	revel.INFO.Printf("Starting cache cleanup")

	allThumbKeys, err := t.checkCacheDir(t.RootDir)

	if err != nil {
		return utils.WrapError(err, "Cannot check thumbnail cache")
	}

	keyHash := make(map[string]bool, len(allThumbKeys))

	for _, key := range allThumbKeys {
		keyHash[key] = true
	}

	fd, err := os.Open(t.CacheDir)

	if err != nil {
		return utils.WrapError(err, "Cannot check thumbnail cache for stall thumbnails")
	}

	defer fd.Close()

	for {
		// Don't read all files at all, it might be a lot
		fis, err := fd.Readdir(1024)

		if err == io.EOF {
			break
		}

		if err != nil {
			return utils.WrapError(err, "Cannot list thumbnails while cleaning cache")
		}

		for _, fi := range fis {
			// Thumbnail corresponds to a known picture, leave it alone
			if _, exists := keyHash[fi.Name()]; exists {
				continue
			}

			thumbPath := path.Join(t.CacheDir, fi.Name())

			revel.INFO.Printf("Removing stale thumbnail with key %s", fi.Name())
			err = os.Remove(thumbPath)

			if err != nil && !os.IsNotExist(err) {
				return utils.WrapError(err, "Cannot delete thumbnail '%s' while cleaning up cache", thumbPath)
			}
		}
	}

	return nil
}

func (t *Thumbnailer) ScheduleThumbnail(filePath string) {
	revel.INFO.Printf("Scheduling thumbnailing of file %s", filePath)

	t.queuedItemsMutex.Lock()
	defer t.queuedItemsMutex.Unlock()

	if _, alreadyQueued := t.queuedItems[filePath]; alreadyQueued {
		revel.INFO.Printf("Thumbnailing already scheduled for file %s", filePath)
		return
	}

	t.thumbnailingQueue <- filePath
	t.queuedItems[filePath] = true
}

// For absolute paths, check that they are in the root dir
// For relative paths, prepend the root dir path
func (t *Thumbnailer) normalizePath(filePath string) (string, error) {
	if len(filePath) > 0 && filePath[0] == '/' {
		if !strings.HasPrefix(filePath, t.RootDir) {
			return "", fmt.Errorf("Not creating a thumbnail for a file outside the root directory: %s", filePath)
		}

		return filePath, nil
	}

	return path.Join(t.RootDir, filePath), nil
}

func (t *Thumbnailer) CreateThumbnail(filePath string) error {
	normalizedPath, err := t.normalizePath(filePath)

	if err != nil {
		return utils.WrapError(err, "Invalid path '%s'", filePath)
	}

	fileId := normalizedPath[1+len(t.RootDir):]

	startTime := time.Now()

	mw := imagick.NewMagickWand()
	defer mw.Destroy()

	err = mw.ReadImage(normalizedPath)

	if err != nil {
		return utils.WrapError(err, "Cannot read file '%s'", normalizedPath)
	}

	thumbKey := makeCacheKey(fileId)
	thumbPath := path.Join(t.CacheDir, thumbKey)

	width := mw.GetImageWidth()
	height := mw.GetImageHeight()
	var scale float32

	if width > height {
		scale = float32(THUMBNAILER_SIZE_PX) / float32(width)
		width = THUMBNAILER_SIZE_PX
		height = uint(float32(height) * scale)
	} else {
		scale = float32(THUMBNAILER_SIZE_PX) / float32(height)
		height = THUMBNAILER_SIZE_PX
		width = uint(float32(width) * scale)
	}

	// TRIANGLE is a simple linear interpolation, should be fast enough
	err = mw.ResizeImage(width, height, imagick.FILTER_TRIANGLE, 1)

	if err != nil {
		return utils.WrapError(err, "Cannot generate thumbnail for file '%s'", normalizedPath)
	}

	err = mw.SetCompressionQuality(THUMBNAILER_COMPRESSION_QUALITY)

	if err != nil {
		return utils.WrapError(err, "Cannot set compression quality for file '%s'", normalizedPath)
	}

	err = mw.WriteImage(thumbPath)

	if err != nil {
		return utils.WrapError(err, "Cannot write thumbnail '%s' for file '%s'", thumbPath, normalizedPath)
	}

	revel.INFO.Printf("Thumbnailed image '%s' as '%s' in %.2f seconds", normalizedPath, thumbPath, time.Now().Sub(startTime).Seconds())

	return nil
}

func (t *Thumbnailer) DeleteThumbnail(filePath string) error {
	normalizedPath, err := t.normalizePath(filePath)

	if err != nil {
		return utils.WrapError(err, "Invalid path '%s'", normalizedPath)
	}

	fileId := normalizedPath[1+len(t.RootDir):]
	thumbKey := makeCacheKey(fileId)
	thumbPath := path.Join(t.CacheDir, thumbKey)

	err = os.Remove(thumbPath)

	if os.IsNotExist(err) {
		return nil
	}

	if err != nil {
		return utils.WrapError(err, "Cannot remove thumbnail")
	}

	revel.INFO.Printf("Deleted thumbnail for image '%s'", normalizedPath)

	return nil
}

func (t *Thumbnailer) ThumbnailQueueSize() int {
	return len(t.queuedItems)
}
<commit_msg>thumbnailer: Also skip hidden files on initial cache check<commit_after>package thumbnailer

import (
	"crypto/sha1"
	"fmt"
	"github.com/abustany/gollery/monitor"
	"github.com/abustany/gollery/utils"
	"github.com/gographics/imagick/imagick"
	"github.com/robfig/revel"
	"io"
	"os"
	"path"
	"runtime"
	"strings"
	"sync"
	"time"
)

const (
	THUMBNAILER_SIZE_PX             = 200
	THUMBNAILER_COMPRESSION_QUALITY = 75
	THUMBNAILER_CLEANUP_JOB_ID      = "@cleanup"
)

type Thumbnailer struct {
	RootDir           string
	CacheDir          string
	thumbnailingQueue chan string
	queuedItems       map[string]bool
	queuedItemsMutex  sync.Mutex
	monitor           *monitor.Monitor
	monitorEvents     chan monitor.Event
}

func init() {
	imagick.Initialize()
}

func makeCacheKey(path string) string {
	h := sha1.New()
	io.WriteString(h, path)
	key := fmt.Sprintf("%.0x", h.Sum(nil))
	return key
}

// returns the list of thumb keys from this directory
func (t *Thumbnailer) checkCacheDir(dirPath string) ([]string, error) {
	dirFd, err := os.Open(dirPath)

	revel.INFO.Printf("Cleaning cache for directory '%s'", dirPath)

	if err != nil {
		return nil, utils.WrapError(err, "Cannot open directory '%s'", dirPath)
	}

	defer dirFd.Close()

	fis, err := dirFd.Readdir(-1)

	if err == io.EOF {
		return nil, nil
	}

	if err != nil {
		return nil, utils.WrapError(err, "Cannot read directory '%s'", dirPath)
	}

	thumbsToCreate := []string{}
	allThumbKeys := []string{}

	for _, f := range fis {
		fPath := path.Join(dirPath, f.Name())

		if strings.HasPrefix(f.Name(), ".") {
			revel.TRACE.Printf("Skipping hidden file %s while checking thumbnails", fPath)
			continue
		}

		if f.IsDir() {
			childThumbKeys, err := t.checkCacheDir(fPath)

			if err != nil {
				revel.WARN.Printf("Cannot clean cache directory '%s': %s (skipping)", fPath, err)
			}

			allThumbKeys = append(allThumbKeys, childThumbKeys...)

			continue
		}

		fId := fPath[1+len(t.RootDir):]

		revel.TRACE.Printf("Checking thumbnail for %s", fId)

		cacheKey := makeCacheKey(fId)

		allThumbKeys = append(allThumbKeys, cacheKey)

		cacheFilePath := path.Join(t.CacheDir, cacheKey)

		_, err := os.Stat(cacheFilePath)

		if os.IsNotExist(err) {
			thumbsToCreate = append(thumbsToCreate, fPath)
			continue
		}

		if err != nil {
			revel.WARN.Printf("Error while checking thumbnail for '%s': %s (skipping)", fPath, err)
			continue
		}
	}

	for _, x := range thumbsToCreate {
		t.ScheduleThumbnail(x)
	}

	return allThumbKeys, nil
}

func (t *Thumbnailer) monitorEventsRoutine() {
	for x := range t.monitorEvents {
		if ev, ok := x.(*monitor.DeleteEvent); ok {
			if ev.IsDirectory {
				t.ScheduleThumbnail(THUMBNAILER_CLEANUP_JOB_ID)
				continue
			}

			t.DeleteThumbnail(ev.Path())
			continue
		}

		if ev, ok := x.(*monitor.CreateEvent); ok {
			if ev.Info.Mode().IsRegular() {
				t.ScheduleThumbnail(ev.Path())
			} else if ev.Info.IsDir() {
				err := t.monitor.Watch(ev.Path())

				if err != nil {
					revel.ERROR.Printf("Cannot setup a file monitor on %s: %s", ev.Path(), err)
					continue
				}

				_, err = t.checkCacheDir(ev.Path())

				if err != nil {
					revel.ERROR.Printf("Cannot create thumbnails for directory '%s': %s", ev.Path(), err)
				}
			}
			continue
		}
	}
}

func (t *Thumbnailer) thumbnailQueueRoutine() {
	for filePath := range t.thumbnailingQueue {
		t.queuedItemsMutex.Lock()
		delete(t.queuedItems, filePath)
		t.queuedItemsMutex.Unlock()

		if filePath == THUMBNAILER_CLEANUP_JOB_ID {
			t.CheckCache()
			continue
		}

		err := t.CreateThumbnail(filePath)

		if err != nil {
			revel.ERROR.Printf("Couldn't create thumbnail for file '%s': %s", filePath, err)
		}

		revel.INFO.Printf("The thumbnailing queue now has %d items", len(t.thumbnailingQueue))
	}
}

func NewThumbnailer(rootDir string, cacheDir string, mon *monitor.Monitor) (*Thumbnailer, error) {
	t := &Thumbnailer{
		RootDir:           rootDir,
		CacheDir:          cacheDir,
		thumbnailingQueue: make(chan string, 256),
		queuedItems:       make(map[string]bool, 256),
		monitor:           mon,
		monitorEvents:     make(chan monitor.Event, 256),
	}

	revel.INFO.Printf("Starting %d thumbnailer routines", runtime.NumCPU())

	for i := 0; i < runtime.NumCPU(); i++ {
		go t.thumbnailQueueRoutine()
	}

	mon.Listen(t.monitorEvents)

	go t.monitorEventsRoutine()

	return t, nil
}

// Creates missing thumbnails
func (t *Thumbnailer) CheckCache() error {
	revel.INFO.Printf("Starting cache cleanup")

	allThumbKeys, err := t.checkCacheDir(t.RootDir)

	if err != nil {
		return utils.WrapError(err, "Cannot check thumbnail cache")
	}

	keyHash := make(map[string]bool, len(allThumbKeys))

	for _, key := range allThumbKeys {
		keyHash[key] = true
	}

	fd, err := os.Open(t.CacheDir)

	if err != nil {
		return utils.WrapError(err, "Cannot check thumbnail cache for stall thumbnails")
	}

	defer fd.Close()

	for {
		// Don't read all files at all, it might be a lot
		fis, err := fd.Readdir(1024)

		if err == io.EOF {
			break
		}

		if err != nil {
			return utils.WrapError(err, "Cannot list thumbnails while cleaning cache")
		}

		for _, fi := range fis {
			// Thumbnail corresponds to a known picture, leave it alone
			if _, exists := keyHash[fi.Name()]; exists {
				continue
			}

			thumbPath := path.Join(t.CacheDir, fi.Name())

			revel.INFO.Printf("Removing stale thumbnail with key %s", fi.Name())
			err = os.Remove(thumbPath)

			if err != nil && !os.IsNotExist(err) {
				return utils.WrapError(err, "Cannot delete thumbnail '%s' while cleaning up cache", thumbPath)
			}
		}
	}

	return nil
}

func (t *Thumbnailer) ScheduleThumbnail(filePath string) {
	revel.INFO.Printf("Scheduling thumbnailing of file %s", filePath)

	t.queuedItemsMutex.Lock()
	defer t.queuedItemsMutex.Unlock()

	if _, alreadyQueued := t.queuedItems[filePath]; alreadyQueued {
		revel.INFO.Printf("Thumbnailing already scheduled for file %s", filePath)
		return
	}

	t.thumbnailingQueue <- filePath
	t.queuedItems[filePath] = true
}

// For absolute paths, check that they are in the root dir
// For relative paths, prepend the root dir path
func (t *Thumbnailer) normalizePath(filePath string) (string, error) {
	if len(filePath) > 0 && filePath[0] == '/' {
		if !strings.HasPrefix(filePath, t.RootDir) {
			return "", fmt.Errorf("Not creating a thumbnail for a file outside the root directory: %s", filePath)
		}

		return filePath, nil
	}

	return path.Join(t.RootDir, filePath), nil
}

func (t *Thumbnailer) CreateThumbnail(filePath string) error {
	normalizedPath, err := t.normalizePath(filePath)

	if err != nil {
		return utils.WrapError(err, "Invalid path '%s'", filePath)
	}

	fileId := normalizedPath[1+len(t.RootDir):]

	startTime := time.Now()

	mw := imagick.NewMagickWand()
	defer mw.Destroy()

	err = mw.ReadImage(normalizedPath)

	if err != nil {
		return utils.WrapError(err, "Cannot read file '%s'", normalizedPath)
	}

	thumbKey := makeCacheKey(fileId)
	thumbPath := path.Join(t.CacheDir, thumbKey)

	width := mw.GetImageWidth()
	height := mw.GetImageHeight()
	var scale float32

	if width > height {
		scale = float32(THUMBNAILER_SIZE_PX) / float32(width)
		width = THUMBNAILER_SIZE_PX
		height = uint(float32(height) * scale)
	} else {
		scale = float32(THUMBNAILER_SIZE_PX) / float32(height)
		height = THUMBNAILER_SIZE_PX
		width = uint(float32(width) * scale)
	}

	// TRIANGLE is a simple linear interpolation, should be fast enough
	err = mw.ResizeImage(width, height, imagick.FILTER_TRIANGLE, 1)

	if err != nil {
		return utils.WrapError(err, "Cannot generate thumbnail for file '%s'", normalizedPath)
	}

	err = mw.SetCompressionQuality(THUMBNAILER_COMPRESSION_QUALITY)

	if err != nil {
		return utils.WrapError(err, "Cannot set compression quality for file '%s'", normalizedPath)
	}

	err = mw.WriteImage(thumbPath)

	if err != nil {
		return utils.WrapError(err, "Cannot write thumbnail '%s' for file '%s'", thumbPath, normalizedPath)
	}

	revel.INFO.Printf("Thumbnailed image '%s' as '%s' in %.2f seconds", normalizedPath, thumbPath, time.Now().Sub(startTime).Seconds())

	return nil
}

func (t *Thumbnailer) DeleteThumbnail(filePath string) error {
	normalizedPath, err := t.normalizePath(filePath)

	if err != nil {
		return utils.WrapError(err, "Invalid path '%s'", normalizedPath)
	}

	fileId := normalizedPath[1+len(t.RootDir):]
	thumbKey := makeCacheKey(fileId)
	thumbPath := path.Join(t.CacheDir, thumbKey)

	err = os.Remove(thumbPath)

	if os.IsNotExist(err) {
		return nil
	}

	if err != nil {
		return utils.WrapError(err, "Cannot remove thumbnail")
	}

	revel.INFO.Printf("Deleted thumbnail for image '%s'", normalizedPath)

	return nil
}

func (t *Thumbnailer) ThumbnailQueueSize() int {
	return len(t.queuedItems)
}
<|endoftext|>


<commit_before>package middleware

import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/labstack/echo"
)

type (
	// CSRFConfig defines the config for CSRF middleware.
	CSRFConfig struct {
		// Key to create CSRF token.
		Secret []byte `json:"secret"`

		// TokenLookup is a string in the form of "<source>:<key>" that is used
		// to extract token from the request.
		// Optional. Default value "header:X-CSRF-Token".
		// Possible values:
		// - "header:<name>"
		// - "form:<name>"
		// - "query:<name>"
		TokenLookup string `json:"token_lookup"`

		// Context key to store generated CSRF token into context.
		// Optional. Default value "csrf".
		ContextKey string `json:"context_key"`

		// Name of the CSRF cookie. This cookie will store CSRF token.
		// Optional. Default value "csrf".
		CookieName string `json:"cookie_name"`

		// Domain of the CSRF cookie.
		// Optional. Default value none.
		CookieDomain string `json:"cookie_domain"`

		// Path of the CSRF cookie.
		// Optional. Default value none.
		CookiePath string `json:"cookie_path"`

		// Max age (in seconds) of the CSRF cookie.
		// Optional. Default value 86400 (24hr).
		CookieMaxAge int `json:"cookie_max_age"`

		// Indicates if CSRF cookie is secure.
		// Optional. Default value false.
		CookieSecure bool `json:"cookie_secure"`
	}

	// csrfTokenExtractor defines a function that takes `echo.Context` and returns
	// either a token or an error.
	csrfTokenExtractor func(echo.Context) (string, error)
)

var (
	// DefaultCSRFConfig is the default CSRF middleware config.
	DefaultCSRFConfig = CSRFConfig{
		TokenLookup:  "header:" + echo.HeaderXCSRFToken,
		ContextKey:   "csrf",
		CookieName:   "_csrf",
		CookieMaxAge: 86400,
	}
)

// CSRF returns a Cross-Site Request Forgery (CSRF) middleware.
// See: https://en.wikipedia.org/wiki/Cross-site_request_forgery
func CSRF(secret []byte) echo.MiddlewareFunc {
	c := DefaultCSRFConfig
	c.Secret = secret
	return CSRFWithConfig(c)
}

// CSRFWithConfig returns a CSRF middleware from config.
// See `CSRF()`.
func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
	// Defaults
	if config.Secret == nil {
		panic("csrf secret must be provided")
	}
	if config.TokenLookup == "" {
		config.TokenLookup = DefaultCSRFConfig.TokenLookup
	}
	if config.ContextKey == "" {
		config.ContextKey = DefaultCSRFConfig.ContextKey
	}
	if config.CookieName == "" {
		config.CookieName = DefaultCSRFConfig.CookieName
	}
	if config.CookieMaxAge == 0 {
		config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge
	}

	// Initialize
	parts := strings.Split(config.TokenLookup, ":")
	extractor := csrfTokenFromHeader(parts[1])
	switch parts[0] {
	case "form":
		extractor = csrfTokenFromForm(parts[1])
	case "query":
		extractor = csrfTokenFromQuery(parts[1])
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			req := c.Request()

			// Set CSRF token
			salt, err := generateSalt(8)
			if err != nil {
				return err
			}
			token := generateCSRFToken(config.Secret, salt)
			c.Set(config.ContextKey, token)

			switch req.Method() {
			case echo.GET, echo.HEAD, echo.OPTIONS, echo.TRACE:
				cookie := new(echo.Cookie)
				cookie.SetName(config.CookieName)
				cookie.SetValue(token)
				if config.CookiePath != "" {
					cookie.SetPath(config.CookiePath)
				}
				if config.CookieDomain != "" {
					cookie.SetDomain(config.CookieDomain)
				}
				cookie.SetExpires(time.Now().Add(time.Duration(config.CookieMaxAge) * time.Second))
				cookie.SetSecure(config.CookieSecure)
				cookie.SetHTTPOnly(true)
				c.SetCookie(cookie)
			default:
				cookie, err := c.Cookie(config.CookieName)
				if err != nil {
					return err
				}
				serverToken := cookie.Value()
				clientToken, err := extractor(c)
				if err != nil {
					return err
				}
				ok, err := validateCSRFToken(serverToken, clientToken, config.Secret)
				if err != nil {
					return err
				}
				if !ok {
					return echo.NewHTTPError(http.StatusForbidden, "invalid csrf token")
				}
			}
			return next(c)
		}
	}
}

// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
// provided request header.
func csrfTokenFromHeader(header string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		return c.Request().Header().Get(header), nil
	}
}

// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
// provided form parameter.
func csrfTokenFromForm(param string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		token := c.FormValue(param)
		if token == "" {
			return "", errors.New("empty csrf token in form param")
		}
		return token, nil
	}
}

// csrfTokenFromQuery returns a `csrfTokenExtractor` that extracts token from the
// provided query parameter.
func csrfTokenFromQuery(param string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		token := c.QueryParam(param)
		if token == "" {
			return "", errors.New("empty csrf token in query param")
		}
		return token, nil
	}
}

func generateCSRFToken(secret, salt []byte) string {
	h := hmac.New(sha1.New, secret)
	h.Write(salt)
	return fmt.Sprintf("%s:%s", hex.EncodeToString(h.Sum(nil)), hex.EncodeToString(salt))
}

func validateCSRFToken(serverToken, clientToken string, secret []byte) (bool, error) {
	if serverToken != clientToken {
		return false, nil
	}
	sep := strings.Index(clientToken, ":")
	if sep < 0 {
		return false, nil
	}
	salt, err := hex.DecodeString(clientToken[sep+1:])
	if err != nil {
		return false, err
	}
	return clientToken == generateCSRFToken(secret, salt), nil
}

func generateSalt(len uint8) (salt []byte, err error) {
	salt = make([]byte, len)
	_, err = rand.Read(salt)
	return
}
<commit_msg>Generate CSRF token only if it is expired (#601)<commit_after>package middleware

import (
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/labstack/echo"
)

type (
	// CSRFConfig defines the config for CSRF middleware.
	CSRFConfig struct {
		// Key to create CSRF token.
		Secret []byte `json:"secret"`

		// TokenLookup is a string in the form of "<source>:<key>" that is used
		// to extract token from the request.
		// Optional. Default value "header:X-CSRF-Token".
		// Possible values:
		// - "header:<name>"
		// - "form:<name>"
		// - "query:<name>"
		TokenLookup string `json:"token_lookup"`

		// Context key to store generated CSRF token into context.
		// Optional. Default value "csrf".
		ContextKey string `json:"context_key"`

		// Name of the CSRF cookie. This cookie will store CSRF token.
		// Optional. Default value "csrf".
		CookieName string `json:"cookie_name"`

		// Domain of the CSRF cookie.
		// Optional. Default value none.
		CookieDomain string `json:"cookie_domain"`

		// Path of the CSRF cookie.
		// Optional. Default value none.
		CookiePath string `json:"cookie_path"`

		// Max age (in seconds) of the CSRF cookie.
		// Optional. Default value 86400 (24hr).
		CookieMaxAge int `json:"cookie_max_age"`

		// Indicates if CSRF cookie is secure.
		// Optional. Default value false.
		CookieSecure bool `json:"cookie_secure"`
	}

	// csrfTokenExtractor defines a function that takes `echo.Context` and returns
	// either a token or an error.
	csrfTokenExtractor func(echo.Context) (string, error)
)

var (
	// DefaultCSRFConfig is the default CSRF middleware config.
	DefaultCSRFConfig = CSRFConfig{
		TokenLookup:  "header:" + echo.HeaderXCSRFToken,
		ContextKey:   "csrf",
		CookieName:   "_csrf",
		CookieMaxAge: 86400,
	}
)

// CSRF returns a Cross-Site Request Forgery (CSRF) middleware.
// See: https://en.wikipedia.org/wiki/Cross-site_request_forgery
func CSRF(secret []byte) echo.MiddlewareFunc {
	c := DefaultCSRFConfig
	c.Secret = secret
	return CSRFWithConfig(c)
}

// CSRFWithConfig returns a CSRF middleware from config.
// See `CSRF()`.
func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
	// Defaults
	if config.Secret == nil {
		panic("csrf secret must be provided")
	}
	if config.TokenLookup == "" {
		config.TokenLookup = DefaultCSRFConfig.TokenLookup
	}
	if config.ContextKey == "" {
		config.ContextKey = DefaultCSRFConfig.ContextKey
	}
	if config.CookieName == "" {
		config.CookieName = DefaultCSRFConfig.CookieName
	}
	if config.CookieMaxAge == 0 {
		config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge
	}

	// Initialize
	parts := strings.Split(config.TokenLookup, ":")
	extractor := csrfTokenFromHeader(parts[1])
	switch parts[0] {
	case "form":
		extractor = csrfTokenFromForm(parts[1])
	case "query":
		extractor = csrfTokenFromQuery(parts[1])
	}

	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			req := c.Request()
			cookie, err := c.Cookie(config.CookieName)
			token := ""

			if err != nil {
				// Token expired, generate it
				salt, err := generateSalt(8)
				if err != nil {
					return err
				}
				token = generateCSRFToken(config.Secret, salt)
				cookie := new(echo.Cookie)
				cookie.SetName(config.CookieName)
				cookie.SetValue(token)
				if config.CookiePath != "" {
					cookie.SetPath(config.CookiePath)
				}
				if config.CookieDomain != "" {
					cookie.SetDomain(config.CookieDomain)
				}
				cookie.SetExpires(time.Now().Add(time.Duration(config.CookieMaxAge) * time.Second))
				cookie.SetSecure(config.CookieSecure)
				cookie.SetHTTPOnly(true)
				c.SetCookie(cookie)
			} else {
				// Reuse token
				token = cookie.Value()
			}

			c.Set(config.ContextKey, token)

			switch req.Method() {
			case echo.GET, echo.HEAD, echo.OPTIONS, echo.TRACE:
			default:
				clientToken, err := extractor(c)
				if err != nil {
					return err
				}
				ok, err := validateCSRFToken(token, clientToken, config.Secret)
				if err != nil {
					return err
				}
				if !ok {
					return echo.NewHTTPError(http.StatusForbidden, "invalid csrf token")
				}
			}
			return next(c)
		}
	}
}

// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
// provided request header.
func csrfTokenFromHeader(header string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		return c.Request().Header().Get(header), nil
	}
}

// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
// provided form parameter.
func csrfTokenFromForm(param string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		token := c.FormValue(param)
		if token == "" {
			return "", errors.New("empty csrf token in form param")
		}
		return token, nil
	}
}

// csrfTokenFromQuery returns a `csrfTokenExtractor` that extracts token from the
// provided query parameter.
func csrfTokenFromQuery(param string) csrfTokenExtractor {
	return func(c echo.Context) (string, error) {
		token := c.QueryParam(param)
		if token == "" {
			return "", errors.New("empty csrf token in query param")
		}
		return token, nil
	}
}

func generateCSRFToken(secret, salt []byte) string {
	h := hmac.New(sha1.New, secret)
	h.Write(salt)
	return fmt.Sprintf("%s:%s", hex.EncodeToString(h.Sum(nil)), hex.EncodeToString(salt))
}

func validateCSRFToken(serverToken, clientToken string, secret []byte) (bool, error) {
	if serverToken != clientToken {
		return false, nil
	}
	sep := strings.Index(clientToken, ":")
	if sep < 0 {
		return false, nil
	}
	salt, err := hex.DecodeString(clientToken[sep+1:])
	if err != nil {
		return false, err
	}
	return clientToken == generateCSRFToken(secret, salt), nil
}

func generateSalt(len uint8) (salt []byte, err error) {
	salt = make([]byte, len)
	_, err = rand.Read(salt)
	return
}
<|endoftext|>


<commit_before>package todotxt

import (
        "time"
        "os"
        "bufio"
        "strings"
        "regexp"
        "sort"
        "unicode"
        "fmt"
        "math/rand"
)

type Task struct {
        id int
        todo string
        priority byte
        create_date time.Time
        contexts []string
        projects []string
        raw_todo string
        finished bool
        finish_date time.Time
        id_padding int
}

type TaskList []Task

func ParseTask(text string, id int) (Task) {
        var task = Task{}
        task.id = id
        task.raw_todo = text

        splits := strings.Split(text, " ")

        // checking if the task is already finished
        if text[0] == 'x' &&
           text[1] == ' ' &&
           !unicode.IsSpace(rune(text[2])) {
                task.finished = true
                splits = splits[1:]
        }

        date_regexp := "([\\d]{4})-([\\d]{2})-([\\d]{2})"

        // checking for finish date
        if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                        panic(e)
                } else {
                        task.finish_date = date
                }

                splits = splits[1:]
        }

        head := splits[0]

        // checking for priority
        if (len(head) == 3) &&
           (head[0] == '(') &&
           (head[2] == ')') &&
           (head[1] >= 65 && head[1] <= 90) { // checking if it's in range [A-Z]
                task.priority = head[1]
                splits = splits[1:]
        }

        // checking for creation date and building the actual todo item
        if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                        panic(e)
                } else {
                        task.create_date = date
                }

                task.todo = strings.Join(splits[1:], " ")
        } else {
                task.todo = strings.Join(splits[0:], " ")
        }

        context_regexp, _ := regexp.Compile("@[[:word:]]+")
        contexts := context_regexp.FindAllStringSubmatch(text, -1)
        if len(contexts) != 0 {
                task.contexts = contexts[0]
        }

        project_regexp, _ := regexp.Compile("\\+[[:word:]]+")
        projects := project_regexp.FindAllStringSubmatch(text, -1)
        if len(projects) != 0 {
                task.projects = projects[0]
        }

        return task
}

func LoadTaskList (filename string) (TaskList) {

        var f, err = os.Open(filename)

        if err != nil {
                panic(err)
        }

        defer f.Close()

        var tasklist = TaskList{}

        scanner := bufio.NewScanner(f)

        for scanner.Scan() {
                text := scanner.Text()
                tasklist.Add(text)
        }

        if err := scanner.Err(); err != nil {
                panic(scanner.Err())
        }

        return tasklist
}

type By func(t1, t2 Task) bool

func (by By) Sort(tasks TaskList) {
        ts := &taskSorter{
                tasks: tasks,
                by:    by,
        }
        sort.Sort(ts)
}

type taskSorter struct {
        tasks TaskList
        by func(t1, t2 Task) bool
}

func (s *taskSorter) Len() int {
        return len(s.tasks)
}

func (s *taskSorter) Swap(i, j int) {
        s.tasks[i], s.tasks[j] = s.tasks[j], s.tasks[i]
}

func (s *taskSorter) Less(i, j int) bool {
        return s.by(s.tasks[i], s.tasks[j])
}

func (tasks TaskList) Len() int {
        return len(tasks)
}

func prioCmp(t1, t2 Task) bool {
        return t1.Priority() < t2.Priority()
}

func prioRevCmp(t1, t2 Task) bool {
        return t1.Priority() > t2.Priority()
}

func dateCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 > tm2
        }
}

func dateRevCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 < tm2
        }
}

func lenCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 < tl2
        }
}

func lenRevCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 > tl2
        }
}

func idCmp(t1, t2 Task) bool {
        return t1.Id() < t2.Id()
}

func randCmp(t1, t2 Task) bool {
        rand.Seed(time.Now().UnixNano()%1e6/1e3)
        return rand.Intn(len(t1.raw_todo)) > rand.Intn(len(t2.raw_todo))
}

func (tasks TaskList) Sort(by string) {
        switch by {
        default:
        case "prio":
                By(prioCmp).Sort(tasks)
        case "prio-rev":
                By(prioRevCmp).Sort(tasks)
        case "date":
                By(dateCmp).Sort(tasks)
        case "date-rev":
                By(dateRevCmp).Sort(tasks)
        case "len":
                By(lenCmp).Sort(tasks)
        case "len-rev":
                By(lenRevCmp).Sort(tasks)
        case "id":
                By(idCmp).Sort(tasks)
        case "rand":
                By(randCmp).Sort(tasks)
        }
}

func (tasks TaskList) Save(filename string) {
        tasks.Sort("id")

        f, err := os.Create(filename)
        if err != nil {
                panic(err)
        }

        defer f.Close()

        for _, task := range tasks {
                f.WriteString(task.RawText() + "\n")
        }
        f.Sync()
}

func (tasks *TaskList) Add(todo string) {
        task := ParseTask(todo, tasks.Len())
        *tasks = append(*tasks, task)
}

func (tasks TaskList) Done(id int, finish_date bool) error {
        if id > tasks.Len() || id < 0 {
                return fmt.Errorf("Error: id is %v", id)
        }

        tasks[id].finished = true
        if finish_date {
                t := time.Now()
                tasks[id].raw_todo = "x " + t.Format("2006-01-02") + " " +
                                        tasks[id].raw_todo
        } else {
                tasks[id].raw_todo = "x " + tasks[id].raw_todo
        }

        return nil
}

func (task Task) Id() int {
        return task.id
}

func (task Task) Text() string {
        return task.todo
}

func (task Task) RawText() string {
        return task.raw_todo
}

func (task Task) Priority() byte {
        // if priority is not from [A-Z], let it be 94 (^)
        if task.priority < 65 || task.priority > 90 {
                return 94 // you know, ^
        } else {
                return task.priority
        }
}

func (task Task) Contexts() []string {
        return task.contexts
}

func (task Task) Projects() []string {
        return task.projects
}

func (task Task) CreateDate() time.Time {
        return task.create_date
}

func (task Task) Finished() bool {
        return task.finished
}

func (task Task) FinishDate() time.Time {
        return task.finish_date
}

func (task *Task) SetIdPaddingBy(tasklist TaskList) {
        l := tasklist.Len()

        if l >= 10000 {
                task.id_padding = 5
        } else if l >= 1000 {
                task.id_padding = 4
        } else if l >= 100 {
                task.id_padding = 3
        } else if l >= 10 {
                task.id_padding = 2
        } else {
                task.id_padding = 1
        }
}

func (task *Task) RebuildRawTodo() {
        if task.finished {
                task.raw_todo = task.PrettyPrint("x %P%t")
        } else {
                task.raw_todo = task.PrettyPrint("%P%t")
        }
}

func (task *Task) SetPriority(prio byte) {
        if task.priority < 65 || task.priority > 90 {
                task.priority = '^'
        } else {
                task.priority = prio
        }
}

func (task *Task) SetTodo(todo string) {
        task.todo = todo
}

func (task Task) IdPadding() int {
        return task.id_padding
}

func pad(in string, length int) string {
        if (length == -1) {
                return in
        }

        if (length > len(in)) {
                return strings.Repeat(" ", length - len(in)) + in
        } else {
                return in[:length]
        }
}

func (task Task) PrettyPrint(pretty string) string {
        rp := regexp.MustCompile("%(\\.\\d+|)([a-zA-Z])")
        padding := -1
        out := rp.ReplaceAllStringFunc(pretty, func(s string) string {
                if (len(s) > 1 && s[0] == '.') {
                        fmt.Scanf(s, "%d", &padding)
                        return ""
                }

                ret := s
                switch s{
                case "%i":
                        str := fmt.Sprintf("%%0%dd", task.IdPadding())
                        ret = fmt.Sprintf(str, task.Id())
                case "%t":
                        ret = task.Text()
                case "%T":
                        ret = task.RawText()
                case "%p":
                        ret = string(task.Priority())
                case "%P":
                        if task.Priority() != '^' {
                                ret = "(" + string(task.Priority()) + ") "
                        } else {
                                ret = ""
                        }
                default:
                        ret = s
                }

                ret = pad(ret, padding)
                padding = -1
                return ret
        })
        return out
}
<commit_msg>finished padding<commit_after>package todotxt

import (
        "time"
        "os"
        "bufio"
        "strings"
        "regexp"
        "sort"
        "unicode"
        "fmt"
        "math/rand"
)

type Task struct {
        id int
        todo string
        priority byte
        create_date time.Time
        contexts []string
        projects []string
        raw_todo string
        finished bool
        finish_date time.Time
        id_padding int
}

type TaskList []Task

func ParseTask(text string, id int) (Task) {
        var task = Task{}
        task.id = id
        task.raw_todo = text

        splits := strings.Split(text, " ")

        // checking if the task is already finished
        if text[0] == 'x' &&
           text[1] == ' ' &&
           !unicode.IsSpace(rune(text[2])) {
                task.finished = true
                splits = splits[1:]
        }

        date_regexp := "([\\d]{4})-([\\d]{2})-([\\d]{2})"

        // checking for finish date
        if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                        panic(e)
                } else {
                        task.finish_date = date
                }

                splits = splits[1:]
        }

        head := splits[0]

        // checking for priority
        if (len(head) == 3) &&
           (head[0] == '(') &&
           (head[2] == ')') &&
           (head[1] >= 65 && head[1] <= 90) { // checking if it's in range [A-Z]
                task.priority = head[1]
                splits = splits[1:]
        }

        // checking for creation date and building the actual todo item
        if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                        panic(e)
                } else {
                        task.create_date = date
                }

                task.todo = strings.Join(splits[1:], " ")
        } else {
                task.todo = strings.Join(splits[0:], " ")
        }

        context_regexp, _ := regexp.Compile("@[[:word:]]+")
        contexts := context_regexp.FindAllStringSubmatch(text, -1)
        if len(contexts) != 0 {
                task.contexts = contexts[0]
        }

        project_regexp, _ := regexp.Compile("\\+[[:word:]]+")
        projects := project_regexp.FindAllStringSubmatch(text, -1)
        if len(projects) != 0 {
                task.projects = projects[0]
        }

        return task
}

func LoadTaskList (filename string) (TaskList) {

        var f, err = os.Open(filename)

        if err != nil {
                panic(err)
        }

        defer f.Close()

        var tasklist = TaskList{}

        scanner := bufio.NewScanner(f)

        for scanner.Scan() {
                text := scanner.Text()
                tasklist.Add(text)
        }

        if err := scanner.Err(); err != nil {
                panic(scanner.Err())
        }

        return tasklist
}

type By func(t1, t2 Task) bool

func (by By) Sort(tasks TaskList) {
        ts := &taskSorter{
                tasks: tasks,
                by:    by,
        }
        sort.Sort(ts)
}

type taskSorter struct {
        tasks TaskList
        by func(t1, t2 Task) bool
}

func (s *taskSorter) Len() int {
        return len(s.tasks)
}

func (s *taskSorter) Swap(i, j int) {
        s.tasks[i], s.tasks[j] = s.tasks[j], s.tasks[i]
}

func (s *taskSorter) Less(i, j int) bool {
        return s.by(s.tasks[i], s.tasks[j])
}

func (tasks TaskList) Len() int {
        return len(tasks)
}

func prioCmp(t1, t2 Task) bool {
        return t1.Priority() < t2.Priority()
}

func prioRevCmp(t1, t2 Task) bool {
        return t1.Priority() > t2.Priority()
}

func dateCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 > tm2
        }
}

func dateRevCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 < tm2
        }
}

func lenCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 < tl2
        }
}

func lenRevCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 > tl2
        }
}

func idCmp(t1, t2 Task) bool {
        return t1.Id() < t2.Id()
}

func randCmp(t1, t2 Task) bool {
        rand.Seed(time.Now().UnixNano()%1e6/1e3)
        return rand.Intn(len(t1.raw_todo)) > rand.Intn(len(t2.raw_todo))
}

func (tasks TaskList) Sort(by string) {
        switch by {
        default:
        case "prio":
                By(prioCmp).Sort(tasks)
        case "prio-rev":
                By(prioRevCmp).Sort(tasks)
        case "date":
                By(dateCmp).Sort(tasks)
        case "date-rev":
                By(dateRevCmp).Sort(tasks)
        case "len":
                By(lenCmp).Sort(tasks)
        case "len-rev":
                By(lenRevCmp).Sort(tasks)
        case "id":
                By(idCmp).Sort(tasks)
        case "rand":
                By(randCmp).Sort(tasks)
        }
}

func (tasks TaskList) Save(filename string) {
        tasks.Sort("id")

        f, err := os.Create(filename)
        if err != nil {
                panic(err)
        }

        defer f.Close()

        for _, task := range tasks {
                f.WriteString(task.RawText() + "\n")
        }
        f.Sync()
}

func (tasks *TaskList) Add(todo string) {
        task := ParseTask(todo, tasks.Len())
        *tasks = append(*tasks, task)
}

func (tasks TaskList) Done(id int, finish_date bool) error {
        if id > tasks.Len() || id < 0 {
                return fmt.Errorf("Error: id is %v", id)
        }

        tasks[id].finished = true
        if finish_date {
                t := time.Now()
                tasks[id].raw_todo = "x " + t.Format("2006-01-02") + " " +
                                        tasks[id].raw_todo
        } else {
                tasks[id].raw_todo = "x " + tasks[id].raw_todo
        }

        return nil
}

func (task Task) Id() int {
        return task.id
}

func (task Task) Text() string {
        return task.todo
}

func (task Task) RawText() string {
        return task.raw_todo
}

func (task Task) Priority() byte {
        // if priority is not from [A-Z], let it be 94 (^)
        if task.priority < 65 || task.priority > 90 {
                return 94 // you know, ^
        } else {
                return task.priority
        }
}

func (task Task) Contexts() []string {
        return task.contexts
}

func (task Task) Projects() []string {
        return task.projects
}

func (task Task) CreateDate() time.Time {
        return task.create_date
}

func (task Task) Finished() bool {
        return task.finished
}

func (task Task) FinishDate() time.Time {
        return task.finish_date
}

func (task *Task) SetIdPaddingBy(tasklist TaskList) {
        l := tasklist.Len()

        if l >= 10000 {
                task.id_padding = 5
        } else if l >= 1000 {
                task.id_padding = 4
        } else if l >= 100 {
                task.id_padding = 3
        } else if l >= 10 {
                task.id_padding = 2
        } else {
                task.id_padding = 1
        }
}

func (task *Task) RebuildRawTodo() {
        if task.finished {
                task.raw_todo = task.PrettyPrint("x %P%t")
        } else {
                task.raw_todo = task.PrettyPrint("%P%t")
        }
}

func (task *Task) SetPriority(prio byte) {
        if task.priority < 65 || task.priority > 90 {
                task.priority = '^'
        } else {
                task.priority = prio
        }
}

func (task *Task) SetTodo(todo string) {
        task.todo = todo
}

func (task Task) IdPadding() int {
        return task.id_padding
}

func pad(in string, length int) string {
        if (length == -1) {
                return in
        }

        if (length > len(in)) {
                return strings.Repeat(" ", length - len(in)) + in
        } else {
                return in[:length]
        }
}

func (task Task) PrettyPrint(pretty string) string {
        rp := regexp.MustCompile("(%(\\.\\d+|)[a-zA-Z])")
        padding := -1
        out := rp.ReplaceAllStringFunc(pretty, func(s string) string {
                if (len(s) < 2) {
                        return ""
                }

                var f string
                if (s[0] == '%' && s[1] == '.') {
                        if _, e := fmt.Sscanf(s[2:], "%d%s", &padding, &f); e != nil {
                                panic(e);
                        }
                        f = "%" + f
                } else {
                        f = s
                }

                ret := s
                switch f {
                case "%i":
                        str := fmt.Sprintf("%%0%dd", task.IdPadding())
                        ret = fmt.Sprintf(str, task.Id())
                case "%t":
                        ret = task.Text()
                case "%T":
                        ret = task.RawText()
                case "%p":
                        ret = string(task.Priority())
                case "%P":
                        if task.Priority() != '^' {
                                ret = "(" + string(task.Priority()) + ") "
                        } else {
                                ret = ""
                        }
                default:
                        ret = s
                }

                ret = pad(ret, padding)
                padding = -1
                return ret
        })
        return out
}
<|endoftext|>


<commit_before>package realms

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	log "github.com/Sirupsen/logrus"
	"github.com/google/go-github/github"
	"github.com/matrix-org/go-neb/database"
	"github.com/matrix-org/go-neb/services/github/client"
	"github.com/matrix-org/go-neb/types"
	"io/ioutil"
	"net/http"
	"net/url"
)

// GithubRealm can handle OAuth processes with github.com
type GithubRealm struct {
	id           string
	redirectURL  string
	ClientSecret string
	ClientID     string
	StarterLink  string
}

// GithubSession represents an authenticated github session
type GithubSession struct {
	// The client-supplied URL to redirect them to after the auth process is complete.
	ClientsRedirectURL string
	// AccessToken is the github access token for the user
	AccessToken string
	// Scopes are the set of *ALLOWED* scopes (which may not be the same as the requested scopes)
	Scopes  string
	id      string
	userID  string
	realmID string
}

// Authenticated returns true if the user has completed the auth process
func (s *GithubSession) Authenticated() bool {
	return s.AccessToken != ""
}

// Info returns a list of possible repositories that this session can integrate with.
func (s *GithubSession) Info() interface{} {
	logger := log.WithFields(log.Fields{
		"user_id":  s.userID,
		"realm_id": s.realmID,
	})
	cli := client.New(s.AccessToken)
	// query for a list of possible projects
	rs, _, err := cli.Repositories.List("", &github.RepositoryListOptions{
		Type: "all",
	})
	if err != nil {
		logger.WithError(err).Print("Failed to query github projects on github.com")
		return nil
	}

	var repos []client.TrimmedRepository

	for _, r := range rs {
		repos = append(repos, client.TrimRepository(r))
	}

	return struct {
		Repos []client.TrimmedRepository
	}{repos}
}

// UserID returns the user_id who authorised with Github
func (s *GithubSession) UserID() string {
	return s.userID
}

// RealmID returns the realm ID of the realm which performed the authentication
func (s *GithubSession) RealmID() string {
	return s.realmID
}

// ID returns the session ID
func (s *GithubSession) ID() string {
	return s.id
}

// ID returns the realm ID
func (r *GithubRealm) ID() string {
	return r.id
}

// Type is github
func (r *GithubRealm) Type() string {
	return "github"
}

// Init does nothing.
func (r *GithubRealm) Init() error {
	return nil
}

// Register does nothing.
func (r *GithubRealm) Register() error {
	return nil
}

// RequestAuthSession generates an OAuth2 URL for this user to auth with github via.
func (r *GithubRealm) RequestAuthSession(userID string, req json.RawMessage) interface{} {
	state, err := randomString(10)
	if err != nil {
		log.WithError(err).Print("Failed to generate state param")
		return nil
	}

	u, _ := url.Parse("https://github.com/login/oauth/authorize")
	q := u.Query()
	q.Set("client_id", r.ClientID)
	q.Set("client_secret", r.ClientSecret)
	q.Set("state", state)
	q.Set("redirect_uri", r.redirectURL)
	u.RawQuery = q.Encode()
	session := &GithubSession{
		id:      state, // key off the state for redirects
		userID:  userID,
		realmID: r.ID(),
	}

	// check if they supplied a redirect URL
	var reqBody struct {
		RedirectURL string
	}
	if err = json.Unmarshal(req, &reqBody); err != nil {
		log.WithError(err).Print("Failed to decode request body")
		return nil
	}
	session.ClientsRedirectURL = reqBody.RedirectURL

	_, err = database.GetServiceDB().StoreAuthSession(session)
	if err != nil {
		log.WithError(err).Print("Failed to store new auth session")
		return nil
	}

	return &struct {
		URL string
	}{u.String()}
}

// OnReceiveRedirect processes OAuth redirect requests from Github
func (r *GithubRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) {
	// parse out params from the request
	code := req.URL.Query().Get("code")
	state := req.URL.Query().Get("state")
	logger := log.WithFields(log.Fields{
		"state": state,
	})
	logger.WithField("code", code).Print("GithubRealm: OnReceiveRedirect")
	if code == "" || state == "" {
		failWith(logger, w, 400, "code and state are required", nil)
		return
	}
	// load the session (we keyed off the state param)
	session, err := database.GetServiceDB().LoadAuthSessionByID(r.ID(), state)
	if err != nil {
		// most likely cause
		failWith(logger, w, 400, "Provided ?state= param is not recognised.", err)
		return
	}
	ghSession, ok := session.(*GithubSession)
	if !ok {
		failWith(logger, w, 500, "Unexpected session found.", nil)
		return
	}
	logger.WithField("user_id", ghSession.UserID()).Print("Mapped redirect to user")

	// exchange code for access_token
	res, err := http.PostForm("https://github.com/login/oauth/access_token",
		url.Values{"client_id": {r.ClientID}, "client_secret": {r.ClientSecret}, "code": {code}})
	if err != nil {
		failWith(logger, w, 502, "Failed to exchange code for token", err)
		return
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		failWith(logger, w, 502, "Failed to read token response", err)
		return
	}
	vals, err := url.ParseQuery(string(body))
	if err != nil {
		failWith(logger, w, 502, "Failed to parse token response", err)
		return
	}

	// update database and return
	ghSession.AccessToken = vals.Get("access_token")
	ghSession.Scopes = vals.Get("scope")
	logger.WithField("scope", ghSession.Scopes).Print("Scopes granted.")
	_, err = database.GetServiceDB().StoreAuthSession(ghSession)
	if err != nil {
		failWith(logger, w, 500, "Failed to persist session", err)
		return
	}
	if ghSession.ClientsRedirectURL != "" {
		w.WriteHeader(302)
		w.Header().Set("Location", ghSession.ClientsRedirectURL)
		// technically don't need a body but *shrug*
		w.Write([]byte(ghSession.ClientsRedirectURL))
	} else {
		w.WriteHeader(200)
		w.Write([]byte("You have successfully linked your Github account to " + ghSession.UserID()))
	}
}

// AuthSession returns a GithubSession for this user
func (r *GithubRealm) AuthSession(id, userID, realmID string) types.AuthSession {
	return &GithubSession{
		id:      id,
		userID:  userID,
		realmID: realmID,
	}
}

func failWith(logger *log.Entry, w http.ResponseWriter, code int, msg string, err error) {
	logger.WithError(err).Print(msg)
	w.WriteHeader(code)
	w.Write([]byte(msg))
}

// Generate a cryptographically secure pseudorandom string with the given number of bytes (length).
// Returns a hex string of the bytes.
func randomString(length int) (string, error) {
	b := make([]byte, length)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(b), nil
}

func init() {
	types.RegisterAuthRealm(func(realmID, redirectURL string) types.AuthRealm {
		return &GithubRealm{id: realmID, redirectURL: redirectURL}
	})
}
<commit_msg>Check for a valid session before exchanging codes<commit_after>package realms

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	log "github.com/Sirupsen/logrus"
	"github.com/google/go-github/github"
	"github.com/matrix-org/go-neb/database"
	"github.com/matrix-org/go-neb/services/github/client"
	"github.com/matrix-org/go-neb/types"
	"io/ioutil"
	"net/http"
	"net/url"
)

// GithubRealm can handle OAuth processes with github.com
type GithubRealm struct {
	id           string
	redirectURL  string
	ClientSecret string
	ClientID     string
	StarterLink  string
}

// GithubSession represents an authenticated github session
type GithubSession struct {
	// The client-supplied URL to redirect them to after the auth process is complete.
	ClientsRedirectURL string
	// AccessToken is the github access token for the user
	AccessToken string
	// Scopes are the set of *ALLOWED* scopes (which may not be the same as the requested scopes)
	Scopes  string
	id      string
	userID  string
	realmID string
}

// Authenticated returns true if the user has completed the auth process
func (s *GithubSession) Authenticated() bool {
	return s.AccessToken != ""
}

// Info returns a list of possible repositories that this session can integrate with.
func (s *GithubSession) Info() interface{} {
	logger := log.WithFields(log.Fields{
		"user_id":  s.userID,
		"realm_id": s.realmID,
	})
	cli := client.New(s.AccessToken)
	// query for a list of possible projects
	rs, _, err := cli.Repositories.List("", &github.RepositoryListOptions{
		Type: "all",
	})
	if err != nil {
		logger.WithError(err).Print("Failed to query github projects on github.com")
		return nil
	}

	var repos []client.TrimmedRepository

	for _, r := range rs {
		repos = append(repos, client.TrimRepository(r))
	}

	return struct {
		Repos []client.TrimmedRepository
	}{repos}
}

// UserID returns the user_id who authorised with Github
func (s *GithubSession) UserID() string {
	return s.userID
}

// RealmID returns the realm ID of the realm which performed the authentication
func (s *GithubSession) RealmID() string {
	return s.realmID
}

// ID returns the session ID
func (s *GithubSession) ID() string {
	return s.id
}

// ID returns the realm ID
func (r *GithubRealm) ID() string {
	return r.id
}

// Type is github
func (r *GithubRealm) Type() string {
	return "github"
}

// Init does nothing.
func (r *GithubRealm) Init() error {
	return nil
}

// Register does nothing.
func (r *GithubRealm) Register() error {
	return nil
}

// RequestAuthSession generates an OAuth2 URL for this user to auth with github via.
func (r *GithubRealm) RequestAuthSession(userID string, req json.RawMessage) interface{} {
	state, err := randomString(10)
	if err != nil {
		log.WithError(err).Print("Failed to generate state param")
		return nil
	}

	u, _ := url.Parse("https://github.com/login/oauth/authorize")
	q := u.Query()
	q.Set("client_id", r.ClientID)
	q.Set("client_secret", r.ClientSecret)
	q.Set("state", state)
	q.Set("redirect_uri", r.redirectURL)
	u.RawQuery = q.Encode()
	session := &GithubSession{
		id:      state, // key off the state for redirects
		userID:  userID,
		realmID: r.ID(),
	}

	// check if they supplied a redirect URL
	var reqBody struct {
		RedirectURL string
	}
	if err = json.Unmarshal(req, &reqBody); err != nil {
		log.WithError(err).Print("Failed to decode request body")
		return nil
	}
	session.ClientsRedirectURL = reqBody.RedirectURL

	_, err = database.GetServiceDB().StoreAuthSession(session)
	if err != nil {
		log.WithError(err).Print("Failed to store new auth session")
		return nil
	}

	return &struct {
		URL string
	}{u.String()}
}

// OnReceiveRedirect processes OAuth redirect requests from Github
func (r *GithubRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) {
	// parse out params from the request
	code := req.URL.Query().Get("code")
	state := req.URL.Query().Get("state")
	logger := log.WithFields(log.Fields{
		"state": state,
	})
	logger.WithField("code", code).Print("GithubRealm: OnReceiveRedirect")
	if code == "" || state == "" {
		failWith(logger, w, 400, "code and state are required", nil)
		return
	}
	// load the session (we keyed off the state param)
	session, err := database.GetServiceDB().LoadAuthSessionByID(r.ID(), state)
	if err != nil {
		// most likely cause
		failWith(logger, w, 400, "Provided ?state= param is not recognised.", err)
		return
	}
	ghSession, ok := session.(*GithubSession)
	if !ok {
		failWith(logger, w, 500, "Unexpected session found.", nil)
		return
	}
	logger.WithField("user_id", ghSession.UserID()).Print("Mapped redirect to user")

	if ghSession.AccessToken != "" && ghSession.Scopes != "" {
		failWith(logger, w, 400, "You have already authenticated with Github", nil)
		return
	}

	// exchange code for access_token
	res, err := http.PostForm("https://github.com/login/oauth/access_token",
		url.Values{"client_id": {r.ClientID}, "client_secret": {r.ClientSecret}, "code": {code}})
	if err != nil {
		failWith(logger, w, 502, "Failed to exchange code for token", err)
		return
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		failWith(logger, w, 502, "Failed to read token response", err)
		return
	}
	vals, err := url.ParseQuery(string(body))
	if err != nil {
		failWith(logger, w, 502, "Failed to parse token response", err)
		return
	}

	// update database and return
	ghSession.AccessToken = vals.Get("access_token")
	ghSession.Scopes = vals.Get("scope")
	logger.WithField("scope", ghSession.Scopes).Print("Scopes granted.")
	_, err = database.GetServiceDB().StoreAuthSession(ghSession)
	if err != nil {
		failWith(logger, w, 500, "Failed to persist session", err)
		return
	}
	if ghSession.ClientsRedirectURL != "" {
		w.WriteHeader(302)
		w.Header().Set("Location", ghSession.ClientsRedirectURL)
		// technically don't need a body but *shrug*
		w.Write([]byte(ghSession.ClientsRedirectURL))
	} else {
		w.WriteHeader(200)
		w.Write([]byte("You have successfully linked your Github account to " + ghSession.UserID()))
	}
}

// AuthSession returns a GithubSession for this user
func (r *GithubRealm) AuthSession(id, userID, realmID string) types.AuthSession {
	return &GithubSession{
		id:      id,
		userID:  userID,
		realmID: realmID,
	}
}

func failWith(logger *log.Entry, w http.ResponseWriter, code int, msg string, err error) {
	logger.WithError(err).Print(msg)
	w.WriteHeader(code)
	w.Write([]byte(msg))
}

// Generate a cryptographically secure pseudorandom string with the given number of bytes (length).
// Returns a hex string of the bytes.
func randomString(length int) (string, error) {
	b := make([]byte, length)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(b), nil
}

func init() {
	types.RegisterAuthRealm(func(realmID, redirectURL string) types.AuthRealm {
		return &GithubRealm{id: realmID, redirectURL: redirectURL}
	})
}
<|endoftext|>


<commit_before>package datadog

import (
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/zorkian/go-datadog-api"

	"github.com/hashicorp/terraform/helper/schema"
)

func resourceDatadogMonitor() *schema.Resource {
	return &schema.Resource{
		Create: resourceDatadogMonitorCreate,
		Read:   resourceDatadogMonitorRead,
		Update: resourceDatadogMonitorUpdate,
		Delete: resourceDatadogMonitorDelete,
		Exists: resourceDatadogMonitorExists,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			// Metric and Monitor settings
			"metric": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"metric_tags": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "*",
			},
			"time_aggr": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"time_window": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"space_aggr": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"operator": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"message": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			// Alert Settings
			"warning": &schema.Schema{
				Type:     schema.TypeMap,
				Required: true,
			},
			"critical": &schema.Schema{
				Type:     schema.TypeMap,
				Required: true,
			},

			// Additional Settings
			"notify_no_data": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
			},

			"no_data_timeframe": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
		},
	}
}

// TODO: Rename this one?
func buildMonitorStruct(d *schema.ResourceData, typeStr string) *datadog.Monitor {
	name := d.Get("name").(string)
	message := d.Get("message").(string)
	timeAggr := d.Get("time_aggr").(string)
	timeWindow := d.Get("time_window").(string)
	spaceAggr := d.Get("space_aggr").(string)
	metric := d.Get("metric").(string)
	tags := d.Get("metric_tags").(string)
	operator := d.Get("operator").(string)
	query := fmt.Sprintf("%s(%s):%s:%s{%s} %s %s", timeAggr, timeWindow, spaceAggr, metric, tags, operator, d.Get(fmt.Sprintf("%s.threshold", typeStr)))

	log.Println(query)

	o := datadog.Options{
		NotifyNoData: d.Get("notify_no_data").(bool),
		NoDataTimeframe: d.Get("no_data_timeframe").(int),
	}

	m := datadog.Monitor{
		Type:    "metric alert",
		Query:   query,
		Name:    fmt.Sprintf("[%s] %s", typeStr, name),
		Message: fmt.Sprintf("%s %s", message, d.Get(fmt.Sprintf("%s.notify", typeStr))),
		Options: o,
	}

	return &m
}

func resourceDatadogMonitorCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*datadog.Client)
	log.Printf("[DEBUG] XX running create.")

	w, w_err := client.CreateMonitor(buildMonitorStruct(d, "warning"))

	if w_err != nil {
		return fmt.Errorf("error creating warning: %s", w_err)
	}

	c, c_err := client.CreateMonitor(buildMonitorStruct(d, "critical"))

	if c_err != nil {
		return fmt.Errorf("error creating warning: %s", c_err)
	}

	log.Printf("[DEBUG] Saving IDs: %s__%s", strconv.Itoa(w.Id), strconv.Itoa(c.Id))

	d.SetId(fmt.Sprintf("%s__%s", strconv.Itoa(w.Id), strconv.Itoa(c.Id)))

	return nil
}

func resourceDatadogMonitorDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*datadog.Client)

	log.Printf("[DEBUG] XX running delete.")

	for _, v := range strings.Split(d.Id(), "__") {
		if v == "" {
			return fmt.Errorf("Id not set.")
		}
		Id, i_err := strconv.Atoi(v)

		if i_err != nil {
			return i_err
		}

		err := client.DeleteMonitor(Id)
		if err != nil {
			return err
		}
	}
	return nil
}

func resourceDatadogMonitorExists(d *schema.ResourceData, meta interface{}) (b bool, e error) {
	// Exists - This is called to verify a resource still exists. It is called prior to Read,
	// and lowers the burden of Read to be able to assume the resource exists.

	client := meta.(*datadog.Client)

	log.Printf("[DEBUG] XX running exists.")

	// Sanitise this one
	exists := true
	for _, v := range strings.Split(d.Id(), "__") {
		if v == "" {
			log.Printf("[DEBUG] Could not parse IDs. %s", v)
			return false, fmt.Errorf("Id not set.")
		}
		Id, i_err := strconv.Atoi(v)

		if i_err != nil {
			log.Printf("[DEBUG] Received error converting string %s", i_err)
			return false, i_err
		}
		_, err := client.GetMonitor(Id)
		if err != nil {
			// Monitor did does not exists, continue.
			log.Printf("[DEBUG] monitor does not exist. %s", err)
			e = err
			continue
		}
		exists = exists && true
	}
	if !exists {
		return false, resourceDatadogMonitorDelete(d, meta)
	}

	return true, nil
}

func resourceDatadogMonitorRead(d *schema.ResourceData, meta interface{}) error {
	return nil
}

func resourceDatadogMonitorUpdate(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] XX running update.")

	split := strings.Split(d.Id(), "__")

	wID, cID := split[0], split[1]

	if wID == "" {
		return fmt.Errorf("Id not set.")
	}

	if cID == "" {
		return fmt.Errorf("Id not set.")
	}

	warningId, i_err := strconv.Atoi(wID)

	if i_err != nil {
		return i_err
	}

	criticalId, i_err := strconv.Atoi(cID)

	if i_err != nil {
		return i_err
	}

	client := meta.(*datadog.Client)

	warning_body := buildMonitorStruct(d, "warning")
	critical_body := buildMonitorStruct(d, "critical")

	warning_body.Id = warningId
	critical_body.Id = criticalId

	w_err := client.UpdateMonitor(warning_body)

	if w_err != nil {
		return fmt.Errorf("error updating warning: %s", w_err.Error())
	}

	c_err := client.UpdateMonitor(critical_body)

	if c_err != nil {
		return fmt.Errorf("error updating critical: %s", c_err.Error())
	}

	return nil
}
<commit_msg>Update comments, fix monitor to detect deletion upstream.<commit_after>package datadog

import (
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/zorkian/go-datadog-api"

	"github.com/hashicorp/terraform/helper/schema"
)

func resourceDatadogMonitor() *schema.Resource {
	return &schema.Resource{
		Create: resourceDatadogMonitorCreate,
		Read:   resourceDatadogMonitorRead,
		Update: resourceDatadogMonitorUpdate,
		Delete: resourceDatadogMonitorDelete,
		Exists: resourceDatadogMonitorExists,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			// Metric and Monitor settings
			"metric": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"metric_tags": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "*",
			},
			"time_aggr": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"time_window": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"space_aggr": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"operator": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"message": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			// Alert Settings
			"warning": &schema.Schema{
				Type:     schema.TypeMap,
				Required: true,
			},
			"critical": &schema.Schema{
				Type:     schema.TypeMap,
				Required: true,
			},

			// Additional Settings
			"notify_no_data": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
			},

			"no_data_timeframe": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
		},
	}
}

// TODO: Rename this one?
func buildMonitorStruct(d *schema.ResourceData, typeStr string) *datadog.Monitor {
	name := d.Get("name").(string)
	message := d.Get("message").(string)
	timeAggr := d.Get("time_aggr").(string)
	timeWindow := d.Get("time_window").(string)
	spaceAggr := d.Get("space_aggr").(string)
	metric := d.Get("metric").(string)
	tags := d.Get("metric_tags").(string)
	operator := d.Get("operator").(string)
	query := fmt.Sprintf("%s(%s):%s:%s{%s} %s %s", timeAggr, timeWindow, spaceAggr, metric, tags, operator, d.Get(fmt.Sprintf("%s.threshold", typeStr)))

	log.Println(query)

	o := datadog.Options{
		NotifyNoData: d.Get("notify_no_data").(bool),
		NoDataTimeframe: d.Get("no_data_timeframe").(int),
	}

	m := datadog.Monitor{
		Type:    "metric alert",
		Query:   query,
		Name:    fmt.Sprintf("[%s] %s", typeStr, name),
		Message: fmt.Sprintf("%s %s", message, d.Get(fmt.Sprintf("%s.notify", typeStr))),
		Options: o,
	}

	return &m
}

func resourceDatadogMonitorCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*datadog.Client)
	log.Printf("[DEBUG] running create.")

	w, w_err := client.CreateMonitor(buildMonitorStruct(d, "warning"))

	if w_err != nil {
		return fmt.Errorf("error creating warning: %s", w_err)
	}

	c, c_err := client.CreateMonitor(buildMonitorStruct(d, "critical"))

	if c_err != nil {
		return fmt.Errorf("error creating warning: %s", c_err)
	}

	log.Printf("[DEBUG] Saving IDs: %s__%s", strconv.Itoa(w.Id), strconv.Itoa(c.Id))

	d.SetId(fmt.Sprintf("%s__%s", strconv.Itoa(w.Id), strconv.Itoa(c.Id)))

	return nil
}

func resourceDatadogMonitorDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*datadog.Client)

	log.Printf("[DEBUG] running delete.")

	for _, v := range strings.Split(d.Id(), "__") {
		if v == "" {
			return fmt.Errorf("Id not set.")
		}
		Id, i_err := strconv.Atoi(v)

		if i_err != nil {
			return i_err
		}

		err := client.DeleteMonitor(Id)
		if err != nil {
			return err
		}
	}
	return nil
}

func resourceDatadogMonitorExists(d *schema.ResourceData, meta interface{}) (b bool, e error) {
	// Exists - This is called to verify a resource still exists. It is called prior to Read,
	// and lowers the burden of Read to be able to assume the resource exists.

	client := meta.(*datadog.Client)

	log.Printf("[DEBUG] running exists.")

	// Sanitise this one
	exists := false
	for _, v := range strings.Split(d.Id(), "__") {
		if v == "" {
			log.Printf("[DEBUG] Could not parse IDs. %s", v)
			return false, fmt.Errorf("Id not set.")
		}
		Id, i_err := strconv.Atoi(v)

		if i_err != nil {
			log.Printf("[DEBUG] Received error converting string %s", i_err)
			return false, i_err
		}
		_, err := client.GetMonitor(Id)
		if err != nil {
			// Monitor did does not exist, continue.
			log.Printf("[DEBUG] monitor does not exist. %s", err)
			e = err
			continue
		}
		exists = true
	}

	if exists == false {
		return false, nil
	}

	return true, nil
}

func resourceDatadogMonitorRead(d *schema.ResourceData, meta interface{}) error {
	return nil
}

func resourceDatadogMonitorUpdate(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] running update.")

	split := strings.Split(d.Id(), "__")

	wID, cID := split[0], split[1]

	if wID == "" {
		return fmt.Errorf("Id not set.")
	}

	if cID == "" {
		return fmt.Errorf("Id not set.")
	}

	warningId, i_err := strconv.Atoi(wID)

	if i_err != nil {
		return i_err
	}

	criticalId, i_err := strconv.Atoi(cID)

	if i_err != nil {
		return i_err
	}

	client := meta.(*datadog.Client)

	warning_body := buildMonitorStruct(d, "warning")
	critical_body := buildMonitorStruct(d, "critical")

	warning_body.Id = warningId
	critical_body.Id = criticalId

	w_err := client.UpdateMonitor(warning_body)

	if w_err != nil {
		return fmt.Errorf("error updating warning: %s", w_err.Error())
	}

	c_err := client.UpdateMonitor(critical_body)

	if c_err != nil {
		return fmt.Errorf("error updating critical: %s", c_err.Error())
	}

	return nil
}
<|endoftext|>


<commit_before>package frames

import "fmt"

// TEXT houses anything just for a TEXT frame
type TEXT struct {
	Frame
}

// DisplayContent will comprehensively display known information
func (t *TEXT) DisplayContent() string {
	return fmt.Sprintf("[%s - %d] (%s) %s\n", t.Name, t.Size, t.Description, t.Cleaned)
}

// GetName will add deprecated notes where appropriate based on version
func (t *TEXT) GetName() string {
	out := t.Name
	if t.Version == Version4 && (t.Name == "TDAT" ||
		t.Name == "TIME" ||
		t.Name == "TORY" ||
		t.Name == "TRDA" ||
		t.Name == "TSIZ" ||
		t.Name == "TYER") {
		out += " (deprecated)"
	}

	return out
}

// ProcessData will handle the acquisition of all data
func (t *TEXT) ProcessData(s int, d []byte) IFrame {
	t.Size = s
	t.Data = d

	// text encoding is a single byte, 0 for latin, 1 for unicode
	if len(d) > 1 {
		if d[0] == '\x01' {
			t.Utf16 = true
		}
		d = d[1:]

		if !t.Utf16 {
			t.Frame.Cleaned = GetStr(d)
		} else {
			t.Frame.Cleaned = GetUnicodeStr(d)
		}
	}

	return t
}
<commit_msg>Use a little less straight logic<commit_after>package frames

import "fmt"

// TEXT houses anything just for a TEXT frame
type TEXT struct {
	Frame
}

// DisplayContent will comprehensively display known information
func (t *TEXT) DisplayContent() string {
	return fmt.Sprintf("[%s - %d] (%s) %s\n", t.Name, t.Size, t.Description, t.Cleaned)
}

// GetName will add deprecated notes where appropriate based on version
func (t *TEXT) GetName() string {
	out := t.Name
	if t.Version == Version4 {
		deprecated := map[string]bool{
			"TDAT": true,
			"TIME": true,
			"TORY": true,
			"TRDA": true,
			"TSIZ": true,
			"TYER": true,
		}

		if _, found := deprecated[t.Name]; found {
			out += " (deprecated)"
		}
	}

	return out
}

// ProcessData will handle the acquisition of all data
func (t *TEXT) ProcessData(s int, d []byte) IFrame {
	t.Size = s
	t.Data = d

	// text encoding is a single byte, 0 for latin, 1 for unicode
	if len(d) > 1 {
		if d[0] == '\x01' {
			t.Utf16 = true
		}
		d = d[1:]

		if !t.Utf16 {
			t.Frame.Cleaned = GetStr(d)
		} else {
			t.Frame.Cleaned = GetUnicodeStr(d)
		}
	}

	return t
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Santiago Arias | Remy Jourde
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package tournaments

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"appengine"
	"appengine/taskqueue"

	"github.com/taironas/route"

	"github.com/santiaago/gonawin/helpers"
	"github.com/santiaago/gonawin/helpers/log"
	templateshlp "github.com/santiaago/gonawin/helpers/templates"

	mdl "github.com/santiaago/gonawin/models"
)

// Tournament add admin handler:
//
// Use this handler to add a user as admin of current tournament.
//	GET	/j/tournaments/[0-9]+/admin/add/
//
func AddAdmin(w http.ResponseWriter, r *http.Request, u *mdl.User) error {
	c := appengine.NewContext(r)
	desc := "Tournament add admin Handler:"
	if r.Method == "POST" {
		// get tournament id and user id
		strTournamentId, err1 := route.Context.Get(r, "tournamentId")
		if err1 != nil {
			log.Errorf(c, "%s error getting tournament id, err:%v", desc, err1)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournamentId int64
		tournamentId, err1 = strconv.ParseInt(strTournamentId, 0, 64)
		if err1 != nil {
			log.Errorf(c, "%s error converting tournament id from string to int64, err:%v", desc, err1)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		strUserId, err2 := route.Context.Get(r, "userId")
		if err2 != nil {
			log.Errorf(c, "%s error getting user id, err:%v", desc, err2)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		var userId int64
		userId, err2 = strconv.ParseInt(strUserId, 0, 64)
		if err2 != nil {
			log.Errorf(c, "%s error converting user id from string to int64, err:%v", desc, err2)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		var tournament *mdl.Tournament
		if tournament, err1 = mdl.TournamentById(c, tournamentId); err1 != nil {
			log.Errorf(c, "%s tournament not found: %v", desc, err1)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		newAdmin, err := mdl.UserById(c, userId)
		log.Infof(c, "%s User: %v", desc, newAdmin)
		if err != nil {
			log.Errorf(c, "%s user not found", desc)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		if err = tournament.AddAdmin(c, newAdmin.Id); err != nil {
			log.Errorf(c, "%s error on AddAdmin to tournament: %v", desc, err)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeInternal)}
		}

		var tJson mdl.TournamentJson
		fieldsToKeep := []string{"Id", "Name", "AdminIds", "Private"}
		helpers.InitPointerStructure(tournament, &tJson, fieldsToKeep)

		msg := fmt.Sprintf("You added %s as admin of tournament %s.", newAdmin.Name, tournament.Name)
		data := struct {
			MessageInfo string `json:",omitempty"`
			Tournament  mdl.TournamentJson
		}{
			msg,
			tJson,
		}

		return templateshlp.RenderJson(w, c, data)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// Tournament remove admin handler:
//
// Use this handler to remove a user as admin of the current tournament.
//	GET	/j/tournaments/[0-9]+/admin/remove/
//
func RemoveAdmin(w http.ResponseWriter, r *http.Request, u *mdl.User) error {
	c := appengine.NewContext(r)
	desc := "Tournament remove admin Handler:"

	if r.Method == "POST" {
		// get tournament id and user id
		strTournamentId, err1 := route.Context.Get(r, "tournamentId")
		if err1 != nil {
			log.Errorf(c, "%s error getting tournament id, err:%v", desc, err1)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournamentId int64
		tournamentId, err1 = strconv.ParseInt(strTournamentId, 0, 64)
		if err1 != nil {
			log.Errorf(c, "%s error converting tournament id from string to int64, err:%v", desc, err1)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		strUserId, err2 := route.Context.Get(r, "userId")
		if err2 != nil {
			log.Errorf(c, "%s error getting user id, err:%v", desc, err2)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		var userId int64
		userId, err2 = strconv.ParseInt(strUserId, 0, 64)
		if err2 != nil {
			log.Errorf(c, "%s error converting user id from string to int64, err:%v", desc, err2)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		var tournament *mdl.Tournament
		if tournament, err1 = mdl.TournamentById(c, tournamentId); err1 != nil {
			log.Errorf(c, "%s tournament not found: %v.", desc, err1)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var oldAdmin *mdl.User
		oldAdmin, err := mdl.UserById(c, userId)
		log.Infof(c, "%s User: %v.", desc, oldAdmin)
		if err != nil {
			log.Errorf(c, "%s user not found.", desc)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		if err = tournament.RemoveAdmin(c, oldAdmin.Id); err != nil {
			log.Errorf(c, "%s error on RemoveAdmin to tournament: %v.", desc, err)
			return &helpers.InternalServerError{Err: err}
		}

		var tJson mdl.TournamentJson
		fieldsToKeep := []string{"Id", "Name", "AdminIds", "Private"}
		helpers.InitPointerStructure(tournament, &tJson, fieldsToKeep)

		msg := fmt.Sprintf("You removed %s as admin of tournament %s.", oldAdmin.Name, tournament.Name)
		data := struct {
			MessageInfo string `json:",omitempty"`
			Tournament  mdl.TournamentJson
		}{
			msg,
			tJson,
		}
		return templateshlp.RenderJson(w, c, data)
	}
	return nil
}

// Tournament sync scores handler:
//
// Use this handler to run taks to sync scores of all users in tournament.
//	GET	/j/tournaments/[0-9]+/admin/syncscores/
//
func SyncScores(w http.ResponseWriter, r *http.Request, u *mdl.User) error {

	c := appengine.NewContext(r)
	desc := "Tournament sync scores Handler:"
	log.Infof(c, "%v", desc)
	if r.Method == "POST" {
		// get tournament id
		strTournamentId, err := route.Context.Get(r, "tournamentId")
		if err != nil {
			log.Errorf(c, "%s error getting tournament id, err:%v", desc, err)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournamentId int64
		tournamentId, err = strconv.ParseInt(strTournamentId, 0, 64)
		if err != nil {
			log.Errorf(c, "%s error converting tournament id from string to int64, err:%v", desc, err)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournament *mdl.Tournament
		if tournament, err = mdl.TournamentById(c, tournamentId); err != nil {
			log.Errorf(c, "%s tournament not found: %v", desc, err)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}
		// prepare data to add task to queue.
		b1, errm := json.Marshal(tournament)
		if errm != nil {
			log.Errorf(c, "%s Error marshaling", desc, errm)
		}

		task := taskqueue.NewPOSTTask("/a/sync/scores/", url.Values{
			"tournament": []string{string(b1)},
		})

		if _, err = taskqueue.Add(c, task, "gw-queue"); err != nil {
			log.Errorf(c, "%s unable to add task to taskqueue.", desc)
			return err
		} else {
			log.Infof(c, "%s add task to taskqueue successfully", desc)
		}

		msg := fmt.Sprintf("You send task to synch scores for all users.")
		data := struct {
			MessageInfo string `json:",omitempty"`
		}{
			msg,
		}

		return templateshlp.RenderJson(w, c, data)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// Tournament activate phase handler:
//
// Use this handler to activate all the matches of given phase in tournament.
//	GET	/j/tournaments/[0-9]+/admin/activatephase/
//
func ActivatePhase(w http.ResponseWriter, r *http.Request, u *mdl.User) error {

	c := appengine.NewContext(r)
	desc := "Tournament activate phase handler:"
	log.Infof(c, "%v", desc)
	if r.Method == "POST" {
		// get tournament id
		strTournamentId, err := route.Context.Get(r, "tournamentId")
		if err != nil {
			log.Errorf(c, "%s error getting tournament id, err:%v", desc, err)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournamentId int64
		tournamentId, err = strconv.ParseInt(strTournamentId, 0, 64)
		if err != nil {
			log.Errorf(c, "%s error converting tournament id from string to int64, err:%v", desc, err)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournament *mdl.Tournament
		if tournament, err = mdl.TournamentById(c, tournamentId); err != nil {
			log.Errorf(c, "%s tournament not found: %v", desc, err)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		phaseName := r.FormValue("phaseName")

		matches := mdl.GetMatchesByPhase(c, tournament, phaseName)

		for _, match := range matches {
			match.Ready = true
		}

		return mdl.UpdateMatches(c, matches)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}
<commit_msg>read formValue properly fixes #607<commit_after>/*
 * Copyright (c) 2014 Santiago Arias | Remy Jourde
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package tournaments

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"appengine"
	"appengine/taskqueue"

	"github.com/taironas/route"

	"github.com/santiaago/gonawin/helpers"
	"github.com/santiaago/gonawin/helpers/log"
	templateshlp "github.com/santiaago/gonawin/helpers/templates"

	mdl "github.com/santiaago/gonawin/models"
)

// Tournament add admin handler:
//
// Use this handler to add a user as admin of current tournament.
//	GET	/j/tournaments/[0-9]+/admin/add/
//
func AddAdmin(w http.ResponseWriter, r *http.Request, u *mdl.User) error {
	c := appengine.NewContext(r)
	desc := "Tournament add admin Handler:"
	if r.Method == "POST" {
		// get tournament id and user id
		strTournamentId, err1 := route.Context.Get(r, "tournamentId")
		if err1 != nil {
			log.Errorf(c, "%s error getting tournament id, err:%v", desc, err1)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournamentId int64
		tournamentId, err1 = strconv.ParseInt(strTournamentId, 0, 64)
		if err1 != nil {
			log.Errorf(c, "%s error converting tournament id from string to int64, err:%v", desc, err1)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		strUserId, err2 := route.Context.Get(r, "userId")
		if err2 != nil {
			log.Errorf(c, "%s error getting user id, err:%v", desc, err2)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		var userId int64
		userId, err2 = strconv.ParseInt(strUserId, 0, 64)
		if err2 != nil {
			log.Errorf(c, "%s error converting user id from string to int64, err:%v", desc, err2)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		var tournament *mdl.Tournament
		if tournament, err1 = mdl.TournamentById(c, tournamentId); err1 != nil {
			log.Errorf(c, "%s tournament not found: %v", desc, err1)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		newAdmin, err := mdl.UserById(c, userId)
		log.Infof(c, "%s User: %v", desc, newAdmin)
		if err != nil {
			log.Errorf(c, "%s user not found", desc)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		if err = tournament.AddAdmin(c, newAdmin.Id); err != nil {
			log.Errorf(c, "%s error on AddAdmin to tournament: %v", desc, err)
			return &helpers.InternalServerError{Err: errors.New(helpers.ErrorCodeInternal)}
		}

		var tJson mdl.TournamentJson
		fieldsToKeep := []string{"Id", "Name", "AdminIds", "Private"}
		helpers.InitPointerStructure(tournament, &tJson, fieldsToKeep)

		msg := fmt.Sprintf("You added %s as admin of tournament %s.", newAdmin.Name, tournament.Name)
		data := struct {
			MessageInfo string `json:",omitempty"`
			Tournament  mdl.TournamentJson
		}{
			msg,
			tJson,
		}

		return templateshlp.RenderJson(w, c, data)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// Tournament remove admin handler:
//
// Use this handler to remove a user as admin of the current tournament.
//	GET	/j/tournaments/[0-9]+/admin/remove/
//
func RemoveAdmin(w http.ResponseWriter, r *http.Request, u *mdl.User) error {
	c := appengine.NewContext(r)
	desc := "Tournament remove admin Handler:"

	if r.Method == "POST" {
		// get tournament id and user id
		strTournamentId, err1 := route.Context.Get(r, "tournamentId")
		if err1 != nil {
			log.Errorf(c, "%s error getting tournament id, err:%v", desc, err1)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournamentId int64
		tournamentId, err1 = strconv.ParseInt(strTournamentId, 0, 64)
		if err1 != nil {
			log.Errorf(c, "%s error converting tournament id from string to int64, err:%v", desc, err1)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		strUserId, err2 := route.Context.Get(r, "userId")
		if err2 != nil {
			log.Errorf(c, "%s error getting user id, err:%v", desc, err2)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		var userId int64
		userId, err2 = strconv.ParseInt(strUserId, 0, 64)
		if err2 != nil {
			log.Errorf(c, "%s error converting user id from string to int64, err:%v", desc, err2)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		var tournament *mdl.Tournament
		if tournament, err1 = mdl.TournamentById(c, tournamentId); err1 != nil {
			log.Errorf(c, "%s tournament not found: %v.", desc, err1)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var oldAdmin *mdl.User
		oldAdmin, err := mdl.UserById(c, userId)
		log.Infof(c, "%s User: %v.", desc, oldAdmin)
		if err != nil {
			log.Errorf(c, "%s user not found.", desc)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeUserNotFound)}
		}

		if err = tournament.RemoveAdmin(c, oldAdmin.Id); err != nil {
			log.Errorf(c, "%s error on RemoveAdmin to tournament: %v.", desc, err)
			return &helpers.InternalServerError{Err: err}
		}

		var tJson mdl.TournamentJson
		fieldsToKeep := []string{"Id", "Name", "AdminIds", "Private"}
		helpers.InitPointerStructure(tournament, &tJson, fieldsToKeep)

		msg := fmt.Sprintf("You removed %s as admin of tournament %s.", oldAdmin.Name, tournament.Name)
		data := struct {
			MessageInfo string `json:",omitempty"`
			Tournament  mdl.TournamentJson
		}{
			msg,
			tJson,
		}
		return templateshlp.RenderJson(w, c, data)
	}
	return nil
}

// Tournament sync scores handler:
//
// Use this handler to run taks to sync scores of all users in tournament.
//	GET	/j/tournaments/[0-9]+/admin/syncscores/
//
func SyncScores(w http.ResponseWriter, r *http.Request, u *mdl.User) error {

	c := appengine.NewContext(r)
	desc := "Tournament sync scores Handler:"
	log.Infof(c, "%v", desc)
	if r.Method == "POST" {
		// get tournament id
		strTournamentId, err := route.Context.Get(r, "tournamentId")
		if err != nil {
			log.Errorf(c, "%s error getting tournament id, err:%v", desc, err)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournamentId int64
		tournamentId, err = strconv.ParseInt(strTournamentId, 0, 64)
		if err != nil {
			log.Errorf(c, "%s error converting tournament id from string to int64, err:%v", desc, err)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournament *mdl.Tournament
		if tournament, err = mdl.TournamentById(c, tournamentId); err != nil {
			log.Errorf(c, "%s tournament not found: %v", desc, err)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}
		// prepare data to add task to queue.
		b1, errm := json.Marshal(tournament)
		if errm != nil {
			log.Errorf(c, "%s Error marshaling", desc, errm)
		}

		task := taskqueue.NewPOSTTask("/a/sync/scores/", url.Values{
			"tournament": []string{string(b1)},
		})

		if _, err = taskqueue.Add(c, task, "gw-queue"); err != nil {
			log.Errorf(c, "%s unable to add task to taskqueue.", desc)
			return err
		} else {
			log.Infof(c, "%s add task to taskqueue successfully", desc)
		}

		msg := fmt.Sprintf("You send task to synch scores for all users.")
		data := struct {
			MessageInfo string `json:",omitempty"`
		}{
			msg,
		}

		return templateshlp.RenderJson(w, c, data)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}

// Tournament activate phase handler:
//
// Use this handler to activate all the matches of given phase in tournament.
//	GET	/j/tournaments/[0-9]+/admin/activatephase/
//
func ActivatePhase(w http.ResponseWriter, r *http.Request, u *mdl.User) error {

	c := appengine.NewContext(r)
	desc := "Tournament activate phase handler:"
	log.Infof(c, "%v", desc)
	if r.Method == "POST" {
		// get tournament id
		strTournamentId, err := route.Context.Get(r, "tournamentId")
		if err != nil {
			log.Errorf(c, "%s error getting tournament id, err:%v", desc, err)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournamentId int64
		tournamentId, err = strconv.ParseInt(strTournamentId, 0, 64)
		if err != nil {
			log.Errorf(c, "%s error converting tournament id from string to int64, err:%v", desc, err)
			return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		var tournament *mdl.Tournament
		if tournament, err = mdl.TournamentById(c, tournamentId); err != nil {
			log.Errorf(c, "%s tournament not found: %v", desc, err)
			return &helpers.NotFound{Err: errors.New(helpers.ErrorCodeTournamentNotFound)}
		}

		phaseName := r.FormValue("phase")
		matches := mdl.GetMatchesByPhase(c, tournament, phaseName)

		for _, match := range matches {
			match.Ready = true
		}

		return mdl.UpdateMatches(c, matches)
	}
	return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
}
<|endoftext|>