<commit_before>// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package gin

import (
	"html/template"
	"net"
	"net/http"
	"os"
	"sync"

	"github.com/gin-gonic/gin/binding"
	"github.com/gin-gonic/gin/render"
)

const Version = "v1.0rc1"

var default404Body = []byte("404 page not found")
var default405Body = []byte("405 method not allowed")

type (
	HandlerFunc   func(*Context)
	HandlersChain []HandlerFunc

	// Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares.
	Engine struct {
		RouterGroup
		HTMLRender  render.HTMLRender
		pool        sync.Pool
		allNoRoute  HandlersChain
		allNoMethod HandlersChain
		noRoute     HandlersChain
		noMethod    HandlersChain
		trees       map[string]*node

		// Enables automatic redirection if the current route can't be matched but a
		// handler for the path with (without) the trailing slash exists.
		// For example if /foo/ is requested but a route only exists for /foo, the
		// client is redirected to /foo with http status code 301 for GET requests
		// and 307 for all other request methods.
		RedirectTrailingSlash bool

		// If enabled, the router tries to fix the current request path, if no
		// handle is registered for it.
		// First superfluous path elements like ../ or // are removed.
		// Afterwards the router does a case-insensitive lookup of the cleaned path.
		// If a handle can be found for this route, the router makes a redirection
		// to the corrected path with status code 301 for GET requests and 307 for
		// all other request methods.
		// For example /FOO and /..//Foo could be redirected to /foo.
		// RedirectTrailingSlash is independent of this option.
		RedirectFixedPath bool

		// If enabled, the router checks if another method is allowed for the
		// current route, if the current request can not be routed.
		// If this is the case, the request is answered with 'Method Not Allowed'
		// and HTTP status code 405.
		// If no other Method is allowed, the request is delegated to the NotFound
		// handler.
		HandleMethodNotAllowed bool
	}
)

// Returns a new blank Engine instance without any middleware attached.
// The most basic configuration
func New() *Engine {
	debugPrintWARNING()
	engine := &Engine{
		RouterGroup: RouterGroup{
			Handlers: nil,
			BasePath: "/",
		},
		RedirectTrailingSlash:  true,
		RedirectFixedPath:      true,
		HandleMethodNotAllowed: true,
		trees: make(map[string]*node),
	}
	engine.RouterGroup.engine = engine
	engine.pool.New = func() interface{} {
		return engine.allocateContext()
	}
	return engine
}

// Returns a Engine instance with the Logger and Recovery already attached.
func Default() *Engine {
	engine := New()
	engine.Use(Recovery(), Logger())
	return engine
}

func (engine *Engine) allocateContext() (context *Context) {
	return &Context{engine: engine}
}

func (engine *Engine) LoadHTMLGlob(pattern string) {
	if IsDebugging() {
		engine.HTMLRender = render.HTMLDebug{Glob: pattern}
	} else {
		templ := template.Must(template.ParseGlob(pattern))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) LoadHTMLFiles(files ...string) {
	if IsDebugging() {
		engine.HTMLRender = render.HTMLDebug{Files: files}
	} else {
		templ := template.Must(template.ParseFiles(files...))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
	engine.HTMLRender = render.HTMLProduction{Template: templ}
}

// Adds handlers for NoRoute. It return a 404 code by default.
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
	engine.noRoute = handlers
	engine.rebuild404Handlers()
}

func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
	engine.noMethod = handlers
	engine.rebuild405Handlers()
}

func (engine *Engine) Use(middlewares ...HandlerFunc) {
	engine.RouterGroup.Use(middlewares...)
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
}

func (engine *Engine) rebuild404Handlers() {
	engine.allNoRoute = engine.combineHandlers(engine.noRoute)
}

func (engine *Engine) rebuild405Handlers() {
	engine.allNoMethod = engine.combineHandlers(engine.noMethod)
}

func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
	debugPrintRoute(method, path, handlers)

	if path[0] != '/' {
		panic("path must begin with '/'")
	}
	if method == "" {
		panic("HTTP method can not be empty")
	}
	if len(handlers) == 0 {
		panic("there must be at least one handler")
	}

	root := engine.trees[method]
	if root == nil {
		root = new(node)
		engine.trees[method] = root
	}
	root.addRoute(path, handlers)
}

func (engine *Engine) Run(addr string) (err error) {
	debugPrint("Listening and serving HTTP on %s\n", addr)
	defer func() { debugPrintError(err) }()

	err = http.ListenAndServe(addr, engine)
	return
}

func (engine *Engine) RunTLS(addr string, cert string, key string) (err error) {
	debugPrint("Listening and serving HTTPS on %s\n", addr)
	defer func() { debugPrintError(err) }()

	err = http.ListenAndServe(addr, engine)
	return
}

func (engine *Engine) RunUnix(file string) (err error) {
	debugPrint("Listening and serving HTTP on unix:/%s", file)
	defer func() { debugPrintError(err) }()

	os.Remove(file)
	listener, err := net.Listen("unix", file)
	if err != nil {
		return
	}
	defer listener.Close()
	err = http.Serve(listener, engine)
	return
}

// ServeHTTP makes the router implement the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	c := engine.getcontext(w, req)
	engine.handleHTTPRequest(c)
	engine.putcontext(c)
}

func (engine *Engine) getcontext(w http.ResponseWriter, req *http.Request) *Context {
	c := engine.pool.Get().(*Context)
	c.writermem.reset(w)
	c.Request = req
	c.reset()
	return c
}

func (engine *Engine) putcontext(c *Context) {
	engine.pool.Put(c)
}

func (engine *Engine) handleHTTPRequest(context *Context) {
	httpMethod := context.Request.Method
	path := context.Request.URL.Path

	// Find root of the tree for the given HTTP method
	if root := engine.trees[httpMethod]; root != nil {
		// Find route in tree
		handlers, params, tsr := root.getValue(path, context.Params)
		if handlers != nil {
			context.handlers = handlers
			context.Params = params
			context.Next()
			context.writermem.WriteHeaderNow()
			return

		} else if httpMethod != "CONNECT" && path != "/" {
			if engine.serveAutoRedirect(context, root, tsr) {
				return
			}
		}
	}

	if engine.HandleMethodNotAllowed {
		for method, root := range engine.trees {
			if method != httpMethod {
				if handlers, _, _ := root.getValue(path, nil); handlers != nil {
					context.handlers = engine.allNoMethod
					serveError(context, 405, default405Body)
					return
				}
			}
		}
	}
	context.handlers = engine.allNoRoute
	serveError(context, 404, default404Body)
}

func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool {
	req := c.Request
	path := req.URL.Path
	code := 301 // Permanent redirect, request with GET method
	if req.Method != "GET" {
		code = 307
	}

	if tsr && engine.RedirectTrailingSlash {
		if len(path) > 1 && path[len(path)-1] == '/' {
			req.URL.Path = path[:len(path)-1]
		} else {
			req.URL.Path = path + "/"
		}
		debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
		http.Redirect(c.Writer, req, req.URL.String(), code)
		c.writermem.WriteHeaderNow()
		return true
	}

	// Try to fix the request path
	if engine.RedirectFixedPath {
		fixedPath, found := root.findCaseInsensitivePath(
			CleanPath(path),
			engine.RedirectTrailingSlash,
		)
		if found {
			req.URL.Path = string(fixedPath)
			debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
			http.Redirect(c.Writer, req, req.URL.String(), code)
			c.writermem.WriteHeaderNow()
			return true
		}
	}
	return false
}

func serveError(c *Context, code int, defaultMessage []byte) {
	c.writermem.status = code
	c.Next()
	if !c.Writer.Written() {
		if c.Writer.Status() == code {
			c.Data(-1, binding.MIMEPlain, defaultMessage)
		} else {
			c.Writer.WriteHeaderNow()
		}
	}
}
<commit_msg>Fixes RunTLS() implementation<commit_after>// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package gin

import (
	"html/template"
	"net"
	"net/http"
	"os"
	"sync"

	"github.com/gin-gonic/gin/binding"
	"github.com/gin-gonic/gin/render"
)

const Version = "v1.0rc1"

var default404Body = []byte("404 page not found")
var default405Body = []byte("405 method not allowed")

type (
	HandlerFunc   func(*Context)
	HandlersChain []HandlerFunc

	// Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares.
	Engine struct {
		RouterGroup
		HTMLRender  render.HTMLRender
		pool        sync.Pool
		allNoRoute  HandlersChain
		allNoMethod HandlersChain
		noRoute     HandlersChain
		noMethod    HandlersChain
		trees       map[string]*node

		// Enables automatic redirection if the current route can't be matched but a
		// handler for the path with (without) the trailing slash exists.
		// For example if /foo/ is requested but a route only exists for /foo, the
		// client is redirected to /foo with http status code 301 for GET requests
		// and 307 for all other request methods.
		RedirectTrailingSlash bool

		// If enabled, the router tries to fix the current request path, if no
		// handle is registered for it.
		// First superfluous path elements like ../ or // are removed.
		// Afterwards the router does a case-insensitive lookup of the cleaned path.
		// If a handle can be found for this route, the router makes a redirection
		// to the corrected path with status code 301 for GET requests and 307 for
		// all other request methods.
		// For example /FOO and /..//Foo could be redirected to /foo.
		// RedirectTrailingSlash is independent of this option.
		RedirectFixedPath bool

		// If enabled, the router checks if another method is allowed for the
		// current route, if the current request can not be routed.
		// If this is the case, the request is answered with 'Method Not Allowed'
		// and HTTP status code 405.
		// If no other Method is allowed, the request is delegated to the NotFound
		// handler.
		HandleMethodNotAllowed bool
	}
)

// Returns a new blank Engine instance without any middleware attached.
// The most basic configuration
func New() *Engine {
	debugPrintWARNING()
	engine := &Engine{
		RouterGroup: RouterGroup{
			Handlers: nil,
			BasePath: "/",
		},
		RedirectTrailingSlash:  true,
		RedirectFixedPath:      true,
		HandleMethodNotAllowed: true,
		trees: make(map[string]*node),
	}
	engine.RouterGroup.engine = engine
	engine.pool.New = func() interface{} {
		return engine.allocateContext()
	}
	return engine
}

// Returns a Engine instance with the Logger and Recovery already attached.
func Default() *Engine {
	engine := New()
	engine.Use(Recovery(), Logger())
	return engine
}

func (engine *Engine) allocateContext() (context *Context) {
	return &Context{engine: engine}
}

func (engine *Engine) LoadHTMLGlob(pattern string) {
	if IsDebugging() {
		engine.HTMLRender = render.HTMLDebug{Glob: pattern}
	} else {
		templ := template.Must(template.ParseGlob(pattern))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) LoadHTMLFiles(files ...string) {
	if IsDebugging() {
		engine.HTMLRender = render.HTMLDebug{Files: files}
	} else {
		templ := template.Must(template.ParseFiles(files...))
		engine.SetHTMLTemplate(templ)
	}
}

func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
	engine.HTMLRender = render.HTMLProduction{Template: templ}
}

// Adds handlers for NoRoute. It return a 404 code by default.
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
	engine.noRoute = handlers
	engine.rebuild404Handlers()
}

func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
	engine.noMethod = handlers
	engine.rebuild405Handlers()
}

func (engine *Engine) Use(middlewares ...HandlerFunc) {
	engine.RouterGroup.Use(middlewares...)
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
}

func (engine *Engine) rebuild404Handlers() {
	engine.allNoRoute = engine.combineHandlers(engine.noRoute)
}

func (engine *Engine) rebuild405Handlers() {
	engine.allNoMethod = engine.combineHandlers(engine.noMethod)
}

func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
	debugPrintRoute(method, path, handlers)

	if path[0] != '/' {
		panic("path must begin with '/'")
	}
	if method == "" {
		panic("HTTP method can not be empty")
	}
	if len(handlers) == 0 {
		panic("there must be at least one handler")
	}

	root := engine.trees[method]
	if root == nil {
		root = new(node)
		engine.trees[method] = root
	}
	root.addRoute(path, handlers)
}

func (engine *Engine) Run(addr string) (err error) {
	debugPrint("Listening and serving HTTP on %s\n", addr)
	defer func() { debugPrintError(err) }()

	err = http.ListenAndServe(addr, engine)
	return
}

func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err error) {
	debugPrint("Listening and serving HTTPS on %s\n", addr)
	defer func() { debugPrintError(err) }()

	err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
	return
}

func (engine *Engine) RunUnix(file string) (err error) {
	debugPrint("Listening and serving HTTP on unix:/%s", file)
	defer func() { debugPrintError(err) }()

	os.Remove(file)
	listener, err := net.Listen("unix", file)
	if err != nil {
		return
	}
	defer listener.Close()
	err = http.Serve(listener, engine)
	return
}

// ServeHTTP makes the router implement the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	c := engine.getcontext(w, req)
	engine.handleHTTPRequest(c)
	engine.putcontext(c)
}

func (engine *Engine) getcontext(w http.ResponseWriter, req *http.Request) *Context {
	c := engine.pool.Get().(*Context)
	c.writermem.reset(w)
	c.Request = req
	c.reset()
	return c
}

func (engine *Engine) putcontext(c *Context) {
	engine.pool.Put(c)
}

func (engine *Engine) handleHTTPRequest(context *Context) {
	httpMethod := context.Request.Method
	path := context.Request.URL.Path

	// Find root of the tree for the given HTTP method
	if root := engine.trees[httpMethod]; root != nil {
		// Find route in tree
		handlers, params, tsr := root.getValue(path, context.Params)
		if handlers != nil {
			context.handlers = handlers
			context.Params = params
			context.Next()
			context.writermem.WriteHeaderNow()
			return

		} else if httpMethod != "CONNECT" && path != "/" {
			if engine.serveAutoRedirect(context, root, tsr) {
				return
			}
		}
	}

	if engine.HandleMethodNotAllowed {
		for method, root := range engine.trees {
			if method != httpMethod {
				if handlers, _, _ := root.getValue(path, nil); handlers != nil {
					context.handlers = engine.allNoMethod
					serveError(context, 405, default405Body)
					return
				}
			}
		}
	}
	context.handlers = engine.allNoRoute
	serveError(context, 404, default404Body)
}

func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool {
	req := c.Request
	path := req.URL.Path
	code := 301 // Permanent redirect, request with GET method
	if req.Method != "GET" {
		code = 307
	}

	if tsr && engine.RedirectTrailingSlash {
		if len(path) > 1 && path[len(path)-1] == '/' {
			req.URL.Path = path[:len(path)-1]
		} else {
			req.URL.Path = path + "/"
		}
		debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
		http.Redirect(c.Writer, req, req.URL.String(), code)
		c.writermem.WriteHeaderNow()
		return true
	}

	// Try to fix the request path
	if engine.RedirectFixedPath {
		fixedPath, found := root.findCaseInsensitivePath(
			CleanPath(path),
			engine.RedirectTrailingSlash,
		)
		if found {
			req.URL.Path = string(fixedPath)
			debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
			http.Redirect(c.Writer, req, req.URL.String(), code)
			c.writermem.WriteHeaderNow()
			return true
		}
	}
	return false
}

func serveError(c *Context, code int, defaultMessage []byte) {
	c.writermem.status = code
	c.Next()
	if !c.Writer.Written() {
		if c.Writer.Status() == code {
			c.Data(-1, binding.MIMEPlain, defaultMessage)
		} else {
			c.Writer.WriteHeaderNow()
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"sdl"
	"time"
)

const (
	modeHBlank = byte(iota)
	modeVBlank
	modeOAM
	modeVRAM
)

const (
	tilesX = 20
	tilesY = 18
	tileW = 8
	tileH = 8
	mapW = 32
	mapH = 32
	lcdW = tileW * tilesX
	lcdH = tileH * tilesY

	scale = 3

	screenW = lcdW * scale
	screenH = lcdH * scale

	hblankTicks = 204/4
	vblankTicks = 4560/4
	oamTicks    = 80/4
	vramTicks   = 172/4

	scanlineTicks = oamTicks + vramTicks + hblankTicks
	refreshTicks  = scanlineTicks * screenH + vblankTicks
)

type GPU struct {
	clock int
	pal []uint32
	maps [4][mapW * mapH * 16]byte
	screen *sdl.Surface
	mmu *MBC
	frameSkip bool
	frameTime int64
}

func NewGPU(m *MBC) *GPU {
	screen := sdl.SetVideoMode(screenW, screenH, 0, sdl.DOUBLEBUF)
	pal := make([]uint32, 4)
	pal[0] = sdl.MapRGBA(screen.Format, 0x9B, 0xBC, 0x0F, 0)
	pal[1] = sdl.MapRGBA(screen.Format, 0x8B, 0xAC, 0x0F, 0)
	pal[2] = sdl.MapRGBA(screen.Format, 0x30, 0x62, 0x30, 0)
	pal[3] = sdl.MapRGBA(screen.Format, 0x0F, 0x38, 0x0F, 0)
	screen.FillRect(nil, pal[0])
	screen.Flip()
	time := time.Nanoseconds()
	return &GPU{pal: pal, screen: screen, mmu: m, frameTime: time}
}

func (gpu *GPU) Step(t int) {
	gpu.clock += t
	if gpu.clock >= refreshTicks {
		gpu.clock -= refreshTicks
	}

	mem := gpu.mmu
	ly := byte(gpu.clock / scanlineTicks)
	mem.WritePort(PortLY, ly)
	mode := calcMode(gpu.clock, ly)
	if mode == mem.LCDMode {
		return;
	}
	mem.LCDMode = mode

	stat := mem.ReadPort(PortSTAT) &^ 3 | mode
	irq := mem.ReadPort(PortIF)

	switch mode {
	case modeOAM:
		if mem.OAMInterrupt {
			mem.WritePort(PortIF, irq | 0x02)
		}
		if lyc := mem.ReadPort(PortLYC); ly-1 == lyc {
			stat |= 0x04
			if mem.LYCInterrupt {
				mem.WritePort(PortIF, irq | 0x02)
			}
		} else {
			stat &^= 0x04
		}
	case modeVRAM:
		if mem.LCDEnable && !gpu.frameSkip {
			gpu.scanline(ly)
		}
	case modeHBlank:
		if mem.HBlankInterrupt {
			mem.WritePort(PortIF, irq | 0x02)
		}
	case modeVBlank:
		if mem.VBlankInterrupt {
			irq |= 0x02
		}
		mem.WritePort(PortIF, irq | 0x01)
		if !gpu.frameSkip {
			if !mem.LCDEnable {
				gpu.screen.FillRect(nil, gpu.pal[0])
			}
			gpu.screen.Flip()
		}
		gpu.frameSkip = !gpu.frameSkip
		gpu.delay()
	}

	mem.WritePort(PortSTAT, stat)
}

func (gpu *GPU) delay() {
	now := time.Nanoseconds()
	delta := now - gpu.frameTime
	target := 16742706 - delta
	if target > 0 {
		time.Sleep(target)
	}
	gpu.frameTime = time.Nanoseconds()
}

func (gpu *GPU) scanline(ly byte) {
	mem := gpu.mmu

	if ly == 0 && mem.vramDirty {
		gpu.update()
	}

	scy := mem.ReadPort(PortSCY)
	scx := mem.ReadPort(PortSCX)
	wy := mem.ReadPort(PortWY)
	wx := mem.ReadPort(PortWX)

	if mem.BGEnable {
		gpu.rleline(mem.BGMap, byte(0), ly, scx, scy)
	}

	if mem.WindowEnable && wx < 167 && wy < 144 && ly >= wy {
		x := int(wx) - 7
		xoff := -x
		if x < 0 { x = 0 }
		gpu.rleline(mem.WindowMap, byte(x), ly, byte(xoff), byte(-wy))
	}

	if mem.SpriteEnable {
		// TODO
	}
}

func (gpu *GPU) rleline(map1 bool, x, y, xoff, yoff byte) {
	r := &sdl.Rect{ X: int16(x) * scale,
		Y: int16(y) * scale, W: scale, H: scale }
	y += yoff
	cur := gpu.mapAt(map1, int(x + xoff), int(y))
	for x++; x < lcdW; x++ {
		b := gpu.mapAt(map1, int(x + xoff), int(y))
		if b != cur {
			gpu.screen.FillRect(r, gpu.pal[gpu.mmu.BGP[cur]])
			cur = b
			r.X = int16(x) * scale
			r.W = scale
		} else {
			r.W += scale
		}
	}
	gpu.screen.FillRect(r, gpu.pal[gpu.mmu.BGP[cur]])
}

func (gpu *GPU) mapAt(map1 bool, x, y int) byte {
	idx := 0
	if map1 { idx = 2 }
	if !gpu.mmu.TileData { idx++ }
	n := ((y / tileH) * mapW + (x / tileW)) * 16
	n += (y % tileH) * 2
	b := uint(tileW - 1 - x % tileW)
	lo := (gpu.maps[idx][n] >> b) & 1
	hi := ((gpu.maps[idx][n+1] >> b) & 1) << 1
	return hi | lo
}

func (gpu *GPU) update() {
	mem := gpu.mmu
	for i := 0; i < 2048; i++ {
		tile := int(mem.vram[0x1800 + i])
		idx  := (i / 1024) * 2
		x := (i & 0x03FF) * 16 // (i % 1024) * 16
		if mem.mapDirty[i] || mem.tileDirty[tile] {
			src := tile * 16
			for n := 0; n < 16; n++ {
				gpu.maps[idx][x+n] = mem.vram[src+n]
			}
		}
		tile = int(int8(tile)) + 256
		idx++
		if mem.mapDirty[i] || mem.tileDirty[tile] {
			src := tile * 16
			for n := 0; n < 16; n++ {
				gpu.maps[idx][x+n] = mem.vram[src+n]
			}
		}
		mem.mapDirty[i] = false
	}
	for i := 0; i < 384; i++ {
		mem.tileDirty[i] = false
	}
	mem.vramDirty = false
}

func calcMode(t int, ly byte) byte {
	if ly >= lcdH {
		return modeVBlank
	}
	t %= scanlineTicks
	if t < oamTicks {
		return modeOAM
	}
	if t < oamTicks + vramTicks {
		return modeVRAM
	}
	return modeHBlank
}
<commit_msg>Remove frame skipping<commit_after>package main

import (
	"sdl"
	"time"
)

const (
	modeHBlank = byte(iota)
	modeVBlank
	modeOAM
	modeVRAM
)

const (
	tilesX = 20
	tilesY = 18
	tileW = 8
	tileH = 8
	mapW = 32
	mapH = 32
	lcdW = tileW * tilesX
	lcdH = tileH * tilesY

	scale = 3

	screenW = lcdW * scale
	screenH = lcdH * scale

	hblankTicks = 204/4
	vblankTicks = 4560/4
	oamTicks    = 80/4
	vramTicks   = 172/4

	scanlineTicks = oamTicks + vramTicks + hblankTicks
	refreshTicks  = scanlineTicks * screenH + vblankTicks
)

type GPU struct {
	clock int
	pal []uint32
	maps [4][mapW * mapH * 16]byte
	screen *sdl.Surface
	mmu *MBC
	frameTime int64
}

func NewGPU(m *MBC) *GPU {
	screen := sdl.SetVideoMode(screenW, screenH, 0, sdl.DOUBLEBUF)
	pal := make([]uint32, 4)
	pal[0] = sdl.MapRGBA(screen.Format, 0x9B, 0xBC, 0x0F, 0)
	pal[1] = sdl.MapRGBA(screen.Format, 0x8B, 0xAC, 0x0F, 0)
	pal[2] = sdl.MapRGBA(screen.Format, 0x30, 0x62, 0x30, 0)
	pal[3] = sdl.MapRGBA(screen.Format, 0x0F, 0x38, 0x0F, 0)
	screen.FillRect(nil, pal[0])
	screen.Flip()
	time := time.Nanoseconds()
	return &GPU{pal: pal, screen: screen, mmu: m, frameTime: time}
}

func (gpu *GPU) Step(t int) {
	gpu.clock += t
	if gpu.clock >= refreshTicks {
		gpu.clock -= refreshTicks
	}

	mem := gpu.mmu
	ly := byte(gpu.clock / scanlineTicks)
	mem.WritePort(PortLY, ly)
	mode := calcMode(gpu.clock, ly)
	if mode == mem.LCDMode {
		return;
	}
	mem.LCDMode = mode

	stat := mem.ReadPort(PortSTAT) &^ 3 | mode
	irq := mem.ReadPort(PortIF)

	switch mode {
	case modeOAM:
		if mem.OAMInterrupt {
			mem.WritePort(PortIF, irq | 0x02)
		}
		if lyc := mem.ReadPort(PortLYC); ly-1 == lyc {
			stat |= 0x04
			if mem.LYCInterrupt {
				mem.WritePort(PortIF, irq | 0x02)
			}
		} else {
			stat &^= 0x04
		}
	case modeVRAM:
		if mem.LCDEnable {
			gpu.scanline(ly)
		}
	case modeHBlank:
		if mem.HBlankInterrupt {
			mem.WritePort(PortIF, irq | 0x02)
		}
	case modeVBlank:
		if mem.VBlankInterrupt {
			irq |= 0x02
		}
		mem.WritePort(PortIF, irq | 0x01)
		if !mem.LCDEnable {
			gpu.screen.FillRect(nil, gpu.pal[0])
		}
		gpu.screen.Flip()
		gpu.delay()
	}

	mem.WritePort(PortSTAT, stat)
}

func (gpu *GPU) delay() {
	now := time.Nanoseconds()
	delta := now - gpu.frameTime
	target := 16742706 - delta
	if target > 0 {
		time.Sleep(target)
	}
	gpu.frameTime = time.Nanoseconds()
}

func (gpu *GPU) scanline(ly byte) {
	mem := gpu.mmu

	if ly == 0 && mem.vramDirty {
		gpu.update()
	}

	scy := mem.ReadPort(PortSCY)
	scx := mem.ReadPort(PortSCX)
	wy := mem.ReadPort(PortWY)
	wx := mem.ReadPort(PortWX)

	if mem.BGEnable {
		gpu.rleline(mem.BGMap, byte(0), ly, scx, scy)
	}

	if mem.WindowEnable && wx < 167 && wy < 144 && ly >= wy {
		x := int(wx) - 7
		xoff := -x
		if x < 0 { x = 0 }
		gpu.rleline(mem.WindowMap, byte(x), ly, byte(xoff), byte(-wy))
	}

	if mem.SpriteEnable {
		// TODO
	}
}

func (gpu *GPU) rleline(map1 bool, x, y, xoff, yoff byte) {
	r := &sdl.Rect{ X: int16(x) * scale,
		Y: int16(y) * scale, W: scale, H: scale }
	y += yoff
	cur := gpu.mapAt(map1, int(x + xoff), int(y))
	for x++; x < lcdW; x++ {
		b := gpu.mapAt(map1, int(x + xoff), int(y))
		if b != cur {
			gpu.screen.FillRect(r, gpu.pal[gpu.mmu.BGP[cur]])
			cur = b
			r.X = int16(x) * scale
			r.W = scale
		} else {
			r.W += scale
		}
	}
	gpu.screen.FillRect(r, gpu.pal[gpu.mmu.BGP[cur]])
}

func (gpu *GPU) mapAt(map1 bool, x, y int) byte {
	idx := 0
	if map1 { idx = 2 }
	if !gpu.mmu.TileData { idx++ }
	n := ((y / tileH) * mapW + (x / tileW)) * 16
	n += (y % tileH) * 2
	b := uint(tileW - 1 - x % tileW)
	lo := (gpu.maps[idx][n] >> b) & 1
	hi := ((gpu.maps[idx][n+1] >> b) & 1) << 1
	return hi | lo
}

func (gpu *GPU) update() {
	mem := gpu.mmu
	for i := 0; i < 2048; i++ {
		tile := int(mem.vram[0x1800 + i])
		idx  := (i / 1024) * 2
		x := (i & 0x03FF) * 16 // (i % 1024) * 16
		if mem.mapDirty[i] || mem.tileDirty[tile] {
			src := tile * 16
			for n := 0; n < 16; n++ {
				gpu.maps[idx][x+n] = mem.vram[src+n]
			}
		}
		tile = int(int8(tile)) + 256
		idx++
		if mem.mapDirty[i] || mem.tileDirty[tile] {
			src := tile * 16
			for n := 0; n < 16; n++ {
				gpu.maps[idx][x+n] = mem.vram[src+n]
			}
		}
		mem.mapDirty[i] = false
	}
	for i := 0; i < 384; i++ {
		mem.tileDirty[i] = false
	}
	mem.vramDirty = false
}

func calcMode(t int, ly byte) byte {
	if ly >= lcdH {
		return modeVBlank
	}
	t %= scanlineTicks
	if t < oamTicks {
		return modeOAM
	}
	if t < oamTicks + vramTicks {
		return modeVRAM
	}
	return modeHBlank
}
<|endoftext|>


<commit_before>// Copyright 2014 Nicolas Vellon.  All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package hal implements encoding of structs into HAL as defined in
// http://stateless.co/hal_specification.html.
//
// See the basic example for an introduction to this package:
// https://github.com/nvellon/hal/blob/master/examples/basic.go

package hal

import (
	"encoding/json"
)

type (
	Entry map[string]interface{}

	// Mapper is the interface implemented by the objects
	// that can be converted into HAL format.
	Mapper interface {
		GetMap() Entry
	}

	// Link types that store hyperlinks and its attributes.
	LinkAttr       map[string]string
	Link           LinkAttr
	LinkRel        string
	LinkCollection map[LinkRel]Link

	// Resource is a struct that stores a resource data.
	// It represents a converted object in the HAL spec by
	// containing all its fields and also a set of related links
	// and a sub-set of recursively related resources.
	Resource struct {
		Payload  Mapper
		Links    LinkCollection
		Embedded []*Resource
	}
)

// NewResource creates a Resource object for a given struct
// and its link.
func NewResource(p Mapper, selfUri string) *Resource {
	var r Resource

	r.Payload = p
	r.Links = make(LinkCollection)

	r.AddNewLink("self", selfUri)

	return &r
}

// AddLink appends a Link to the resource.
func (r *Resource) AddLink(rel LinkRel, l Link) {
	r.Links[rel] = l
}

// AddNewLink appends a new Link object based on
// the rel and href params.
func (r *Resource) AddNewLink(rel LinkRel, href string) {
	r.AddLink(rel, NewLink(href, nil))
}

// Embed appends a Resource to the array of
// embedded resources.
func (r *Resource) Embed(er *Resource) {
	r.Embedded = append(r.Embedded, er)
}

// Map implements the interface Mapper.
func (r Resource) GetMap() Entry {
	mapped := make(Entry)

	mp := r.Payload.GetMap()

	for k, v := range mp {
		mapped[k] = v
	}

	mapped["_links"] = r.Links

	if len(r.Embedded) > 0 {
		mapped["_embedded"] = r.Embedded
	}

	return mapped
}

// MarshalJSON is a Marshaler interface implementation
// for Resource struct
func (r Resource) MarshalJSON() ([]byte, error) {
	return json.Marshal(r.GetMap())
}

// NewLink returns a new Link object.
func NewLink(href string, attr LinkAttr) Link {
	l := make(Link)

	l["href"] = href

	for k, v := range attr {
		l[k] = v
	}

	return l
}
<commit_msg>Link relations as slices to handle multipli links using the same relation. Adding relation to embedded resources.<commit_after>// Copyright 2014 Nicolas Vellon.  All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package hal implements encoding of structs into HAL as defined in
// http://stateless.co/hal_specification.html.
//
// See the basic example for an introduction to this package:
// https://github.com/nvellon/hal/blob/master/examples/basic.go

package hal

import (
	"encoding/json"
)

type (
	Entry map[string]interface{}

	// Mapper is the interface implemented by the objects
	// that can be converted into HAL format.
	Mapper interface {
		GetMap() Entry
	}

	Relation string

	// Link types that store hyperlinks and its attributes.
	LinkAttr       map[string]string
	Link           LinkAttr
	LinkCollection []Link
	LinkRelations  map[Relation]LinkCollection

	// Resource is a struct that stores a resource data.
	// It represents a converted object in the HAL spec by
	// containing all its fields and also a set of related links
	// and a sub-set of recursively related resources.
	Resource struct {
		Payload  Mapper
		Links    LinkRelations
		Embedded map[Relation]ResourceCollection
	}

	ResourceCollection []*Resource
)

// NewResource creates a Resource object for a given struct
// and its link.
func NewResource(p Mapper, selfUri string) *Resource {
	var r Resource

	r.Payload = p

	r.Links = make(LinkRelations)
	r.AddNewLink("self", selfUri)

	r.Embedded = make(map[Relation]ResourceCollection)

	return &r
}

// AddLink appends a Link to the resource.
func (r *Resource) AddLink(rel Relation, l Link) {
	r.Links[rel] = append(r.Links[rel], l)
}

// AddNewLink appends a new Link object based on
// the rel and href params.
func (r *Resource) AddNewLink(rel Relation, href string) {
	r.AddLink(rel, NewLink(href, nil))
}

// Embed appends a Resource to the array of
// embedded resources.
func (r *Resource) Embed(rel Relation, er *Resource) {
	r.Embedded[rel] = append(r.Embedded[rel], er)
}

// Map implements the interface Mapper.
func (r Resource) GetMap() Entry {
	mapped := make(Entry)

	mp := r.Payload.GetMap()

	for k, v := range mp {
		mapped[k] = v
	}

	mapped["_links"] = r.Links

	if len(r.Embedded) > 0 {
		mapped["_embedded"] = r.Embedded
	}

	return mapped
}

// MarshalJSON is a Marshaler interface implementation
// for Resource struct
func (r Resource) MarshalJSON() ([]byte, error) {
	return json.Marshal(r.GetMap())
}

// NewLink returns a new Link object.
func NewLink(href string, attr LinkAttr) Link {
	l := make(Link)

	l["href"] = href

	for k, v := range attr {
		l[k] = v
	}

	return l
}
<|endoftext|>


<commit_before>package sdp

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/pions/webrtc/pkg/ice"
)

// ICECandidateUnmarshal takes a candidate strings and returns a ice.Candidate or nil if it fails to parse
func ICECandidateUnmarshal(raw string) ice.Candidate {
	split := strings.Fields(raw)
	if len(split) < 8 {
		fmt.Printf("Attribute not long enough to be ICE candidate (%d) %s \n", len(split), raw)
		return nil
	}

	getValue := func(key string) string {
		rtrnNext := false
		for _, i := range split {
			if rtrnNext {
				return i
			} else if i == key {
				rtrnNext = true
			}
		}
		return ""
	}

	port, err := strconv.Atoi(split[5])
	if err != nil {
		return nil
	}

	// TODO verify valid address
	address := split[4]

	switch getValue("typ") {
	case "host":
		return &ice.CandidateHost{
			CandidateBase: ice.CandidateBase{
				Protocol: ice.TransportUDP,
				Address:  address,
				Port:     port,
			},
		}
	default:
		return nil
	}
}

func iceSrflxCandidateString(c *ice.CandidateSrflx, component int) string {
	return fmt.Sprintf("udpcandidate %d udp %d %s %d typ srflx raddr %s rport %d generation 0",
		component, c.CandidateBase.Priority(ice.SrflxCandidatePreference, uint16(component)), c.CandidateBase.Address, c.CandidateBase.Port, c.RemoteAddress, c.RemotePort)
}

func iceHostCandidateString(c *ice.CandidateHost, component int) string {
	return fmt.Sprintf("udpcandidate %d udp %d %s %d typ host generation 0",
		component, c.CandidateBase.Priority(ice.HostCandidatePreference, uint16(component)), c.CandidateBase.Address, c.CandidateBase.Port)
}

// ICECandidateMarshal takes a candidate and returns a string representation
func ICECandidateMarshal(c ice.Candidate) []string {
	out := make([]string, 0)

	switch c := c.(type) {
	case *ice.CandidateSrflx:
		out = append(out, iceSrflxCandidateString(c, 1))
		out = append(out, iceSrflxCandidateString(c, 2))
	case *ice.CandidateHost:
		out = append(out, iceHostCandidateString(c, 1))
		out = append(out, iceHostCandidateString(c, 2))
	}

	return out
}
<commit_msg>Parse srflx candidates in SDP<commit_after>package sdp

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/pions/webrtc/pkg/ice"
)

// ICECandidateUnmarshal takes a candidate strings and returns a ice.Candidate or nil if it fails to parse
func ICECandidateUnmarshal(raw string) ice.Candidate {
	split := strings.Fields(raw)
	if len(split) < 8 {
		fmt.Printf("Attribute not long enough to be ICE candidate (%d) %s \n", len(split), raw)
		return nil
	}

	getValue := func(key string) string {
		rtrnNext := false
		for _, i := range split {
			if rtrnNext {
				return i
			} else if i == key {
				rtrnNext = true
			}
		}
		return ""
	}

	port, err := strconv.Atoi(split[5])
	if err != nil {
		return nil
	}

	// TODO verify valid address
	address := split[4]

	switch getValue("typ") {
	case "host":
		return &ice.CandidateHost{
			CandidateBase: ice.CandidateBase{
				Protocol: ice.TransportUDP,
				Address:  address,
				Port:     port,
			},
		}
	case "srflx":
		return &ice.CandidateSrflx{
			CandidateBase: ice.CandidateBase{
				Protocol: ice.TransportUDP,
				Address:  address,
				Port:     port,
			},
		}
	default:
		return nil
	}
}

func iceSrflxCandidateString(c *ice.CandidateSrflx, component int) string {
	return fmt.Sprintf("udpcandidate %d udp %d %s %d typ srflx raddr %s rport %d generation 0",
		component, c.CandidateBase.Priority(ice.SrflxCandidatePreference, uint16(component)), c.CandidateBase.Address, c.CandidateBase.Port, c.RemoteAddress, c.RemotePort)
}

func iceHostCandidateString(c *ice.CandidateHost, component int) string {
	return fmt.Sprintf("udpcandidate %d udp %d %s %d typ host generation 0",
		component, c.CandidateBase.Priority(ice.HostCandidatePreference, uint16(component)), c.CandidateBase.Address, c.CandidateBase.Port)
}

// ICECandidateMarshal takes a candidate and returns a string representation
func ICECandidateMarshal(c ice.Candidate) []string {
	out := make([]string, 0)

	switch c := c.(type) {
	case *ice.CandidateSrflx:
		out = append(out, iceSrflxCandidateString(c, 1))
		out = append(out, iceSrflxCandidateString(c, 2))
	case *ice.CandidateHost:
		out = append(out, iceHostCandidateString(c, 1))
		out = append(out, iceHostCandidateString(c, 2))
	}

	return out
}
<|endoftext|>


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

package v1

import (
	"strings"

	common "github.com/kubeflow/common/operator/v1"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime"
)

// Int32 is a helper routine that allocates a new int32 value
// to store v and returns a pointer to it.
func Int32(v int32) *int32 {
	return &v
}

func addDefaultingFuncs(scheme *runtime.Scheme) error {
	return RegisterDefaults(scheme)
}

// setDefaultPort sets the default ports for tensorflow container.
func setDefaultPort(spec *v1.PodSpec) {
	index := 0
	for i, container := range spec.Containers {
		if container.Name == DefaultContainerName {
			index = i
			break
		}
	}

	hasJobPort := false
	for _, port := range spec.Containers[index].Ports {
		if port.Name == DefaultPortName {
			hasJobPort = true
			break
		}
	}
	if !hasJobPort {
		spec.Containers[index].Ports = append(spec.Containers[index].Ports, v1.ContainerPort{
			Name:          DefaultPortName,
			ContainerPort: DefaultPort,
		})
	}
}

func setDefaultReplicas(spec *common.ReplicaSpec) {
	if spec.Replicas == nil {
		spec.Replicas = Int32(1)
	}
	if spec.RestartPolicy == "" {
		spec.RestartPolicy = DefaultRestartPolicy
	}
}

// setTypeNamesToCamelCase sets the name of all replica types from any case to correct case.
func setTypeNamesToCamelCase(testJob *TestJob) {
	setTypeNameToCamelCase(testJob, TestReplicaTypeWorker)
	setTypeNameToCamelCase(testJob, TestReplicaTypeMaster)
}

// setTypeNameToCamelCase sets the name of the replica type from any case to correct case.
// E.g. from ps to PS; from WORKER to Worker.
func setTypeNameToCamelCase(testJob *TestJob, typ TestReplicaType) {
	for t := range testJob.Spec.TestReplicaSpecs {
		if strings.EqualFold(string(t), string(typ)) && t != typ {
			spec := testJob.Spec.TestReplicaSpecs[t]
			delete(testJob.Spec.TestReplicaSpecs, t)
			testJob.Spec.TestReplicaSpecs[typ] = spec
			return
		}
	}
}

// SetDefaults_TestJob sets any unspecified values to defaults.
func SetDefaults_TestJob(testjob *TestJob) {
	// Set default cleanpod policy to Running.
	if testjob.Spec.RunPolicy.CleanPodPolicy == nil {
		running := common.CleanPodPolicyRunning
		testjob.Spec.RunPolicy.CleanPodPolicy = &running
	}

	// Update the key of TestReplicaSpecs to camel case.
	setTypeNamesToCamelCase(testjob)

	for _, spec := range testjob.Spec.TestReplicaSpecs {
		// Set default replicas to 1.
		setDefaultReplicas(spec)
		// Set default port to tensorFlow container.
		setDefaultPort(&spec.Template.Spec)
	}
}
<commit_msg>Remove mentions of tensorflow in test job (#21)<commit_after>// Copyright 2019 The Kubeflow Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1

import (
	"strings"

	common "github.com/kubeflow/common/operator/v1"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime"
)

// Int32 is a helper routine that allocates a new int32 value
// to store v and returns a pointer to it.
func Int32(v int32) *int32 {
	return &v
}

func addDefaultingFuncs(scheme *runtime.Scheme) error {
	return RegisterDefaults(scheme)
}

// setDefaultPort sets the default ports for container.
func setDefaultPort(spec *v1.PodSpec) {
	index := 0
	for i, container := range spec.Containers {
		if container.Name == DefaultContainerName {
			index = i
			break
		}
	}

	hasJobPort := false
	for _, port := range spec.Containers[index].Ports {
		if port.Name == DefaultPortName {
			hasJobPort = true
			break
		}
	}
	if !hasJobPort {
		spec.Containers[index].Ports = append(spec.Containers[index].Ports, v1.ContainerPort{
			Name:          DefaultPortName,
			ContainerPort: DefaultPort,
		})
	}
}

func setDefaultReplicas(spec *common.ReplicaSpec) {
	if spec.Replicas == nil {
		spec.Replicas = Int32(1)
	}
	if spec.RestartPolicy == "" {
		spec.RestartPolicy = DefaultRestartPolicy
	}
}

// setTypeNamesToCamelCase sets the name of all replica types from any case to correct case.
func setTypeNamesToCamelCase(testJob *TestJob) {
	setTypeNameToCamelCase(testJob, TestReplicaTypeWorker)
	setTypeNameToCamelCase(testJob, TestReplicaTypeMaster)
}

// setTypeNameToCamelCase sets the name of the replica type from any case to correct case.
// E.g. from ps to PS; from WORKER to Worker.
func setTypeNameToCamelCase(testJob *TestJob, typ TestReplicaType) {
	for t := range testJob.Spec.TestReplicaSpecs {
		if strings.EqualFold(string(t), string(typ)) && t != typ {
			spec := testJob.Spec.TestReplicaSpecs[t]
			delete(testJob.Spec.TestReplicaSpecs, t)
			testJob.Spec.TestReplicaSpecs[typ] = spec
			return
		}
	}
}

// SetDefaults_TestJob sets any unspecified values to defaults.
func SetDefaults_TestJob(testjob *TestJob) {
	// Set default cleanpod policy to Running.
	if testjob.Spec.RunPolicy.CleanPodPolicy == nil {
		running := common.CleanPodPolicyRunning
		testjob.Spec.RunPolicy.CleanPodPolicy = &running
	}

	// Update the key of TestReplicaSpecs to camel case.
	setTypeNamesToCamelCase(testjob)

	for _, spec := range testjob.Spec.TestReplicaSpecs {
		// Set default replicas to 1.
		setDefaultReplicas(spec)
		// Set default port to the container.
		setDefaultPort(&spec.Template.Spec)
	}
}
<|endoftext|>


<commit_before><commit_msg>Global and local configuration: When using allmark init in the home directory it will create a GLOBAL configuration instead of a local configuration<commit_after><|endoftext|>


<commit_before>package jwt

import (
	"crypto/rsa"
	"errors"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"

	"bytes"

	"time"

	"io/ioutil"
	"path"

	"github.com/dgrijalva/jwt-go"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// global declaration of the backend to enable caching secret key material across requests
// when keys are specified in the config file
var b = backend{
	cache: make(map[string]keycache),
}

// AuthBackend represents a backend interface that retrieves secret key material
// to validate tokens
type AuthBackend interface {
	GetHMACSecret() (b []byte)
	GetRSAPublicKey() (r *rsa.PublicKey)
	IsConfigValid() (v bool)
}

func (h Auth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	// if the request path is any of the configured paths, validate JWT
	for _, p := range h.Rules {
		if !httpserver.Path(r.URL.Path).Matches(p.Path) {
			continue
		}

		// Check excepted paths for this rule and allow access without validating any token
		var isExceptedPath bool
		for _, e := range p.ExceptedPaths {
			if httpserver.Path(r.URL.Path).Matches(e) {
				isExceptedPath = true
			}
		}
		if isExceptedPath {
			continue
		}
		if r.URL.Path == "/" && p.AllowRoot {
			// special case for protecting children of the root path, only allow access to base directory with directive `allowbase`
			continue
		}

		// Path matches, look for unvalidated token
		uToken, err := ExtractToken(r)
		if err != nil {
			if p.Passthrough {
				continue
			}
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		// Initialize a new caching layer if this is the first request to a protected path.
		// Cache only operates when key material is stored on disk.  When using environment variables
		// this has no effect.
		_, ok := b.cache[p.KeyFile]
		if !ok {
			b.cache[p.KeyFile] = keycache{
				KeyFile:     p.KeyFile,
				KeyFileType: p.KeyFileType,
			}
		}
		b.current = b.cache[p.KeyFile]

		// Validate token
		vToken, err := ValidateToken(uToken, b)
		if err != nil {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}
		vClaims, err := Flatten(vToken.Claims.(jwt.MapClaims), "", DotStyle)
		if err != nil {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		// If token contains rules with allow or deny, evaluate
		if len(p.AccessRules) > 0 {
			var isAuthorized []bool
			for _, rule := range p.AccessRules {
				v := vClaims[rule.Claim]
				ruleMatches := contains(v, rule.Value) || v == rule.Value
				switch rule.Authorize {
				case ALLOW:
					isAuthorized = append(isAuthorized, ruleMatches)
				case DENY:
					isAuthorized = append(isAuthorized, !ruleMatches)
				default:
					return handleUnauthorized(w, r, p, h.Realm), fmt.Errorf("unknown rule type")
				}
			}
			// test all flags, if any are true then ok to pass
			ok := false
			for _, result := range isAuthorized {
				if result {
					ok = true
				}
			}
			if !ok {
				return handleForbidden(w, r, p, h.Realm), nil
			}
		}

		// set claims as separate headers for downstream to consume
		for claim, value := range vClaims {
			headerName := "Token-Claim-" + strings.ToUpper(claim)
			switch v := value.(type) {
			case string:
				r.Header.Set(headerName, v)
			case int64:
				r.Header.Set(headerName, strconv.FormatInt(v, 10))
			case bool:
				r.Header.Set(headerName, strconv.FormatBool(v))
			case int32:
				r.Header.Set(headerName, strconv.FormatInt(int64(v), 10))
			case float32:
				r.Header.Set(headerName, strconv.FormatFloat(float64(v), 'f', -1, 32))
			case float64:
				r.Header.Set(headerName, strconv.FormatFloat(v, 'f', -1, 64))
			case []interface{}:
				b := bytes.NewBufferString("")
				for i, item := range v {
					if i > 0 {
						b.WriteString(",")
					}
					b.WriteString(fmt.Sprintf("%v", item))
				}
				r.Header.Set(headerName, b.String())
			default:
				// ignore, because, JWT spec says in https://tools.ietf.org/html/rfc7519#section-4
				//     all claims that are not understood
				//     by implementations MUST be ignored.
			}
		}

		return h.Next.ServeHTTP(w, r)
	}
	// pass request if no paths protected with JWT
	return h.Next.ServeHTTP(w, r)
}

// ExtractToken will find a JWT token passed one of three ways: (1) as the Authorization
// header in the form `Bearer <JWT Token>`; (2) as a cookie named `jwt_token`; (3) as
// a URL query paramter of the form https://example.com?token=<JWT token>
func ExtractToken(r *http.Request) (string, error) {
	jwtHeader := strings.Split(r.Header.Get("Authorization"), " ")
	if jwtHeader[0] == "Bearer" && len(jwtHeader) == 2 {
		return jwtHeader[1], nil
	}

	jwtCookie, err := r.Cookie("jwt_token")
	if err == nil {
		return jwtCookie.Value, nil
	}

	jwtQuery := r.URL.Query().Get("token")
	if jwtQuery != "" {
		return jwtQuery, nil
	}

	return "", fmt.Errorf("no token found")
}

// ValidateToken will return a parsed token if it passes validation, or an
// error if any part of the token fails validation.  Possible errors include
// malformed tokens, unknown/unspecified signing algorithms, missing secret key,
// tokens that are not valid yet (i.e., 'nbf' field), tokens that are expired,
// and tokens that fail signature verification (forged)
func ValidateToken(uToken string, b AuthBackend) (*jwt.Token, error) {
	if len(uToken) == 0 {
		return nil, fmt.Errorf("Token length is zero")
	}

	if !b.IsConfigValid() {
		return nil, errors.New("No valid configuration for JWT validation found")
	}

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("HMAC: Unexpected signing method: %v", t.Header["alg"])
			}
			return hmac, nil
		})

		if err != nil {
			return nil, err
		}

		return token, nil

	case rsa != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
				return nil, fmt.Errorf("RSA: Unexpected signing method: %v", t.Header["alg"])
			}
			return rsa, nil
		})
		if err != nil {
			return nil, err
		}

		return token, nil
	default:
		return nil, errors.New("No valid configuration for JWT validation found")
	}

}

type backend struct {
	current keycache
	cache   map[string]keycache
}

type keycache struct {
	KeyFile     string
	KeyFileType EncryptionType
	Key         []byte
	ModifyTime  time.Time
}

func (b backend) GetHMACSecret() []byte {
	switch b.current.KeyFileType {
	case RSA:
		return nil
	case HMAC:
		if secret, err := readKeyFromFile(&b); err == nil {
			return secret
		}
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	default:
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	}
}

func (b backend) GetRSAPublicKey() *rsa.PublicKey {

	switch b.current.KeyFileType {
	case HMAC:
		return nil
	case RSA:
		if pem, err := readKeyFromFile(&b); err == nil {
			rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
			if err != nil {
				return nil
			}
			return rsaPub
		}
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	default:
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	}
}

func (b backend) IsConfigValid() bool {

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil && rsa == nil:
		return true
	case hmac == nil && rsa != nil:
		return true
	default:
		return false
	}
}

// readKeyFromFile attempts to read key material from the path specified in the config.
// If the path is not absolute it will attempt to find it as a relative path from the
// working directory.  To prevent issues with concurrent read/write to the filesystem
// on every request, it will cache the result of the file read and only re-read the file
// when the modification time is earlier than the cached value
func readKeyFromFile(b *backend) ([]byte, error) {

	var keyfilePath string
	if path.IsAbs(b.current.KeyFile) {
		keyfilePath = b.current.KeyFile
	} else {
		wd, err := os.Getwd()
		if err != nil {
			return nil, err
		}
		keyfilePath = path.Join(wd, b.current.KeyFile)
	}

	finfo, err := os.Stat(keyfilePath)
	if os.IsNotExist(err) {
		return nil, err
	}

	cachehit, ok := b.cache[b.current.KeyFile]
	if !ok {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}

		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	if finfo.ModTime().After(cachehit.ModifyTime) {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}
		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	return cachehit.Key, nil
}

// handleUnauthorized checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleUnauthorized(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}

	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"invalid_token\"", realm))
	return http.StatusUnauthorized
}

// handleForbidden checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleForbidden(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}
	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"insufficient_scope\"", realm))
	return http.StatusForbidden
}

// contains checks weather list is a slice ans containts the
// supplied string value.
func contains(list interface{}, value string) bool {
	switch l := list.(type) {
	case []interface{}:
		for _, v := range l {
			if v == value {
				return true
			}
		}
	}
	return false
}
<commit_msg>strip potentially spoofed claims in request headers<commit_after>package jwt

import (
	"crypto/rsa"
	"errors"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"

	"bytes"

	"time"

	"io/ioutil"
	"path"

	"github.com/dgrijalva/jwt-go"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// global declaration of the backend to enable caching secret key material across requests
// when keys are specified in the config file
var b = backend{
	cache: make(map[string]keycache),
}

// AuthBackend represents a backend interface that retrieves secret key material
// to validate tokens
type AuthBackend interface {
	GetHMACSecret() (b []byte)
	GetRSAPublicKey() (r *rsa.PublicKey)
	IsConfigValid() (v bool)
}

func (h Auth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	// if the request path is any of the configured paths, validate JWT
	for _, p := range h.Rules {
		if !httpserver.Path(r.URL.Path).Matches(p.Path) {
			continue
		}

		// strip potentially spoofed claims
		for header, _ := range r.Header {
			if strings.HasPrefix(header, "Token-Claim-") {
				r.Header.Del(header)
			}
		}

		// Check excepted paths for this rule and allow access without validating any token
		var isExceptedPath bool
		for _, e := range p.ExceptedPaths {
			if httpserver.Path(r.URL.Path).Matches(e) {
				isExceptedPath = true
			}
		}
		if isExceptedPath {
			continue
		}
		if r.URL.Path == "/" && p.AllowRoot {
			// special case for protecting children of the root path, only allow access to base directory with directive `allowbase`
			continue
		}

		// Path matches, look for unvalidated token
		uToken, err := ExtractToken(r)
		if err != nil {
			if p.Passthrough {
				continue
			}
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		// Initialize a new caching layer if this is the first request to a protected path.
		// Cache only operates when key material is stored on disk.  When using environment variables
		// this has no effect.
		_, ok := b.cache[p.KeyFile]
		if !ok {
			b.cache[p.KeyFile] = keycache{
				KeyFile:     p.KeyFile,
				KeyFileType: p.KeyFileType,
			}
		}
		b.current = b.cache[p.KeyFile]

		// Validate token
		vToken, err := ValidateToken(uToken, b)
		if err != nil {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}
		vClaims, err := Flatten(vToken.Claims.(jwt.MapClaims), "", DotStyle)
		if err != nil {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		// If token contains rules with allow or deny, evaluate
		if len(p.AccessRules) > 0 {
			var isAuthorized []bool
			for _, rule := range p.AccessRules {
				v := vClaims[rule.Claim]
				ruleMatches := contains(v, rule.Value) || v == rule.Value
				switch rule.Authorize {
				case ALLOW:
					isAuthorized = append(isAuthorized, ruleMatches)
				case DENY:
					isAuthorized = append(isAuthorized, !ruleMatches)
				default:
					return handleUnauthorized(w, r, p, h.Realm), fmt.Errorf("unknown rule type")
				}
			}
			// test all flags, if any are true then ok to pass
			ok := false
			for _, result := range isAuthorized {
				if result {
					ok = true
				}
			}
			if !ok {
				return handleForbidden(w, r, p, h.Realm), nil
			}
		}

		// set claims as separate headers for downstream to consume
		for claim, value := range vClaims {
			headerName := "Token-Claim-" + strings.ToUpper(claim)
			switch v := value.(type) {
			case string:
				r.Header.Set(headerName, v)
			case int64:
				r.Header.Set(headerName, strconv.FormatInt(v, 10))
			case bool:
				r.Header.Set(headerName, strconv.FormatBool(v))
			case int32:
				r.Header.Set(headerName, strconv.FormatInt(int64(v), 10))
			case float32:
				r.Header.Set(headerName, strconv.FormatFloat(float64(v), 'f', -1, 32))
			case float64:
				r.Header.Set(headerName, strconv.FormatFloat(v, 'f', -1, 64))
			case []interface{}:
				b := bytes.NewBufferString("")
				for i, item := range v {
					if i > 0 {
						b.WriteString(",")
					}
					b.WriteString(fmt.Sprintf("%v", item))
				}
				r.Header.Set(headerName, b.String())
			default:
				// ignore, because, JWT spec says in https://tools.ietf.org/html/rfc7519#section-4
				//     all claims that are not understood
				//     by implementations MUST be ignored.
			}
		}

		return h.Next.ServeHTTP(w, r)
	}
	// pass request if no paths protected with JWT
	return h.Next.ServeHTTP(w, r)
}

// ExtractToken will find a JWT token passed one of three ways: (1) as the Authorization
// header in the form `Bearer <JWT Token>`; (2) as a cookie named `jwt_token`; (3) as
// a URL query paramter of the form https://example.com?token=<JWT token>
func ExtractToken(r *http.Request) (string, error) {
	jwtHeader := strings.Split(r.Header.Get("Authorization"), " ")
	if jwtHeader[0] == "Bearer" && len(jwtHeader) == 2 {
		return jwtHeader[1], nil
	}

	jwtCookie, err := r.Cookie("jwt_token")
	if err == nil {
		return jwtCookie.Value, nil
	}

	jwtQuery := r.URL.Query().Get("token")
	if jwtQuery != "" {
		return jwtQuery, nil
	}

	return "", fmt.Errorf("no token found")
}

// ValidateToken will return a parsed token if it passes validation, or an
// error if any part of the token fails validation.  Possible errors include
// malformed tokens, unknown/unspecified signing algorithms, missing secret key,
// tokens that are not valid yet (i.e., 'nbf' field), tokens that are expired,
// and tokens that fail signature verification (forged)
func ValidateToken(uToken string, b AuthBackend) (*jwt.Token, error) {
	if len(uToken) == 0 {
		return nil, fmt.Errorf("Token length is zero")
	}

	if !b.IsConfigValid() {
		return nil, errors.New("No valid configuration for JWT validation found")
	}

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("HMAC: Unexpected signing method: %v", t.Header["alg"])
			}
			return hmac, nil
		})

		if err != nil {
			return nil, err
		}

		return token, nil

	case rsa != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
				return nil, fmt.Errorf("RSA: Unexpected signing method: %v", t.Header["alg"])
			}
			return rsa, nil
		})
		if err != nil {
			return nil, err
		}

		return token, nil
	default:
		return nil, errors.New("No valid configuration for JWT validation found")
	}

}

type backend struct {
	current keycache
	cache   map[string]keycache
}

type keycache struct {
	KeyFile     string
	KeyFileType EncryptionType
	Key         []byte
	ModifyTime  time.Time
}

func (b backend) GetHMACSecret() []byte {
	switch b.current.KeyFileType {
	case RSA:
		return nil
	case HMAC:
		if secret, err := readKeyFromFile(&b); err == nil {
			return secret
		}
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	default:
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	}
}

func (b backend) GetRSAPublicKey() *rsa.PublicKey {

	switch b.current.KeyFileType {
	case HMAC:
		return nil
	case RSA:
		if pem, err := readKeyFromFile(&b); err == nil {
			rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
			if err != nil {
				return nil
			}
			return rsaPub
		}
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	default:
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	}
}

func (b backend) IsConfigValid() bool {

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil && rsa == nil:
		return true
	case hmac == nil && rsa != nil:
		return true
	default:
		return false
	}
}

// readKeyFromFile attempts to read key material from the path specified in the config.
// If the path is not absolute it will attempt to find it as a relative path from the
// working directory.  To prevent issues with concurrent read/write to the filesystem
// on every request, it will cache the result of the file read and only re-read the file
// when the modification time is earlier than the cached value
func readKeyFromFile(b *backend) ([]byte, error) {

	var keyfilePath string
	if path.IsAbs(b.current.KeyFile) {
		keyfilePath = b.current.KeyFile
	} else {
		wd, err := os.Getwd()
		if err != nil {
			return nil, err
		}
		keyfilePath = path.Join(wd, b.current.KeyFile)
	}

	finfo, err := os.Stat(keyfilePath)
	if os.IsNotExist(err) {
		return nil, err
	}

	cachehit, ok := b.cache[b.current.KeyFile]
	if !ok {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}

		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	if finfo.ModTime().After(cachehit.ModifyTime) {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}
		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	return cachehit.Key, nil
}

// handleUnauthorized checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleUnauthorized(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}

	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"invalid_token\"", realm))
	return http.StatusUnauthorized
}

// handleForbidden checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleForbidden(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}
	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"insufficient_scope\"", realm))
	return http.StatusForbidden
}

// contains checks weather list is a slice ans containts the
// supplied string value.
func contains(list interface{}, value string) bool {
	switch l := list.(type) {
	case []interface{}:
		for _, v := range l {
			if v == value {
				return true
			}
		}
	}
	return false
}
<|endoftext|>


<commit_before>// BaruwaAPI Golang bindings for Baruwa REST API
// Copyright (C) 2019 Andrew Colin Kissa <andrew@topdog.za.net>

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

// Package api Golang bindings for Baruwa REST API
package api

// AuthServer holds an authentication server
type AuthServer struct {
	ID              int    `json:"id,omitempty"`
	Address         string `json:"address"`
	Protocol        int    `json:"protocol"`
	Port            int    `json:"port"`
	Enabled         bool   `json:"enabled"`
	SplitAddress    bool   `json:"split_address"`
	UserMapTemplate string `json:"user_map_template"`
}

// GetAuthServer returns an authentication server
// https://www.baruwa.com/docs/api/#retrieve-authentication-settings
func (c *Client) GetAuthServer(id int) (server *AuthServer, err error) {
	return
}

// CreateAuthServer creates an authentication server
// https://www.baruwa.com/docs/api/#create-authentication-settings
func (c *Client) CreateAuthServer(server *AuthServer) (err error) {
	return
}

// UpdateAuthServer updates an authentication server
// https://www.baruwa.com/docs/api/#update-authentication-settings
func (c *Client) UpdateAuthServer(server *AuthServer) (err error) {
	return
}

// DeleteAuthServer deletes an authentication server
// https://www.baruwa.com/docs/api/#delete-authentication-settings
func (c *Client) DeleteAuthServer(id int) (err error) {
	return
}
<commit_msg>FET: Domain Authentication settings implementation<commit_after>// BaruwaAPI Golang bindings for Baruwa REST API
// Copyright (C) 2019 Andrew Colin Kissa <andrew@topdog.za.net>

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

// Package api Golang bindings for Baruwa REST API
package api

import (
	"fmt"
	"net/url"

	"github.com/google/go-querystring/query"
)

// AuthServer holds an authentication server
type AuthServer struct {
	ID              int    `json:"id,omitempty"`
	Address         string `json:"address"`
	Protocol        int    `json:"protocol"`
	Port            int    `json:"port"`
	Enabled         bool   `json:"enabled"`
	SplitAddress    bool   `json:"split_address"`
	UserMapTemplate string `json:"user_map_template"`
}

// GetAuthServer returns an authentication server
// https://www.baruwa.com/docs/api/#retrieve-authentication-settings
func (c *Client) GetAuthServer(domainid, serverid int) (server *AuthServer, err error) {
	if domainid <= 0 {
		err = fmt.Errorf("The domainid param should be > 0")
		return
	}

	if serverid <= 0 {
		err = fmt.Errorf("The serverid param should be > 0")
		return
	}

	err = c.get(fmt.Sprintf("authservers/%d/%d", domainid, serverid), server)

	return
}

// CreateAuthServer creates an authentication server
// https://www.baruwa.com/docs/api/#create-authentication-settings
func (c *Client) CreateAuthServer(domainid int, server *AuthServer) (err error) {
	var v url.Values

	if domainid <= 0 {
		err = fmt.Errorf("The domainid param should be > 0")
		return
	}

	if server == nil {
		err = fmt.Errorf("The server param cannot be nil")
		return
	}

	if v, err = query.Values(server); err != nil {
		return
	}

	err = c.post(fmt.Sprintf("authservers/%d", domainid), v, server)

	return
}

// UpdateAuthServer updates an authentication server
// https://www.baruwa.com/docs/api/#update-authentication-settings
func (c *Client) UpdateAuthServer(domainid int, server *AuthServer) (err error) {
	var v url.Values

	if domainid <= 0 {
		err = fmt.Errorf("The domainid param should be > 0")
		return
	}

	if server == nil {
		err = fmt.Errorf("The server param cannot be nil")
		return
	}

	if server.ID <= 0 {
		err = fmt.Errorf("The server.ID param should be > 0")
		return
	}

	if v, err = query.Values(server); err != nil {
		return
	}

	err = c.post(fmt.Sprintf("authservers/%d/%d", domainid, server.ID), v, server)

	return
}

// DeleteAuthServer deletes an authentication server
// https://www.baruwa.com/docs/api/#delete-authentication-settings
func (c *Client) DeleteAuthServer(domainid int, server *AuthServer) (err error) {
	var v url.Values

	if domainid <= 0 {
		err = fmt.Errorf("The domainid param should be > 0")
		return
	}

	if server == nil {
		err = fmt.Errorf("The server param cannot be nil")
		return
	}

	if server.ID <= 0 {
		err = fmt.Errorf("The server.ID param should be > 0")
		return
	}

	if v, err = query.Values(server); err != nil {
		return
	}

	err = c.delete(fmt.Sprintf("authservers/%d/%d", domainid, server.ID), v)

	return
}
<|endoftext|>


<commit_before>package keystoneplugin

import (
	"encoding/json"
	"fmt"
	"log"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/iopred/bruxism"
)

type keystoneDungeonID int

const (
	BRH keystoneDungeonID = iota
	COS
	DHT
	EOA
	HOV
	MOS
	NL
	ARC
	VOW
)

type keystoneDungeon struct {
	Name    string
	Aliases []string
}

var dungeons map[keystoneDungeonID]*keystoneDungeon = map[keystoneDungeonID]*keystoneDungeon{
	BRH: &keystoneDungeon{
		Name:    "Black Rook Hold",
		Aliases: []string{"black rook hold", "brh", "black rook", "hold"},
	},
	COS: &keystoneDungeon{
		Name:    "Court of Stars",
		Aliases: []string{"court of stars", "cos", "court"},
	},
	DHT: &keystoneDungeon{
		Name:    "Darkheart Thicket",
		Aliases: []string{"darkheart thicket", "dht", "thicket", "darkheart"},
	},
	EOA: &keystoneDungeon{
		Name:    "Eye of Azshara",
		Aliases: []string{"eye of azshara", "eoa", "eye", "azshara"},
	},
	HOV: &keystoneDungeon{
		Name:    "Halls of Valor",
		Aliases: []string{"halls of valor", "hall of valor", "hov", "halls"},
	},
	MOS: &keystoneDungeon{
		Name:    "Maw of Souls",
		Aliases: []string{"maw of souls", "mos", "maw"},
	},
	NL: &keystoneDungeon{
		Name:    "Neltharion's Lair",
		Aliases: []string{"neltharion's lair", "nl", "neltharions lair", "nel", "nelth", "lair"},
	},
	ARC: &keystoneDungeon{
		Name:    "The Arcway",
		Aliases: []string{"the arcway", "arc", "arcway"},
	},
	VOW: &keystoneDungeon{
		Name:    "Vault of the Wardens",
		Aliases: []string{"vault of the wardens", "vow", "vault", "warden", "wardens"},
	},
}

type keystone struct {
	User      string
	Dungeon   keystoneDungeonID
	Level     int
	Depleted  bool
	Modifiers []string
}

func (k *keystone) String() string {
	str := fmt.Sprintf("Level %d **%s**", k.Level, dungeons[k.Dungeon].Name)
	if len(k.Modifiers) != 0 {
		str += " *(" + strings.Join(k.Modifiers, ", ") + ")*"
	}
	if k.Depleted {
		str += " - Depleted"
	}
	return str
}

type keystoneChannel struct {
	Users        map[string]*keystone
	LastModified time.Time
}

func lastTuesday(t time.Time) time.Time {
	year, month, day := t.Date()
	t = time.Date(year, month, day, 0, 0, 0, 0, t.Location())
	for t.Weekday() == 1 {
		t = t.Add(-24 * time.Hour)
	}

	return t
}

func (c *keystoneChannel) check() {
	if time.Now().After(lastTuesday(c.LastModified).Add(24 * 7 * time.Hour)) {
		c.Users = map[string]*keystone{}
	}
}

func (c *keystoneChannel) add(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, query string) bool {
	query = strings.ToLower(query)
	for dungeonID, dungeon := range dungeons {
		for _, alias := range dungeon.Aliases {
			if strings.Index(query, alias+" ") == 0 {
				query = query[len(alias)+1:]
				if len(query) == 0 {
					return false
				}

				parts := strings.Split(query, " ")

				level, err := strconv.Atoi(parts[0])
				if err != nil {
					return false
				}

				parts = parts[1:]

				depleted := false
				modifiers := []string{}

				for _, part := range parts {
					if strings.Index(part, "deplete") == 0 {
						depleted = true
					} else {
						modifiers = append(modifiers, part)
					}
				}

				c.Users[message.UserID()] = &keystone{
					User:      message.UserName(),
					Dungeon:   dungeonID,
					Level:     level,
					Depleted:  depleted,
					Modifiers: modifiers,
				}
				c.LastModified = time.Now()

				return true
			}
		}
	}
	return false
}

type keystoneList []*keystone

// Len is part of sort.Interface.
func (s keystoneList) Len() int {
	return len(s)
}

// Swap is part of sort.Interface.
func (s keystoneList) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s keystoneList) Less(i, j int) bool {
	if s[i].Level == s[j].Level {
		return dungeons[s[i].Dungeon].Name > dungeons[s[j].Dungeon].Name
	}
	return s[i].Level > s[j].Level
}

func (c *keystoneChannel) list(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if len(c.Users) == 0 {
		service.SendMessage(message.Channel(), "No keystones have been set this week.")
		return
	}

	keystones := keystoneList{}
	for _, keystone := range c.Users {
		keystones = append(keystones, keystone)
	}

	sort.Sort(keystones)

	content := ""
	for _, keystone := range keystones {
		if len(content) != 0 {
			content += "\n"
		}
		content += keystone.String()
		content += " - " + keystone.User
	}
	service.SendMessage(message.Channel(), content)
}

type keystonePlugin struct {
	sync.RWMutex
	Channels map[string]*keystoneChannel
}

// Name returns the name of the plugin.
func (p *keystonePlugin) Name() string {
	return "Keystone"
}

// Load will load plugin state from a byte array.
func (p *keystonePlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	if p.Channels == nil {
		p.Channels = make(map[string]*keystoneChannel)
	}

	return nil
}

// Save will save plugin state to a byte array.
func (p *keystonePlugin) Save() ([]byte, error) {
	return json.Marshal(p)
}

// Help returns a list of help strings that are printed when the user requests them.
func (p *keystonePlugin) Help(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	if p.Channels[message.Channel()] == nil {
		return nil
	}

	if !detailed {
		return []string{
			"`keystone <set <dungeon> <level> [modifiers]|list|deplete|undeplete>` - WoW Mythic Keystones.",
		}
	}

	return []string{
		"`keystone set<dungeon> <level> [modifiers]` - Set your keystone for this week.",
		"`keystone list` - Lists all this weeks keystones.",
		"`keystone deplete` - Depletes your keystone.",
		"Examples:",
		"`keystone set hov 5 teeming` - Adds a Level 5 Halls of Valor keystone with teeming.",
		"`keystone set eye of azshara 2 depleted` - Adds a depleted Level 2 Eye of Azshara keystone.",
	}
}

// Message handler.
func (p *keystonePlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	defer bruxism.MessageRecover()
	if !service.IsMe(message) {
		messageChannel := message.Channel()

		if service.IsBotOwner(message) && bruxism.MatchesCommand(service, "keystone", message) {
			_, parts := bruxism.ParseCommand(service, message)

			p.Lock()
			defer p.Unlock()

			switch parts[0] {
			case "start":
				p.Channels[messageChannel] = &keystoneChannel{
					Users: map[string]*keystone{},
				}
				service.SendMessage(messageChannel, "This channel is now tracking mythic keystones.")
			case "stop":
				delete(p.Channels, messageChannel)
				service.SendMessage(messageChannel, "This channel is no longer tracking mythic keystones.")
			}
		} else if channel, ok := p.Channels[messageChannel]; ok {
			channel.check()
			parts := strings.Split(strings.ToLower(message.Message()), " ")
			if len(parts) < 2 || parts[0] != "keystone" {
				return
			}

			switch parts[1] {
			case "set":
				if len(parts) > 2 && channel.add(bot, service, message, message.Message()[len("keystone set "):]) {
					channel.list(bot, service, message)
					service.SendMessage(messageChannel, fmt.Sprintf("Keystone set: %s", keystone.String()))
				} else {
					service.SendMessage(messageChannel, "Invalid keystone. Eg: `keystone hall of valor 3 sanguine`")
				}
			case "list":
				channel.list(bot, service, message)
			case "deplete":
				keystone := channel.Users[message.UserID()]
				if keystone == nil {
					service.SendMessage(messageChannel, "You haven't set a keystone this week.")
				} else {
					keystone.Depleted = true
					keystone.User = message.UserName()
					service.SendMessage(messageChannel, fmt.Sprintf("Keystone depleted: %s", keystone.String()))
				}
			case "undeplete":
				keystone := channel.Users[message.UserID()]
				if keystone == nil {
					service.SendMessage(messageChannel, "You haven't set a keystone this week.")
				} else {
					keystone.Depleted = false
					keystone.User = message.UserName()
					service.SendMessage(messageChannel, fmt.Sprintf("Keystone undepleted: %s", keystone.String()))
				}
			}
		}
	}
}

// Stats will return the stats for a plugin.
func (p *keystonePlugin) Stats(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) []string {
	return nil
}

// New will create a new wormhole plugin.
func New() bruxism.Plugin {
	return &keystonePlugin{}
}
<commit_msg>:ok_hand:<commit_after>package keystoneplugin

import (
	"encoding/json"
	"fmt"
	"log"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/iopred/bruxism"
)

type keystoneDungeonID int

const (
	BRH keystoneDungeonID = iota
	COS
	DHT
	EOA
	HOV
	MOS
	NL
	ARC
	VOW
)

type keystoneDungeon struct {
	Name    string
	Aliases []string
}

var dungeons map[keystoneDungeonID]*keystoneDungeon = map[keystoneDungeonID]*keystoneDungeon{
	BRH: &keystoneDungeon{
		Name:    "Black Rook Hold",
		Aliases: []string{"black rook hold", "brh", "black rook", "hold"},
	},
	COS: &keystoneDungeon{
		Name:    "Court of Stars",
		Aliases: []string{"court of stars", "cos", "court"},
	},
	DHT: &keystoneDungeon{
		Name:    "Darkheart Thicket",
		Aliases: []string{"darkheart thicket", "dht", "thicket", "darkheart"},
	},
	EOA: &keystoneDungeon{
		Name:    "Eye of Azshara",
		Aliases: []string{"eye of azshara", "eoa", "eye", "azshara"},
	},
	HOV: &keystoneDungeon{
		Name:    "Halls of Valor",
		Aliases: []string{"halls of valor", "hall of valor", "hov", "halls"},
	},
	MOS: &keystoneDungeon{
		Name:    "Maw of Souls",
		Aliases: []string{"maw of souls", "mos", "maw"},
	},
	NL: &keystoneDungeon{
		Name:    "Neltharion's Lair",
		Aliases: []string{"neltharion's lair", "nl", "neltharions lair", "nel", "nelth", "lair"},
	},
	ARC: &keystoneDungeon{
		Name:    "The Arcway",
		Aliases: []string{"the arcway", "arc", "arcway"},
	},
	VOW: &keystoneDungeon{
		Name:    "Vault of the Wardens",
		Aliases: []string{"vault of the wardens", "vow", "vault", "warden", "wardens"},
	},
}

type keystone struct {
	User      string
	Dungeon   keystoneDungeonID
	Level     int
	Depleted  bool
	Modifiers []string
}

func (k *keystone) String() string {
	str := fmt.Sprintf("Level %d **%s**", k.Level, dungeons[k.Dungeon].Name)
	if len(k.Modifiers) != 0 {
		str += " *(" + strings.Join(k.Modifiers, ", ") + ")*"
	}
	if k.Depleted {
		str += " - Depleted"
	}
	return str
}

type keystoneChannel struct {
	Users        map[string]*keystone
	LastModified time.Time
}

func lastTuesday(t time.Time) time.Time {
	year, month, day := t.Date()
	t = time.Date(year, month, day, 0, 0, 0, 0, t.Location())
	for t.Weekday() == 1 {
		t = t.Add(-24 * time.Hour)
	}

	return t
}

func (c *keystoneChannel) check() {
	if time.Now().After(lastTuesday(c.LastModified).Add(24 * 7 * time.Hour)) {
		c.Users = map[string]*keystone{}
	}
}

func (c *keystoneChannel) add(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, query string) bool {
	query = strings.ToLower(query)
	for dungeonID, dungeon := range dungeons {
		for _, alias := range dungeon.Aliases {
			if strings.Index(query, alias+" ") == 0 {
				query = query[len(alias)+1:]
				if len(query) == 0 {
					return false
				}

				parts := strings.Split(query, " ")

				level, err := strconv.Atoi(parts[0])
				if err != nil {
					return false
				}

				parts = parts[1:]

				depleted := false
				modifiers := []string{}

				for _, part := range parts {
					if strings.Index(part, "deplete") == 0 {
						depleted = true
					} else {
						modifiers = append(modifiers, part)
					}
				}

				c.Users[message.UserID()] = &keystone{
					User:      message.UserName(),
					Dungeon:   dungeonID,
					Level:     level,
					Depleted:  depleted,
					Modifiers: modifiers,
				}
				c.LastModified = time.Now()

				return true
			}
		}
	}
	return false
}

type keystoneList []*keystone

// Len is part of sort.Interface.
func (s keystoneList) Len() int {
	return len(s)
}

// Swap is part of sort.Interface.
func (s keystoneList) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s keystoneList) Less(i, j int) bool {
	if s[i].Level == s[j].Level {
		return dungeons[s[i].Dungeon].Name > dungeons[s[j].Dungeon].Name
	}
	return s[i].Level > s[j].Level
}

func (c *keystoneChannel) list(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if len(c.Users) == 0 {
		service.SendMessage(message.Channel(), "No keystones have been set this week.")
		return
	}

	keystones := keystoneList{}
	for _, keystone := range c.Users {
		keystones = append(keystones, keystone)
	}

	sort.Sort(keystones)

	content := ""
	for _, keystone := range keystones {
		if len(content) != 0 {
			content += "\n"
		}
		content += keystone.String()
		content += " - " + keystone.User
	}
	service.SendMessage(message.Channel(), content)
}

type keystonePlugin struct {
	sync.RWMutex
	Channels map[string]*keystoneChannel
}

// Name returns the name of the plugin.
func (p *keystonePlugin) Name() string {
	return "Keystone"
}

// Load will load plugin state from a byte array.
func (p *keystonePlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	if p.Channels == nil {
		p.Channels = make(map[string]*keystoneChannel)
	}

	return nil
}

// Save will save plugin state to a byte array.
func (p *keystonePlugin) Save() ([]byte, error) {
	return json.Marshal(p)
}

// Help returns a list of help strings that are printed when the user requests them.
func (p *keystonePlugin) Help(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	if p.Channels[message.Channel()] == nil {
		return nil
	}

	if !detailed {
		return []string{
			"`keystone <set <dungeon> <level> [modifiers]|list|deplete|undeplete>` - WoW Mythic Keystones.",
		}
	}

	return []string{
		"`keystone set<dungeon> <level> [modifiers]` - Set your keystone for this week.",
		"`keystone list` - Lists all this weeks keystones.",
		"`keystone deplete` - Depletes your keystone.",
		"Examples:",
		"`keystone set hov 5 teeming` - Adds a Level 5 Halls of Valor keystone with teeming.",
		"`keystone set eye of azshara 2 depleted` - Adds a depleted Level 2 Eye of Azshara keystone.",
	}
}

// Message handler.
func (p *keystonePlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	defer bruxism.MessageRecover()
	if !service.IsMe(message) {
		messageChannel := message.Channel()

		if service.IsBotOwner(message) && bruxism.MatchesCommand(service, "keystone", message) {
			_, parts := bruxism.ParseCommand(service, message)

			p.Lock()
			defer p.Unlock()

			switch parts[0] {
			case "start":
				p.Channels[messageChannel] = &keystoneChannel{
					Users: map[string]*keystone{},
				}
				service.SendMessage(messageChannel, "This channel is now tracking mythic keystones.")
			case "stop":
				delete(p.Channels, messageChannel)
				service.SendMessage(messageChannel, "This channel is no longer tracking mythic keystones.")
			}
		} else if channel, ok := p.Channels[messageChannel]; ok {
			channel.check()
			parts := strings.Split(strings.ToLower(message.Message()), " ")
			if len(parts) < 2 || parts[0] != "keystone" {
				return
			}

			switch parts[1] {
			case "set":
				if len(parts) > 2 && channel.add(bot, service, message, message.Message()[len("keystone set "):]) {
					channel.list(bot, service, message)
				} else {
					service.SendMessage(messageChannel, "Invalid keystone. Eg: `keystone hall of valor 3 sanguine`")
				}
			case "list":
				channel.list(bot, service, message)
			case "deplete":
				keystone := channel.Users[message.UserID()]
				if keystone == nil {
					service.SendMessage(messageChannel, "You haven't set a keystone this week.")
				} else {
					keystone.Depleted = true
					keystone.User = message.UserName()
					service.SendMessage(messageChannel, fmt.Sprintf("Keystone depleted: %s", keystone.String()))
				}
			case "undeplete":
				keystone := channel.Users[message.UserID()]
				if keystone == nil {
					service.SendMessage(messageChannel, "You haven't set a keystone this week.")
				} else {
					keystone.Depleted = false
					keystone.User = message.UserName()
					service.SendMessage(messageChannel, fmt.Sprintf("Keystone undepleted: %s", keystone.String()))
				}
			}
		}
	}
}

// Stats will return the stats for a plugin.
func (p *keystonePlugin) Stats(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) []string {
	return nil
}

// New will create a new wormhole plugin.
func New() bruxism.Plugin {
	return &keystonePlugin{}
}
<|endoftext|>