<commit_before>package classic

import (
// "fmt"
)

var jjbitVec0 = []int64{1, 0, 0, 0}

var jjnextStates = []int{
	37, 39, 40, 17, 18, 20, 42, 45, 31, 46, 43, 22, 23, 25, 26, 24,
	25, 26, 45, 31, 46, 44, 47, 35, 22, 28, 29, 27, 27, 30, 30, 0,
	1, 2, 4, 5,
}

var jjstrLiteralImages = map[int]string{
	0: "", 11: "\u0053", 12: "\055",
	14: "\050", 15: "\051", 16: "\072", 17: "\052", 18: "\136",
	25: "\133", 26: "\173", 28: "\124\117", 29: "\135", 30: "\175",
}

type TokenManager struct {
	curLexState     int
	defaultLexState int
	jjnewStateCnt   int
	jjround         int
	jjmatchedPos    int
	jjmatchedKind   int

	input_stream CharStream
	jjrounds     []int
	jjstateSet   []int
	curChar      rune
}

func newTokenManager(stream CharStream) *TokenManager {
	return &TokenManager{
		curLexState:     2,
		defaultLexState: 2,
		input_stream:    stream,
		jjrounds:        make([]int, 49),
		jjstateSet:      make([]int, 98),
	}
}

// L41

func (tm *TokenManager) jjMoveStringLiteralDfa0_2() int {
	switch tm.curChar {
	case 40:
		panic("not implemented yet")
	case 41:
		panic("not implemented yet")
	case 42:
		panic("not implemented yet")
	case 43:
		panic("not implemented yet")
	case 45:
		panic("not implemented yet")
	case 58:
		panic("not implemented yet")
	case 91:
		panic("not implemented yet")
	case 94:
		panic("not implemented yet")
	case 123:
		panic("not implemented yet")
	default:
		return tm.jjMoveNfa_2(0, 0)
	}
}

// L87

func (tm *TokenManager) jjMoveNfa_2(startState, curPos int) int {
	startsAt := 0
	tm.jjnewStateCnt = 49
	i := 1
	tm.jjstateSet[0] = startState
	kind := 0x7fffffff
	for {
		if tm.jjround++; tm.jjround == 0x7fffffff {
			tm.reInitRounds()
		}
		if tm.curChar < 64 {
			l := int64(1 << uint(tm.curChar))
			for {
				i--
				switch tm.jjstateSet[i] {
				case 49, 33:
					if (0xfbff7cf8ffffd9ff & uint64(l)) != 0 {
						if kind > 23 {
							kind = 23
						}
						tm.jjCheckNAddTwoStates(33, 34)
					}
				case 0:
					if (0xfbff54f8ffffd9ff & uint64(l)) != 0 {
						if kind > 23 {
							kind = 23
						}
						tm.jjCheckNAddTwoStates(33, 34)
					} else if (0x100002600 & l) != 0 {
						panic("not implemented yet")
					} else if (0x280200000000 & l) != 0 {
						panic("not implemented yet")
					} else if tm.curChar == 47 {
						panic("not implemented yet")
					} else if tm.curChar == 34 {
						panic("not implemented yet")
					}
					if (0x7bff50f8ffffd9ff & l) != 0 {
						if kind > 20 {
							kind = 20
						}
						tm.jjCheckNAddStates(6, 10)
					} else if tm.curChar == 42 {
						panic("not implemented yet")
					} else if tm.curChar == 33 {
						panic("not implemented yet")
					}
					if tm.curChar == 38 {
						panic("not implemented yet")
					}

				case 4:
					panic("not implemented yet")
				case 5:
					panic("not implemented yet")
				case 13:
					panic("not implemented yet")
				case 14:
					panic("not implemented yet")
				case 15:
					panic("not implemented yet")
				case 16:
					panic("not implemented yet")
				case 17:
					panic("not implemented yet")
				case 19:
					panic("not implemented yet")
				case 20:
					panic("not implemented yet")
				case 22:
					panic("not implemented yet")
				case 23:
					panic("not implemented yet")
				case 24:
					panic("not implemented yet")
				case 25:
					panic("not implemented yet")
				case 27:
					panic("not implemented yet")
				case 28:
					panic("not implemented yet")
				case 30:
					panic("not implemented yet")
				case 31:
					if tm.curChar == 42 && kind > 22 {
						kind = 22
					}
				case 32:
					panic("not implemented yet")
				case 35:
					panic("not implemented yet")
				case 36, 38:
					panic("not implemented yet")
				case 37:
					panic("not implemented yet")
				case 40:
					panic("not implemented yet")
				case 41:
					panic("not implemented yet")
				case 42:
					if (0x7bff78f8ffffd9ff & l) != 0 {
						if kind > 20 {
							kind = 20
						}
						tm.jjCheckNAddTwoStates(42, 43)
					}
				case 44:
					panic("not implemented yet")
				case 45:
					if (0x7bff78f8ffffd9ff & l) != 0 {
						tm.jjCheckNAddStates(18, 20)
					}
				case 47:
					panic("not implemented yet")
				}
				if i == startsAt {
					break
				}
			}
		} else if tm.curChar < 128 {
			panic("not implemented yet")
		} else {
			hiByte := int(tm.curChar >> 8)
			i1 := hiByte >> 6
			l1 := int64(1 << (uint64(hiByte) & 077))
			i2 := int((tm.curChar & 0xff) >> 6)
			l2 := int64(1 << uint64(tm.curChar&077))
			for {
				i--
				switch tm.jjstateSet[i] {
				case 49, 33:
					panic("not implemented yet")
				case 0:
					if jjCanMove_0(hiByte, i1, i2, l1, l2) {
						if kind > 7 {
							kind = 7
						}
					}
					if jjCanMove_2(hiByte, i1, i2, l1, l2) {
						if kind > 23 {
							kind = 23
						}
						tm.jjCheckNAddTwoStates(33, 34)
					}
					if jjCanMove_2(hiByte, i1, i2, l1, l2) {
						if kind > 20 {
							kind = 20
						}
						tm.jjCheckNAddStates(6, 10)
					}
				case 15:
					panic("not implemented yet")
				case 17, 19:
					panic("not implemented yet")
				case 25:
					panic("not implemented yet")
				case 27:
					panic("not implemented yet")
				case 28:
					panic("not implemented yet")
				case 30:
					panic("not implemented yet")
				case 32:
					panic("not implemented yet")
				case 35:
					panic("not implemented yet")
				case 37:
					panic("not implemented yet")
				case 41:
					panic("not implemented yet")
				case 42:
					panic("not implemented yet")
				case 44:
					panic("not implemented yet")
				case 45:
					panic("not implemented yet")
				case 47:
					panic("not implemented yet")
				}
				if i == startsAt {
					break
				}
			}
		}
		if kind != 0x7fffffff {
			tm.jjmatchedKind = kind
			tm.jjmatchedPos = curPos
			kind = 0x7fffffff
		}
		curPos++
		i = tm.jjnewStateCnt
		tm.jjnewStateCnt = startsAt
		startsAt = 49 - tm.jjnewStateCnt
		if i == startsAt {
			return curPos
		}
		var err error
		if tm.curChar, err = tm.input_stream.readChar(); err != nil {
			return curPos
		}
	}
	panic("should not be here")
}

func jjCanMove_0(hiByte, i1, i2 int, l1, l2 int64) bool {
	switch hiByte {
	case 48:
		return (jjbitVec0[i2] & 12) != 0
	}
	return false
}

func jjCanMove_2(hiByte, i1, i2 int, l1, l2 int64) bool {
	switch hiByte {
	case 0:
		panic("not implemented yet")
	case 48:
		panic("not implemented yet")
	}
	return false
}

func (tm *TokenManager) ReInit(stream CharStream) {
	tm.jjmatchedPos = 0
	tm.jjnewStateCnt = 0
	tm.curLexState = tm.defaultLexState
	tm.input_stream = stream
	tm.reInitRounds()
}

func (tm *TokenManager) reInitRounds() {
	tm.jjround = 0x80000001
	for i := 48; i >= 0; i-- {
		tm.jjrounds[i] = 0x80000000
	}
}

// L1027

func (tm *TokenManager) jjFillToken() *Token {
	var curTokenImage string
	if im, ok := jjstrLiteralImages[tm.jjmatchedKind]; ok {
		curTokenImage = im
	} else {
		curTokenImage = tm.input_stream.image()
	}
	beginLine := tm.input_stream.beginLine()
	beginColumn := tm.input_stream.beginColumn()
	endLine := tm.input_stream.endLine()
	endColumn := tm.input_stream.endColumn()
	t := newToken(tm.jjmatchedKind, curTokenImage)

	t.beginLine = beginLine
	t.endLine = endLine
	t.beginColumn = beginColumn
	t.endColumn = endColumn
	return t
}

func (tm *TokenManager) nextToken() (matchedToken *Token) {
	curPos := 0
	var err error
	var eof = false
	for !eof {
		if tm.curChar, err = tm.input_stream.beginToken(); err != nil {
			tm.jjmatchedKind = 0
			matchedToken = tm.jjFillToken()
			return
		}

		switch tm.curLexState {
		case 0:
			panic("not implemented yet")
		case 1:
			panic("not implemented yet")
		case 2:
			tm.jjmatchedKind = 0x7fffffff
			tm.jjmatchedPos = 0
			curPos = tm.jjMoveStringLiteralDfa0_2()
		}

		if tm.jjmatchedKind != 0x7fffffff {
			panic("not implemented yet")
		}
		error_line := tm.input_stream.endLine()
		error_column := tm.input_stream.endColumn()
		var error_after string
		var eofSeen = false
		if _, err = tm.input_stream.readChar(); err == nil {
			tm.input_stream.backup(1)
			tm.input_stream.backup(1)
			if curPos > 1 {
				error_after = tm.input_stream.image()
			}
		} else {
			eofSeen = true
			if curPos > 1 {
				error_after = tm.input_stream.image()
			}
			if tm.curChar == '\n' || tm.curChar == '\r' {
				error_line++
				error_column = 0
			} else {
				error_column++
			}
		}
		panic(newTokenMgrError(eofSeen, tm.curLexState, error_line,
			error_column, error_after, tm.curChar, LEXICAL_ERROR))
	}
	panic("should not be here")
}

// L1137
func (tm *TokenManager) jjCheckNAdd(state int) {
	if tm.jjrounds[state] != tm.jjround {
		tm.jjstateSet[tm.jjnewStateCnt] = state
		tm.jjnewStateCnt++
		tm.jjrounds[state] = tm.jjround
	}
}

// L1151

func (tm *TokenManager) jjCheckNAddTwoStates(state1, state2 int) {
	tm.jjCheckNAdd(state1)
	tm.jjCheckNAdd(state2)
}

func (tm *TokenManager) jjCheckNAddStates(start, end int) {
	assert(start < end)
	assert(start >= 0)
	assert(end <= len(jjnextStates))
	for {
		tm.jjCheckNAdd(jjnextStates[start])
		start++
		if start >= end {
			break
		}
	}
}

func assert(ok bool) {
	if !ok {
		panic("assert fail")
	}
}
<commit_msg>fix jjCanMove_2()<commit_after>package classic

import (
// "fmt"
)

var jjbitVec0 = []int64{1, 0, 0, 0}
var jjbitVec4 = []uint64{
	0xfffefffffffffffe, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff,
}

var jjnextStates = []int{
	37, 39, 40, 17, 18, 20, 42, 45, 31, 46, 43, 22, 23, 25, 26, 24,
	25, 26, 45, 31, 46, 44, 47, 35, 22, 28, 29, 27, 27, 30, 30, 0,
	1, 2, 4, 5,
}

var jjstrLiteralImages = map[int]string{
	0: "", 11: "\u0053", 12: "\055",
	14: "\050", 15: "\051", 16: "\072", 17: "\052", 18: "\136",
	25: "\133", 26: "\173", 28: "\124\117", 29: "\135", 30: "\175",
}

type TokenManager struct {
	curLexState     int
	defaultLexState int
	jjnewStateCnt   int
	jjround         int
	jjmatchedPos    int
	jjmatchedKind   int

	input_stream CharStream
	jjrounds     []int
	jjstateSet   []int
	curChar      rune
}

func newTokenManager(stream CharStream) *TokenManager {
	return &TokenManager{
		curLexState:     2,
		defaultLexState: 2,
		input_stream:    stream,
		jjrounds:        make([]int, 49),
		jjstateSet:      make([]int, 98),
	}
}

// L41

func (tm *TokenManager) jjMoveStringLiteralDfa0_2() int {
	switch tm.curChar {
	case 40:
		panic("not implemented yet")
	case 41:
		panic("not implemented yet")
	case 42:
		panic("not implemented yet")
	case 43:
		panic("not implemented yet")
	case 45:
		panic("not implemented yet")
	case 58:
		panic("not implemented yet")
	case 91:
		panic("not implemented yet")
	case 94:
		panic("not implemented yet")
	case 123:
		panic("not implemented yet")
	default:
		return tm.jjMoveNfa_2(0, 0)
	}
}

// L87

func (tm *TokenManager) jjMoveNfa_2(startState, curPos int) int {
	startsAt := 0
	tm.jjnewStateCnt = 49
	i := 1
	tm.jjstateSet[0] = startState
	kind := 0x7fffffff
	for {
		if tm.jjround++; tm.jjround == 0x7fffffff {
			tm.reInitRounds()
		}
		if tm.curChar < 64 {
			l := int64(1 << uint(tm.curChar))
			for {
				i--
				switch tm.jjstateSet[i] {
				case 49, 33:
					if (0xfbff7cf8ffffd9ff & uint64(l)) != 0 {
						if kind > 23 {
							kind = 23
						}
						tm.jjCheckNAddTwoStates(33, 34)
					}
				case 0:
					if (0xfbff54f8ffffd9ff & uint64(l)) != 0 {
						if kind > 23 {
							kind = 23
						}
						tm.jjCheckNAddTwoStates(33, 34)
					} else if (0x100002600 & l) != 0 {
						panic("not implemented yet")
					} else if (0x280200000000 & l) != 0 {
						panic("not implemented yet")
					} else if tm.curChar == 47 {
						panic("not implemented yet")
					} else if tm.curChar == 34 {
						panic("not implemented yet")
					}
					if (0x7bff50f8ffffd9ff & l) != 0 {
						if kind > 20 {
							kind = 20
						}
						tm.jjCheckNAddStates(6, 10)
					} else if tm.curChar == 42 {
						panic("not implemented yet")
					} else if tm.curChar == 33 {
						panic("not implemented yet")
					}
					if tm.curChar == 38 {
						panic("not implemented yet")
					}

				case 4:
					panic("not implemented yet")
				case 5:
					panic("not implemented yet")
				case 13:
					panic("not implemented yet")
				case 14:
					panic("not implemented yet")
				case 15:
					panic("not implemented yet")
				case 16:
					panic("not implemented yet")
				case 17:
					panic("not implemented yet")
				case 19:
					panic("not implemented yet")
				case 20:
					panic("not implemented yet")
				case 22:
					panic("not implemented yet")
				case 23:
					panic("not implemented yet")
				case 24:
					panic("not implemented yet")
				case 25:
					panic("not implemented yet")
				case 27:
					panic("not implemented yet")
				case 28:
					panic("not implemented yet")
				case 30:
					panic("not implemented yet")
				case 31:
					if tm.curChar == 42 && kind > 22 {
						kind = 22
					}
				case 32:
					panic("not implemented yet")
				case 35:
					panic("not implemented yet")
				case 36, 38:
					panic("not implemented yet")
				case 37:
					panic("not implemented yet")
				case 40:
					panic("not implemented yet")
				case 41:
					panic("not implemented yet")
				case 42:
					if (0x7bff78f8ffffd9ff & l) != 0 {
						if kind > 20 {
							kind = 20
						}
						tm.jjCheckNAddTwoStates(42, 43)
					}
				case 44:
					panic("not implemented yet")
				case 45:
					if (0x7bff78f8ffffd9ff & l) != 0 {
						tm.jjCheckNAddStates(18, 20)
					}
				case 47:
					panic("not implemented yet")
				}
				if i == startsAt {
					break
				}
			}
		} else if tm.curChar < 128 {
			panic("not implemented yet")
		} else {
			hiByte := int(tm.curChar >> 8)
			i1 := hiByte >> 6
			l1 := int64(1 << (uint64(hiByte) & 077))
			i2 := int((tm.curChar & 0xff) >> 6)
			l2 := int64(1 << uint64(tm.curChar&077))
			for {
				i--
				switch tm.jjstateSet[i] {
				case 49, 33:
					panic("not implemented yet")
				case 0:
					if jjCanMove_0(hiByte, i1, i2, l1, l2) {
						if kind > 7 {
							kind = 7
						}
					}
					if jjCanMove_2(hiByte, i1, i2, l1, l2) {
						if kind > 23 {
							kind = 23
						}
						tm.jjCheckNAddTwoStates(33, 34)
					}
					if jjCanMove_2(hiByte, i1, i2, l1, l2) {
						if kind > 20 {
							kind = 20
						}
						tm.jjCheckNAddStates(6, 10)
					}
				case 15:
					panic("not implemented yet")
				case 17, 19:
					panic("not implemented yet")
				case 25:
					panic("not implemented yet")
				case 27:
					panic("not implemented yet")
				case 28:
					panic("not implemented yet")
				case 30:
					panic("not implemented yet")
				case 32:
					panic("not implemented yet")
				case 35:
					panic("not implemented yet")
				case 37:
					panic("not implemented yet")
				case 41:
					panic("not implemented yet")
				case 42:
					panic("not implemented yet")
				case 44:
					panic("not implemented yet")
				case 45:
					panic("not implemented yet")
				case 47:
					panic("not implemented yet")
				}
				if i == startsAt {
					break
				}
			}
		}
		if kind != 0x7fffffff {
			tm.jjmatchedKind = kind
			tm.jjmatchedPos = curPos
			kind = 0x7fffffff
		}
		curPos++
		i = tm.jjnewStateCnt
		tm.jjnewStateCnt = startsAt
		startsAt = 49 - tm.jjnewStateCnt
		if i == startsAt {
			return curPos
		}
		var err error
		if tm.curChar, err = tm.input_stream.readChar(); err != nil {
			return curPos
		}
	}
	panic("should not be here")
}

func jjCanMove_0(hiByte, i1, i2 int, l1, l2 int64) bool {
	switch hiByte {
	case 48:
		return (jjbitVec0[i2] & 12) != 0
	}
	return false
}

func jjCanMove_2(hiByte, i1, i2 int, l1, l2 int64) bool {
	switch hiByte {
	case 0:
		panic("not implemented yet")
	case 48:
		panic("not implemented yet")
	}
	return (jjbitVec4[i1] & uint64(l1)) != 0
}

func (tm *TokenManager) ReInit(stream CharStream) {
	tm.jjmatchedPos = 0
	tm.jjnewStateCnt = 0
	tm.curLexState = tm.defaultLexState
	tm.input_stream = stream
	tm.reInitRounds()
}

func (tm *TokenManager) reInitRounds() {
	tm.jjround = 0x80000001
	for i := 48; i >= 0; i-- {
		tm.jjrounds[i] = 0x80000000
	}
}

// L1027

func (tm *TokenManager) jjFillToken() *Token {
	var curTokenImage string
	if im, ok := jjstrLiteralImages[tm.jjmatchedKind]; ok {
		curTokenImage = im
	} else {
		curTokenImage = tm.input_stream.image()
	}
	beginLine := tm.input_stream.beginLine()
	beginColumn := tm.input_stream.beginColumn()
	endLine := tm.input_stream.endLine()
	endColumn := tm.input_stream.endColumn()
	t := newToken(tm.jjmatchedKind, curTokenImage)

	t.beginLine = beginLine
	t.endLine = endLine
	t.beginColumn = beginColumn
	t.endColumn = endColumn
	return t
}

func (tm *TokenManager) nextToken() (matchedToken *Token) {
	curPos := 0
	var err error
	var eof = false
	for !eof {
		if tm.curChar, err = tm.input_stream.beginToken(); err != nil {
			tm.jjmatchedKind = 0
			matchedToken = tm.jjFillToken()
			return
		}

		switch tm.curLexState {
		case 0:
			panic("not implemented yet")
		case 1:
			panic("not implemented yet")
		case 2:
			tm.jjmatchedKind = 0x7fffffff
			tm.jjmatchedPos = 0
			curPos = tm.jjMoveStringLiteralDfa0_2()
		}

		if tm.jjmatchedKind != 0x7fffffff {
			panic("not implemented yet")
		}
		error_line := tm.input_stream.endLine()
		error_column := tm.input_stream.endColumn()
		var error_after string
		var eofSeen = false
		if _, err = tm.input_stream.readChar(); err == nil {
			tm.input_stream.backup(1)
			tm.input_stream.backup(1)
			if curPos > 1 {
				error_after = tm.input_stream.image()
			}
		} else {
			eofSeen = true
			if curPos > 1 {
				error_after = tm.input_stream.image()
			}
			if tm.curChar == '\n' || tm.curChar == '\r' {
				error_line++
				error_column = 0
			} else {
				error_column++
			}
		}
		panic(newTokenMgrError(eofSeen, tm.curLexState, error_line,
			error_column, error_after, tm.curChar, LEXICAL_ERROR))
	}
	panic("should not be here")
}

// L1137
func (tm *TokenManager) jjCheckNAdd(state int) {
	if tm.jjrounds[state] != tm.jjround {
		tm.jjstateSet[tm.jjnewStateCnt] = state
		tm.jjnewStateCnt++
		tm.jjrounds[state] = tm.jjround
	}
}

// L1151

func (tm *TokenManager) jjCheckNAddTwoStates(state1, state2 int) {
	tm.jjCheckNAdd(state1)
	tm.jjCheckNAdd(state2)
}

func (tm *TokenManager) jjCheckNAddStates(start, end int) {
	assert(start < end)
	assert(start >= 0)
	assert(end <= len(jjnextStates))
	for {
		tm.jjCheckNAdd(jjnextStates[start])
		start++
		if start >= end {
			break
		}
	}
}

func assert(ok bool) {
	if !ok {
		panic("assert fail")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"crypto/sha1"
	"fmt"
	"golang.org/x/crypto/bcrypt"
	//"golang.org/x/crypto/bcrypt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

var (
	null_time, _ = time.Parse("2006-01-02 15:04:05", "0000-00-00 00:00:00")
)

const (
	chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 abcdefghijklmnopqrstuvwxyz~!@#$%%^&*()_+{}[]-=:\"\\/?.>,<;:'"
)

func benchmarkTimer(name string, given_time time.Time, starting bool) time.Time {
	if starting {
		// starting benchmark test
		println(2, "Starting benchmark \""+name+"\"")
		return given_time
	} else {
		// benchmark is finished, print the duration
		// convert nanoseconds to a decimal seconds
		printf(2, "benchmark %s completed in %d seconds", name, time.Since(given_time).Seconds())
		return time.Now() // we don't really need this, but we have to return something
	}
}

func md5_sum(str string) string {
	hash := md5.New()
	io.WriteString(hash, str)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func sha1_sum(str string) string {
	hash := sha1.New()
	io.WriteString(hash, str)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func bcrypt_sum(str string) string {
	hash := ""
	digest, err := bcrypt.GenerateFromPassword([]byte(str), 4)
	if err == nil {
		hash = string(digest)
	}
	return hash
}

func byteByByteReplace(input, from, to string) string {
	if len(from) != len(to) {
		return ""
	}
	for i := 0; i < len(from); i += 1 {
		input = strings.Replace(input, from[i:i+1], to[i:i+1], -1)
	}
	return input
}

// Deletes files in a folder (root) that match a given regular expression.
// Returns the number of files that were deleted, and any error encountered.
func deleteMatchingFiles(root, match string) (files_deleted int, err error) {
	files, err := ioutil.ReadDir(root)
	if err != nil {
		return 0, err
	}
	for _, f := range files {
		match, _ := regexp.MatchString(match, f.Name())
		if match {
			os.Remove(filepath.Join(root, f.Name()))
			files_deleted++
		}
	}
	return files_deleted, err
}

// getBoardArr performs a query against the database, and returns an array of BoardsTables along with an error value.
// If specified, the string where is added to the query, prefaced by WHERE. An example valid value is where = "id = 1".
func getBoardArr(where string) (boards []BoardsTable, err error) {
	if where == "" {
		where = "1"
	}
	rows, err := db.Query("SELECT * FROM `" + config.DBprefix + "boards` WHERE " + where + " ORDER BY `order`;")
	if err != nil {
		error_log.Print(err.Error())
		return
	}

	// For each row in the results from the database, populate a new BoardsTable instance,
	// 	then append it to the boards array we are going to return
	for rows.Next() {
		board := new(BoardsTable)
		err = rows.Scan(
			&board.ID,
			&board.Order,
			&board.Dir,
			&board.Type,
			&board.UploadType,
			&board.Title,
			&board.Subtitle,
			&board.Description,
			&board.Section,
			&board.MaxImageSize,
			&board.MaxPages,
			&board.Locale,
			&board.DefaultStyle,
			&board.Locked,
			&board.CreatedOn,
			&board.Anonymous,
			&board.ForcedAnon,
			&board.MaxAge,
			&board.AutosageAfter,
			&board.NoImagesAfter,
			&board.MaxMessageLength,
			&board.EmbedsAllowed,
			&board.RedirectToThread,
			&board.RequireFile,
			&board.EnableCatalog,
		)
		board.IName = "board"
		if err != nil {
			error_log.Print(err.Error())
			fmt.Println(err.Error())
			return
		} else {
			boards = append(boards, *board)
		}
	}
	return
}

func getPostArr(sql string) (posts []interface{}, err error) {
	rows, err := db.Query(sql)
	if err != nil {
		error_log.Print(err.Error())
		return
	}
	for rows.Next() {
		var post PostTable
		err = rows.Scan(&post.ID, &post.BoardID, &post.ParentID, &post.Name, &post.Tripcode,
			&post.Email, &post.Subject, &post.MessageHTML, &post.MessageText, &post.Password, &post.Filename,
			&post.FilenameOriginal, &post.FileChecksum, &post.Filesize, &post.ImageW,
			&post.ImageH, &post.ThumbW, &post.ThumbH, &post.IP, &post.Tag, &post.Timestamp,
			&post.Autosage, &post.PosterAuthority, &post.DeletedTimestamp, &post.Bumped,
			&post.Stickied, &post.Locked, &post.Reviewed, &post.Sillytag)
		if err != nil {
			error_log.Print("util.go:getPostArr() ERROR: " + err.Error())
			return
		}
		posts = append(posts, post)
	}
	return
}

func getSectionArr(where string) (sections []interface{}, err error) {
	if where == "" {
		where = "1"
	}
	rows, err := db.Query("SELECT * FROM `" + config.DBprefix + "sections` WHERE " + where + " ORDER BY `order`;")
	if err != nil {
		error_log.Print(err.Error())
		return
	}

	for rows.Next() {
		section := new(BoardSectionsTable)
		section.IName = "section"

		err = rows.Scan(&section.ID, &section.Order, &section.Hidden, &section.Name, &section.Abbreviation)
		if err != nil {
			error_log.Print(err.Error())
			return
		}
		sections = append(sections, section)
	}
	return
}

func getCookie(name string) *http.Cookie {
	num_cookies := len(cookies)
	for c := 0; c < num_cookies; c += 1 {
		if cookies[c].Name == name {
			return cookies[c]
		}
	}
	return nil
}

func generateSalt() string {
	salt := make([]byte, 3)
	salt[0] = chars[rand.Intn(86)]
	salt[1] = chars[rand.Intn(86)]
	salt[2] = chars[rand.Intn(86)]
	return string(salt)
}

func getFileExtension(filename string) string {
	if strings.Index(filename, ".") == -1 {
		return ""
		//} else if strings.Index(filename, "/") > -1 {
	} else {
		return filename[strings.LastIndex(filename, ".")+1:]
	}
}

func getFormattedFilesize(size float32) string {
	if size < 1000 {
		return fmt.Sprintf("%fB", size)
	} else if size <= 100000 {
		return fmt.Sprintf("%fKB", size/1024)
	} else if size <= 100000000 {
		return fmt.Sprintf("%fMB", size/1024/1024)
	}
	return fmt.Sprintf("%0.2fGB", size/1024/1024/1024)
}

func getSQLDateTime() string {
	now := time.Now()
	return now.Format(mysql_datetime_format)
}

func getSpecificSQLDateTime(t time.Time) string {
	return t.Format(mysql_datetime_format)
}

func humanReadableTime(t time.Time) string {
	return t.Format(config.DateTimeFormat)
}

// paginate returns a 2d array of a specified interface from a 1d array passed in,
//	with a specified number of values per array in the 2d array.
// interface_length is the number of interfaces per array in the 2d array (e.g, threads per page)
// interf is the array of interfaces to be split up.
func paginate(interface_length int, interf []interface{}) [][]interface{} {
	// paginated_interfaces = the finished interface array
	// num_arrays = the current number of arrays (before remainder overflow)
	// interfaces_remaining = if greater than 0, these are the remaining interfaces
	// 		that will be added to the super-interface

	var paginated_interfaces [][]interface{}
	num_arrays := len(interf) / interface_length
	interfaces_remaining := len(interf) % interface_length
	//paginated_interfaces = append(paginated_interfaces, interf)
	current_interface := 0
	for l := 0; l < num_arrays; l++ {
		paginated_interfaces = append(paginated_interfaces,
			interf[current_interface:current_interface+interface_length])
		current_interface += interface_length
	}
	if interfaces_remaining > 0 {
		paginated_interfaces = append(paginated_interfaces, interf[len(interf)-interfaces_remaining:])
	}
	return paginated_interfaces
}

func printf(v int, format string, a ...interface{}) {
	if config.Verbosity >= v {
		fmt.Printf(format, a...)
	}
}

func println(v int, a ...interface{}) {
	/*if fmt.Sprintf("%s", a) == "sql: no rows in result set" {
		panic(a)
	}*/

	if config.Verbosity >= v {
		fmt.Println(a...)
	}
}

func resetBoardSectionArrays() {
	// run when the board list needs to be changed (board/section is added, deleted, etc)
	all_boards = nil
	all_sections = nil

	all_boards_a, _ := getBoardArr("")
	for _, b := range all_boards_a {
		all_boards = append(all_boards, b)
	}
	all_sections_a, _ := getSectionArr("")
	for _, b := range all_sections_a {
		all_boards = append(all_sections, b)
	}
}

func searchStrings(item string, arr []string, permissive bool) int {
	var length = len(arr)
	for i := 0; i < length; i++ {
		if item == arr[i] {
			return i
		}
	}
	return -1
}

func Btoi(b bool) int {
	if b == true {
		return 1
	}
	return 0
}

func Btoa(b bool) string {
	if b == true {
		return "1"
	}
	return "0"
}
<commit_msg>Remove commented out bcrypt reference, fix whitespace<commit_after>package main

import (
	"crypto/md5"
	"crypto/sha1"
	"fmt"
	"golang.org/x/crypto/bcrypt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

var (
	null_time, _ = time.Parse("2006-01-02 15:04:05", "0000-00-00 00:00:00")
)

const (
	chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 abcdefghijklmnopqrstuvwxyz~!@#$%%^&*()_+{}[]-=:\"\\/?.>,<;:'"
)

func benchmarkTimer(name string, given_time time.Time, starting bool) time.Time {
	if starting {
		// starting benchmark test
		println(2, "Starting benchmark \""+name+"\"")
		return given_time
	} else {
		// benchmark is finished, print the duration
		// convert nanoseconds to a decimal seconds
		printf(2, "benchmark %s completed in %d seconds", name, time.Since(given_time).Seconds())
		return time.Now() // we don't really need this, but we have to return something
	}
}

func md5_sum(str string) string {
	hash := md5.New()
	io.WriteString(hash, str)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func sha1_sum(str string) string {
	hash := sha1.New()
	io.WriteString(hash, str)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func bcrypt_sum(str string) string {
	hash := ""
	digest, err := bcrypt.GenerateFromPassword([]byte(str), 4)
	if err == nil {
		hash = string(digest)
	}
	return hash
}

func byteByByteReplace(input, from, to string) string {
	if len(from) != len(to) {
		return ""
	}
	for i := 0; i < len(from); i += 1 {
		input = strings.Replace(input, from[i:i+1], to[i:i+1], -1)
	}
	return input
}

// Deletes files in a folder (root) that match a given regular expression.
// Returns the number of files that were deleted, and any error encountered.
func deleteMatchingFiles(root, match string) (files_deleted int, err error) {
	files, err := ioutil.ReadDir(root)
	if err != nil {
		return 0, err
	}
	for _, f := range files {
		match, _ := regexp.MatchString(match, f.Name())
		if match {
			os.Remove(filepath.Join(root, f.Name()))
			files_deleted++
		}
	}
	return files_deleted, err
}

// getBoardArr performs a query against the database, and returns an array of BoardsTables along with an error value.
// If specified, the string where is added to the query, prefaced by WHERE. An example valid value is where = "id = 1".
func getBoardArr(where string) (boards []BoardsTable, err error) {
	if where == "" {
		where = "1"
	}
	rows, err := db.Query("SELECT * FROM `" + config.DBprefix + "boards` WHERE " + where + " ORDER BY `order`;")
	if err != nil {
		error_log.Print(err.Error())
		return
	}

	// For each row in the results from the database, populate a new BoardsTable instance,
	// 	then append it to the boards array we are going to return
	for rows.Next() {
		board := new(BoardsTable)
		err = rows.Scan(
			&board.ID,
			&board.Order,
			&board.Dir,
			&board.Type,
			&board.UploadType,
			&board.Title,
			&board.Subtitle,
			&board.Description,
			&board.Section,
			&board.MaxImageSize,
			&board.MaxPages,
			&board.Locale,
			&board.DefaultStyle,
			&board.Locked,
			&board.CreatedOn,
			&board.Anonymous,
			&board.ForcedAnon,
			&board.MaxAge,
			&board.AutosageAfter,
			&board.NoImagesAfter,
			&board.MaxMessageLength,
			&board.EmbedsAllowed,
			&board.RedirectToThread,
			&board.RequireFile,
			&board.EnableCatalog,
		)
		board.IName = "board"
		if err != nil {
			error_log.Print(err.Error())
			fmt.Println(err.Error())
			return
		} else {
			boards = append(boards, *board)
		}
	}
	return
}

func getPostArr(sql string) (posts []interface{}, err error) {
	rows, err := db.Query(sql)
	if err != nil {
		error_log.Print(err.Error())
		return
	}
	for rows.Next() {
		var post PostTable
		err = rows.Scan(&post.ID, &post.BoardID, &post.ParentID, &post.Name, &post.Tripcode,
			&post.Email, &post.Subject, &post.MessageHTML, &post.MessageText, &post.Password, &post.Filename,
			&post.FilenameOriginal, &post.FileChecksum, &post.Filesize, &post.ImageW,
			&post.ImageH, &post.ThumbW, &post.ThumbH, &post.IP, &post.Tag, &post.Timestamp,
			&post.Autosage, &post.PosterAuthority, &post.DeletedTimestamp, &post.Bumped,
			&post.Stickied, &post.Locked, &post.Reviewed, &post.Sillytag)
		if err != nil {
			error_log.Print("util.go:getPostArr() ERROR: " + err.Error())
			return
		}
		posts = append(posts, post)
	}
	return
}

func getSectionArr(where string) (sections []interface{}, err error) {
	if where == "" {
		where = "1"
	}
	rows, err := db.Query("SELECT * FROM `" + config.DBprefix + "sections` WHERE " + where + " ORDER BY `order`;")
	if err != nil {
		error_log.Print(err.Error())
		return
	}

	for rows.Next() {
		section := new(BoardSectionsTable)
		section.IName = "section"

		err = rows.Scan(&section.ID, &section.Order, &section.Hidden, &section.Name, &section.Abbreviation)
		if err != nil {
			error_log.Print(err.Error())
			return
		}
		sections = append(sections, section)
	}
	return
}

func getCookie(name string) *http.Cookie {
	num_cookies := len(cookies)
	for c := 0; c < num_cookies; c += 1 {
		if cookies[c].Name == name {
			return cookies[c]
		}
	}
	return nil
}

func generateSalt() string {
	salt := make([]byte, 3)
	salt[0] = chars[rand.Intn(86)]
	salt[1] = chars[rand.Intn(86)]
	salt[2] = chars[rand.Intn(86)]
	return string(salt)
}

func getFileExtension(filename string) string {
	if strings.Index(filename, ".") == -1 {
		return ""
		//} else if strings.Index(filename, "/") > -1 {
	} else {
		return filename[strings.LastIndex(filename, ".")+1:]
	}
}

func getFormattedFilesize(size float32) string {
	if size < 1000 {
		return fmt.Sprintf("%fB", size)
	} else if size <= 100000 {
		return fmt.Sprintf("%fKB", size/1024)
	} else if size <= 100000000 {
		return fmt.Sprintf("%fMB", size/1024/1024)
	}
	return fmt.Sprintf("%0.2fGB", size/1024/1024/1024)
}

func getSQLDateTime() string {
	now := time.Now()
	return now.Format(mysql_datetime_format)
}

func getSpecificSQLDateTime(t time.Time) string {
	return t.Format(mysql_datetime_format)
}

func humanReadableTime(t time.Time) string {
	return t.Format(config.DateTimeFormat)
}

// paginate returns a 2d array of a specified interface from a 1d array passed in,
//	with a specified number of values per array in the 2d array.
// interface_length is the number of interfaces per array in the 2d array (e.g, threads per page)
// interf is the array of interfaces to be split up.
func paginate(interface_length int, interf []interface{}) [][]interface{} {
	// paginated_interfaces = the finished interface array
	// num_arrays = the current number of arrays (before remainder overflow)
	// interfaces_remaining = if greater than 0, these are the remaining interfaces
	// 		that will be added to the super-interface

	var paginated_interfaces [][]interface{}
	num_arrays := len(interf) / interface_length
	interfaces_remaining := len(interf) % interface_length
	//paginated_interfaces = append(paginated_interfaces, interf)
	current_interface := 0
	for l := 0; l < num_arrays; l++ {
		paginated_interfaces = append(paginated_interfaces,
			interf[current_interface:current_interface+interface_length])
		current_interface += interface_length
	}
	if interfaces_remaining > 0 {
		paginated_interfaces = append(paginated_interfaces, interf[len(interf)-interfaces_remaining:])
	}
	return paginated_interfaces
}

func printf(v int, format string, a ...interface{}) {
	if config.Verbosity >= v {
		fmt.Printf(format, a...)
	}
}

func println(v int, a ...interface{}) {
	/*if fmt.Sprintf("%s", a) == "sql: no rows in result set" {
		panic(a)
	}*/

	if config.Verbosity >= v {
		fmt.Println(a...)
	}
}

func resetBoardSectionArrays() {
	// run when the board list needs to be changed (board/section is added, deleted, etc)
	all_boards = nil
	all_sections = nil

	all_boards_a, _ := getBoardArr("")
	for _, b := range all_boards_a {
		all_boards = append(all_boards, b)
	}
	all_sections_a, _ := getSectionArr("")
	for _, b := range all_sections_a {
		all_boards = append(all_sections, b)
	}
}

func searchStrings(item string, arr []string, permissive bool) int {
	var length = len(arr)
	for i := 0; i < length; i++ {
		if item == arr[i] {
			return i
		}
	}
	return -1
}

func Btoi(b bool) int {
	if b == true {
		return 1
	}
	return 0
}

func Btoa(b bool) string {
	if b == true {
		return "1"
	}
	return "0"
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"net"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/Zamiell/isaac-racing-server/src/log"
	"github.com/Zamiell/isaac-racing-server/src/models"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	limiter "github.com/julianshen/gin-limiter"
)

const (
	sessionName = "isaac.sid"
)

var (
	sessionStore sessions.CookieStore
	GATrackingID string
	myHTTPClient = &http.Client{ // We don't want to use the default http.Client structure because it has no default timeout set
		Timeout: 10 * time.Second,
	}
)

/*
	Data structures
*/

type TemplateData struct {
	Title string

	// Races stuff
	RaceResults    []models.RaceHistory
	ResultsRaces   []models.RaceHistory
	TotalRaceCount int
	TotalPages     int
	PreviousPage   int
	NextPage       int

	// Profiles/profile stuff
	ResultsProfiles   []models.ProfilesRow
	ResultsProfile    models.ProfileData
	TotalProfileCount int
	UsersPerPage      int
}

/*
	Initialization function
*/

func httpInit() {
	// Create a new Gin HTTP router
	gin.SetMode(gin.ReleaseMode) // Comment this out to debug HTTP stuff
	httpRouter := gin.Default()

	// Read some HTTP server configuration values from environment variables
	// (they were loaded from the .env file in main.go)
	sessionSecret := os.Getenv("SESSION_SECRET")
	if len(sessionSecret) == 0 {
		log.Info("The \"SESSION_SECRET\" environment variable is blank; aborting HTTP initalization.")
		return
	}
	domain := os.Getenv("DOMAIN")
	if len(domain) == 0 {
		log.Info("The \"DOMAIN\" environment variable is blank; aborting HTTP initalization.")
		return
	}
	tlsCertFile := os.Getenv("TLS_CERT_FILE")
	tlsKeyFile := os.Getenv("TLS_KEY_FILE")
	useTLS := true
	if len(tlsCertFile) == 0 || len(tlsKeyFile) == 0 {
		useTLS = false
	}

	// Create a session store
	sessionStore = sessions.NewCookieStore([]byte(sessionSecret))
	options := sessions.Options{
		Path:   "/",
		Domain: domain,
		MaxAge: 5, // 5 seconds
		// After getting a cookie via "/login", the client will immediately
		// establish a WebSocket connection via "/ws", so the cookie only needs
		// to exist for that time frame
		Secure: true,
		// Only send the cookie over HTTPS:
		// https://www.owasp.org/index.php/Testing_for_cookies_attributes_(OTG-SESS-002)
		HttpOnly: true,
		// Mitigate XSS attacks:
		// https://www.owasp.org/index.php/HttpOnly
	}
	if !useTLS {
		options.Secure = false
	}
	sessionStore.Options(options)
	httpRouter.Use(sessions.Sessions(sessionName, sessionStore))

	// Use the gin-limiter middleware for rate-limiting
	// (to only allow one request per second)
	// Based on: https://github.com/julianshen/gin-limiter/blob/master/example/web.go
	limiterMiddleware := limiter.NewRateLimiter(time.Second, 1, func(c *gin.Context) (string, error) {
		// Local variables
		r := c.Request
		ip, _, _ := net.SplitHostPort(r.RemoteAddr)

		// Just use the IP address as the key
		return ip, nil
	}).Middleware()
	httpRouter.Use(limiterMiddleware)

	// Use a custom middleware for Google Analytics tracking
	GATrackingID = os.Getenv("GA_TRACKING_ID")
	if len(GATrackingID) != 0 {
		httpRouter.Use(httpMwGoogleAnalytics)
	}

	// Path handlers (for the WebSocket server)
	httpRouter.POST("/login", httpLogin)
	httpRouter.POST("/register", httpRegister)
	httpRouter.GET("/ws", httpWS)

	// Path handlers (for the website)
	httpRouter.GET("/", httpHome)

	// Path handlers for single profile
	httpRouter.GET("/profile", httpProfile)
	httpRouter.GET("/profile/:player", httpProfile) // Handles profile username

	// Path handlers for all profiles
	httpRouter.GET("/profiles", httpProfiles)
	httpRouter.GET("/profiles/:page", httpProfiles) // Handles extra pages for profiles

	// Path handlers for race page
	httpRouter.GET("/race", httpRace)
	httpRouter.GET("/race/:raceid", httpRace)

	// Path handlers for races page
	httpRouter.GET("/races", httpRaces)
	httpRouter.GET("/races/:page", httpRaces)

	//	httpRouter.GET("/leaderboards", httpLeaderboards)
	httpRouter.GET("/info", httpInfo)
	httpRouter.GET("/download", httpDownload)
	httpRouter.Static("/public", "../public")

	// Figure out the port that we are using for the HTTP server
	var port int
	if useTLS {
		// We want all HTTP requests to be redirected to HTTPS
		// (but make an exception for Let's Encrypt)
		// The Gin router is using the default serve mux, so we need to create a
		// new fresh one for the HTTP handler
		HTTPServeMux := http.NewServeMux()
		HTTPServeMux.Handle("/.well-known/acme-challenge/", http.FileServer(http.FileSystem(http.Dir("letsencrypt"))))
		HTTPServeMux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			http.Redirect(w, req, "https://"+req.Host+req.URL.String(), http.StatusMovedPermanently)
		}))

		// ListenAndServe is blocking, so start listening on a new goroutine
		go func() {
			http.ListenAndServe(":80", HTTPServeMux) // Nothing before the colon implies 0.0.0.0
			log.Fatal("http.ListenAndServe ended for port 80.", nil)
		}()

		// 443 is the default port for HTTPS
		port = 443
	} else {
		// 80 is the defeault port for HTTP
		port = 80
	}

	// Start listening and serving requests (which is blocking)
	log.Info("Listening on port " + strconv.Itoa(port) + ".")
	if useTLS {
		if err := http.ListenAndServeTLS(
			":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0
			tlsCertFile,
			tlsKeyFile,
			httpRouter,
		); err != nil {
			log.Fatal("http.ListenAndServeTLS failed:", err)
		}
		log.Fatal("http.ListenAndServeTLS ended prematurely.", nil)
	} else {
		// Listen and serve (HTTP)
		if err := http.ListenAndServe(
			":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0
			httpRouter,
		); err != nil {
			log.Fatal("http.ListenAndServe failed:", err)
		}
		log.Fatal("http.ListenAndServe ended prematurely.", nil)
	}
}

/*
	HTTP miscellaneous subroutines
*/

func httpServeTemplate(w http.ResponseWriter, templateName string, data interface{}) {
	lp := path.Join("views", "layout.tmpl")
	fp := path.Join("views", templateName+".tmpl")

	// Return a 404 if the template doesn't exist
	info, err := os.Stat(fp)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			return
		}
	}

	// Return a 404 if the request is for a directory
	if info.IsDir() {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	// Create the template
	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		log.Error("Failed to create the template: " + err.Error())
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	// Execute the template and send it to the user
	if err := tmpl.ExecuteTemplate(w, "layout", data); strings.HasSuffix(err.Error(), ": write: broken pipe") {
		// Broken pipe errors can occur when the user presses the "Stop" button while the template is executing
		// We don't want to reporting these errors to Sentry
		// https://stackoverflow.com/questions/26853200/filter-out-broken-pipe-errors-from-template-execution
		log.Info("Failed to execute the template: " + err.Error())
	} else if err != nil {
		log.Error("Failed to execute the template: " + err.Error())
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
}
<commit_msg>fixing panics<commit_after>package main

import (
	"html/template"
	"net"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/Zamiell/isaac-racing-server/src/log"
	"github.com/Zamiell/isaac-racing-server/src/models"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	limiter "github.com/julianshen/gin-limiter"
)

const (
	sessionName = "isaac.sid"
)

var (
	sessionStore sessions.CookieStore
	GATrackingID string
	myHTTPClient = &http.Client{ // We don't want to use the default http.Client structure because it has no default timeout set
		Timeout: 10 * time.Second,
	}
)

/*
	Data structures
*/

type TemplateData struct {
	Title string

	// Races stuff
	RaceResults    []models.RaceHistory
	ResultsRaces   []models.RaceHistory
	TotalRaceCount int
	TotalPages     int
	PreviousPage   int
	NextPage       int

	// Profiles/profile stuff
	ResultsProfiles   []models.ProfilesRow
	ResultsProfile    models.ProfileData
	TotalProfileCount int
	UsersPerPage      int
}

/*
	Initialization function
*/

func httpInit() {
	// Create a new Gin HTTP router
	//gin.SetMode(gin.ReleaseMode) // Comment this out to debug HTTP stuff
	httpRouter := gin.Default()

	// Read some HTTP server configuration values from environment variables
	// (they were loaded from the .env file in main.go)
	sessionSecret := os.Getenv("SESSION_SECRET")
	if len(sessionSecret) == 0 {
		log.Info("The \"SESSION_SECRET\" environment variable is blank; aborting HTTP initalization.")
		return
	}
	domain := os.Getenv("DOMAIN")
	if len(domain) == 0 {
		log.Info("The \"DOMAIN\" environment variable is blank; aborting HTTP initalization.")
		return
	}
	tlsCertFile := os.Getenv("TLS_CERT_FILE")
	tlsKeyFile := os.Getenv("TLS_KEY_FILE")
	useTLS := true
	if len(tlsCertFile) == 0 || len(tlsKeyFile) == 0 {
		useTLS = false
	}

	// Create a session store
	sessionStore = sessions.NewCookieStore([]byte(sessionSecret))
	options := sessions.Options{
		Path:   "/",
		Domain: domain,
		MaxAge: 5, // 5 seconds
		// After getting a cookie via "/login", the client will immediately
		// establish a WebSocket connection via "/ws", so the cookie only needs
		// to exist for that time frame
		Secure: true,
		// Only send the cookie over HTTPS:
		// https://www.owasp.org/index.php/Testing_for_cookies_attributes_(OTG-SESS-002)
		HttpOnly: true,
		// Mitigate XSS attacks:
		// https://www.owasp.org/index.php/HttpOnly
	}
	if !useTLS {
		options.Secure = false
	}
	sessionStore.Options(options)
	httpRouter.Use(sessions.Sessions(sessionName, sessionStore))

	/*
		Commented out because it doesn't work:
		https://github.com/didip/tollbooth_gin/issues/3

		// Use the Tollbooth Gin middleware for Google Analytics tracking
		limiter := tollbooth.NewLimiter(1, time.Second, nil) // Limit each user to 1 request per second
		httpRouter.Use(tollbooth_gin.LimitHandler(limiter))
	*/

	// Use the gin-limiter middleware for rate-limiting
	// (to only allow one request per second)
	// Based on: https://github.com/julianshen/gin-limiter/blob/master/example/web.go
	limiterMiddleware := limiter.NewRateLimiter(time.Second*60, 60, func(c *gin.Context) (string, error) {
		// Local variables
		r := c.Request
		ip, _, _ := net.SplitHostPort(r.RemoteAddr)

		// Just use the IP address as the key
		return ip, nil
	}).Middleware()
	httpRouter.Use(limiterMiddleware)

	// Use a custom middleware for Google Analytics tracking
	GATrackingID = os.Getenv("GA_TRACKING_ID")
	if len(GATrackingID) != 0 {
		httpRouter.Use(httpMwGoogleAnalytics)
	}

	// Path handlers (for the WebSocket server)
	httpRouter.POST("/login", httpLogin)
	httpRouter.POST("/register", httpRegister)
	httpRouter.GET("/ws", httpWS)

	// Path handlers (for the website)
	httpRouter.GET("/", httpHome)

	// Path handlers for single profile
	httpRouter.GET("/profile", httpProfile)
	httpRouter.GET("/profile/:player", httpProfile) // Handles profile username

	// Path handlers for all profiles
	httpRouter.GET("/profiles", httpProfiles)
	httpRouter.GET("/profiles/:page", httpProfiles) // Handles extra pages for profiles

	// Path handlers for race page
	httpRouter.GET("/race", httpRace)
	httpRouter.GET("/race/:raceid", httpRace)

	// Path handlers for races page
	httpRouter.GET("/races", httpRaces)
	httpRouter.GET("/races/:page", httpRaces)

	//	httpRouter.GET("/leaderboards", httpLeaderboards)
	httpRouter.GET("/info", httpInfo)
	httpRouter.GET("/download", httpDownload)
	httpRouter.Static("/public", "../public")

	// Figure out the port that we are using for the HTTP server
	var port int
	if useTLS {
		// We want all HTTP requests to be redirected to HTTPS
		// (but make an exception for Let's Encrypt)
		// The Gin router is using the default serve mux, so we need to create a
		// new fresh one for the HTTP handler
		HTTPServeMux := http.NewServeMux()
		HTTPServeMux.Handle("/.well-known/acme-challenge/", http.FileServer(http.FileSystem(http.Dir("letsencrypt"))))
		HTTPServeMux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			http.Redirect(w, req, "https://"+req.Host+req.URL.String(), http.StatusMovedPermanently)
		}))

		// ListenAndServe is blocking, so start listening on a new goroutine
		go func() {
			http.ListenAndServe(":80", HTTPServeMux) // Nothing before the colon implies 0.0.0.0
			log.Fatal("http.ListenAndServe ended for port 80.", nil)
		}()

		// 443 is the default port for HTTPS
		port = 443
	} else {
		// 80 is the defeault port for HTTP
		port = 80
	}

	// Start listening and serving requests (which is blocking)
	log.Info("Listening on port " + strconv.Itoa(port) + ".")
	if useTLS {
		if err := http.ListenAndServeTLS(
			":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0
			tlsCertFile,
			tlsKeyFile,
			httpRouter,
		); err != nil {
			log.Fatal("http.ListenAndServeTLS failed:", err)
		}
		log.Fatal("http.ListenAndServeTLS ended prematurely.", nil)
	} else {
		// Listen and serve (HTTP)
		if err := http.ListenAndServe(
			":"+strconv.Itoa(port), // Nothing before the colon implies 0.0.0.0
			httpRouter,
		); err != nil {
			log.Fatal("http.ListenAndServe failed:", err)
		}
		log.Fatal("http.ListenAndServe ended prematurely.", nil)
	}
}

/*
	HTTP miscellaneous subroutines
*/

func httpServeTemplate(w http.ResponseWriter, templateName string, data interface{}) {
	lp := path.Join("views", "layout.tmpl")
	fp := path.Join("views", templateName+".tmpl")

	// Return a 404 if the template doesn't exist
	info, err := os.Stat(fp)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			return
		}
	}

	// Return a 404 if the request is for a directory
	if info.IsDir() {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	// Create the template
	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		log.Error("Failed to create the template: " + err.Error())
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	// Execute the template and send it to the user
	if err := tmpl.ExecuteTemplate(w, "layout", data); err != nil {
		if strings.HasSuffix(err.Error(), ": write: broken pipe") {
			// Broken pipe errors can occur when the user presses the "Stop" button while the template is executing
			// We don't want to reporting these errors to Sentry
			// https://stackoverflow.com/questions/26853200/filter-out-broken-pipe-errors-from-template-execution
			log.Info("Failed to execute the template: " + err.Error())
		} else {
			log.Error("Failed to execute the template: " + err.Error())
		}
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}
<|endoftext|>


<commit_before>// Copyright or whatever, Roger Booth (roger.booth@gmail.com)
// In the unlikely event that you find this code useful,
// feel free to provide attribution :)
package main

import (
	"container/ring"
	"fmt"
)

type Color string

var colors = [...]Color{"white", "blue", "red", "yellow", "orange", "green"}
var edgesForFace = map[Color][]Color{
	"white":  {"red", "green", "orange", "blue"},
	"blue":   {"white", "orange", "yellow", "red"},
	"red":    {"blue", "yellow", "green", "white"},
	"yellow": {"green", "red", "blue", "orange"},
	"orange": {"yellow", "blue", "white", "green"},
	"green":  {"orange", "white", "red", "yellow"},
}

var edgePos = [...]int{0, 7, 6, 4, 3, 2, 6, 5, 4, 2, 1, 0}

type Face [8]Color

type Edge [12]*Color

type Cube struct {
	faceMap map[Color]*Face
	edgeMap map[Color]Edge
}

type Entanglement [8]*Cube

func NewCube() (*Cube, error) {
	newFaceMap := make(map[Color]*Face)
	newEdgeMap := make(map[Color]Edge)
	for _, color := range colors {
		newFaceMap[color] = &Face{color, color, color, color, color, color, color, color}
	}
	i := 0
	for _, faceColor := range colors {
		var newEdge Edge
		for _, edgeColor := range edgesForFace[faceColor] {
		        //fmt.Println(faceColor)
		        //fmt.Println(i)
			newEdge[i] = &newFaceMap[edgeColor][edgePos[i]]
			newEdge[i+1] = &newFaceMap[edgeColor][edgePos[i+1]]
			newEdge[i+2] = &newFaceMap[edgeColor][edgePos[i+2]]
			i += 3
			if i == 12 {
			    i = 0
			}
		}
		newEdgeMap[faceColor] = newEdge
	}
	return &Cube{newFaceMap, newEdgeMap}, nil
}

func NewEntanglement() (*Entanglement, error) {
        var newEntanglement Entanglement
	for i:=0; i<8; i++{
		newEntanglement[i], _ = NewCube();
	}
	return &newEntanglement, nil
}

type ThreeDTransformer struct {
	faceRing *ring.Ring
	edgeRing *ring.Ring
}

func ThreeDRotate(ent *Entanglement, cubeId int, face Color, direction int) error {
        newFaceRing := ring.New(8)
        newEdgeRing := ring.New(12)
        trx := ThreeDTransformer{
		newFaceRing,newEdgeRing}
	for _, faceColor := range ent[cubeId].faceMap[face] {
		trx.faceRing.Value = faceColor
		trx.faceRing = trx.faceRing.Next()
	}
	for _, edgeColorPtr := range ent[cubeId].edgeMap[face] {
		trx.edgeRing.Value = *edgeColorPtr
		trx.edgeRing = trx.edgeRing.Next()
	}

	trx.faceRing = trx.faceRing.Move(2*direction)
	trx.edgeRing = trx.edgeRing.Move(3*direction)

	for i, _ := range ent[cubeId].faceMap[face] {
	        if v,ok := trx.faceRing.Value.(Color); ok {
		    ent[cubeId].faceMap[face][i] = v
		}
		trx.faceRing = trx.faceRing.Next()
	}
	for i, _ := range ent[cubeId].edgeMap[face] {
	        if v,ok := trx.edgeRing.Value.(Color); ok {
		    *ent[cubeId].edgeMap[face][i] = v
		}
		trx.edgeRing = trx.edgeRing.Next()
	}

        return nil
}

func main() {
	entanglement1,_ := NewEntanglement()
	fmt.Println(entanglement1[0].faceMap["red"][1])
	fmt.Println(entanglement1[0].faceMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][3])
	fmt.Println(*entanglement1[0].edgeMap["red"][8])
	fmt.Println(*entanglement1[0].edgeMap["red"][11])
	err := ThreeDRotate(entanglement1, 0, "red", 1)
	fmt.Println(err)
	fmt.Println(entanglement1[0].faceMap["red"][1])
	fmt.Println(entanglement1[0].faceMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][3])
	fmt.Println(*entanglement1[0].edgeMap["red"][8])
	fmt.Println(*entanglement1[0].edgeMap["red"][11])
<commit_msg>Changed some comments.<commit_after>// Copyright Roger Booth (roger.booth@gmail.com)
// In the event that you find this code useful, feel free to provide attribution :)

package main

import (
	"container/ring"
	"fmt"
)

type Color string

var colors = [...]Color{"white", "blue", "red", "yellow", "orange", "green"}
var edgesForFace = map[Color][]Color{
	"white":  {"red", "green", "orange", "blue"},
	"blue":   {"white", "orange", "yellow", "red"},
	"red":    {"blue", "yellow", "green", "white"},
	"yellow": {"green", "red", "blue", "orange"},
	"orange": {"yellow", "blue", "white", "green"},
	"green":  {"orange", "white", "red", "yellow"},
}

// Based on the coordinate system I discovered in 1984
var edgePos = [...]int{0, 7, 6, 4, 3, 2, 6, 5, 4, 2, 1, 0}

type Face [8]Color

type Edge [12]*Color

type Cube struct {
	faceMap map[Color]*Face
	edgeMap map[Color]Edge
}

type Entanglement [8]*Cube

func NewCube() (*Cube, error) {
	newFaceMap := make(map[Color]*Face)
	newEdgeMap := make(map[Color]Edge)
	for _, color := range colors {
		newFaceMap[color] = &Face{color, color, color, color, color, color, color, color}
	}
	i := 0
	for _, faceColor := range colors {
		var newEdge Edge
		for _, edgeColor := range edgesForFace[faceColor] {
		        //fmt.Println(faceColor)
		        //fmt.Println(i)
			newEdge[i] = &newFaceMap[edgeColor][edgePos[i]]
			newEdge[i+1] = &newFaceMap[edgeColor][edgePos[i+1]]
			newEdge[i+2] = &newFaceMap[edgeColor][edgePos[i+2]]
			i += 3
			if i == 12 {
			    i = 0
			}
		}
		newEdgeMap[faceColor] = newEdge
	}
	return &Cube{newFaceMap, newEdgeMap}, nil
}

func NewEntanglement() (*Entanglement, error) {
        var newEntanglement Entanglement
	for i:=0; i<8; i++{
		newEntanglement[i], _ = NewCube();
	}
	return &newEntanglement, nil
}

type ThreeDTransformer struct {
	faceRing *ring.Ring
	edgeRing *ring.Ring
}

func ThreeDRotate(ent *Entanglement, cubeId int, face Color, direction int) error {
        newFaceRing := ring.New(8)
        newEdgeRing := ring.New(12)
        trx := ThreeDTransformer{
		newFaceRing,newEdgeRing}
	for _, faceColor := range ent[cubeId].faceMap[face] {
		trx.faceRing.Value = faceColor
		trx.faceRing = trx.faceRing.Next()
	}
	for _, edgeColorPtr := range ent[cubeId].edgeMap[face] {
		trx.edgeRing.Value = *edgeColorPtr
		trx.edgeRing = trx.edgeRing.Next()
	}

	trx.faceRing = trx.faceRing.Move(2*direction)
	trx.edgeRing = trx.edgeRing.Move(3*direction)

	for i, _ := range ent[cubeId].faceMap[face] {
	        if v,ok := trx.faceRing.Value.(Color); ok {
		    ent[cubeId].faceMap[face][i] = v
		}
		trx.faceRing = trx.faceRing.Next()
	}
	for i, _ := range ent[cubeId].edgeMap[face] {
	        if v,ok := trx.edgeRing.Value.(Color); ok {
		    *ent[cubeId].edgeMap[face][i] = v
		}
		trx.edgeRing = trx.edgeRing.Next()
	}

        return nil
}

func main() {
	entanglement1,_ := NewEntanglement()
	fmt.Println(entanglement1[0].faceMap["red"][1])
	fmt.Println(entanglement1[0].faceMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][3])
	fmt.Println(*entanglement1[0].edgeMap["red"][8])
	fmt.Println(*entanglement1[0].edgeMap["red"][11])
	err := ThreeDRotate(entanglement1, 0, "red", 1)
	fmt.Println(err)
	fmt.Println(entanglement1[0].faceMap["red"][1])
	fmt.Println(entanglement1[0].faceMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][2])
	fmt.Println(*entanglement1[0].edgeMap["red"][3])
	fmt.Println(*entanglement1[0].edgeMap["red"][8])
	fmt.Println(*entanglement1[0].edgeMap["red"][11])
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/pprof"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/buddyfs/buddystore"
	"github.com/buddyfs/gobuddyfs"
)

var PORT uint = 9000
var TIMEOUT time.Duration = time.Duration(20 * time.Millisecond)

var Usage = func() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "  %s MOUNTPOINT\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	flag.Usage = Usage
	flag.Parse()

	if flag.NArg() != 1 {
		Usage()
		os.Exit(2)
	}
	mountpoint := flag.Arg(0)

	c, err := fuse.Mount(mountpoint)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	f, err := os.Create("buddyfs.prof")
	if err != nil {
		log.Fatal(err)
	}
	pprof.StartCPUProfile(f)
	defer pprof.StopCPUProfile()

	// kvStore := gobuddyfs.NewMemStore()

	var listen string = fmt.Sprintf("localhost:%d", PORT)
	trans, _ := buddystore.InitTCPTransport(listen, TIMEOUT)
	var conf *buddystore.Config = buddystore.DefaultConfig(listen)
	r, _ := buddystore.Create(conf, trans)
	kvStore := buddystore.NewKVStoreClient(r)

	err = fs.Serve(c, gobuddyfs.NewBuddyFS(kvStore))
	if err != nil {
		log.Fatal(err)
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Use BuddyStore instance to create a KVStoreClient instance.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/pprof"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/buddyfs/buddystore"
	"github.com/buddyfs/gobuddyfs"
	"github.com/golang/glog"
)

var PORT uint = 9000
var TIMEOUT time.Duration = time.Duration(20 * time.Millisecond)

var Usage = func() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "  %s MOUNTPOINT\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	flag.Usage = Usage
	flag.Parse()

	if flag.NArg() != 1 {
		Usage()
		os.Exit(2)
	}
	mountpoint := flag.Arg(0)

	c, err := fuse.Mount(mountpoint)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	f, err := os.Create("buddyfs.prof")
	if err != nil {
		log.Fatal(err)
	}
	pprof.StartCPUProfile(f)
	defer pprof.StopCPUProfile()

	// kvStore := gobuddyfs.NewMemStore()

	/*
		var listen string = fmt.Sprintf("localhost:%d", PORT)
		trans, _ := buddystore.InitTCPTransport(listen, TIMEOUT)
		var conf *buddystore.Config = buddystore.DefaultConfig(listen)
		r, _ := buddystore.Create(conf, trans)
		kvStore := buddystore.NewKVStoreClient(r)
	*/

	config := &buddystore.BuddyStoreConfig{MyID: "foo"}
	bStore := buddystore.NewBuddyStore(config)
	kvStore, errno := bStore.GetMyKVClient()

	if errno != buddystore.OK {
		glog.Fatalf("Error getting KVClient instance from Buddystore. %d", errno)
	}

	err = fs.Serve(c, gobuddyfs.NewBuddyFS(kvStore))
	if err != nil {
		log.Fatal(err)
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The prometheus-operator 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 alertmanager

import (
	"fmt"
	"net/url"
	"path"
	"strings"

	"k8s.io/api/apps/v1beta1"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"

	"github.com/blang/semver"
	monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
	"github.com/pkg/errors"
)

const (
	governingServiceName   = "alertmanager-operated"
	defaultVersion         = "v0.9.1"
	alertmanagerConfDir    = "/etc/alertmanager/config"
	alertmanagerConfFile   = alertmanagerConfDir + "/alertmanager.yaml"
	alertmanagerStorageDir = "/var/alertmanager/data"
)

var (
	minReplicas         int32 = 1
	probeTimeoutSeconds int32 = 3
)

func makeStatefulSet(am *monitoringv1.Alertmanager, old *v1beta1.StatefulSet, config Config) (*v1beta1.StatefulSet, error) {
	// TODO(fabxc): is this the right point to inject defaults?
	// Ideally we would do it before storing but that's currently not possible.
	// Potentially an update handler on first insertion.

	if am.Spec.BaseImage == "" {
		am.Spec.BaseImage = config.AlertmanagerDefaultBaseImage
	}
	if am.Spec.Version == "" {
		am.Spec.Version = defaultVersion
	}
	if am.Spec.Replicas == nil {
		am.Spec.Replicas = &minReplicas
	}
	intZero := int32(0)
	if am.Spec.Replicas != nil && *am.Spec.Replicas < 0 {
		am.Spec.Replicas = &intZero
	}
	if am.Spec.Resources.Requests == nil {
		am.Spec.Resources.Requests = v1.ResourceList{}
	}
	if _, ok := am.Spec.Resources.Requests[v1.ResourceMemory]; !ok {
		am.Spec.Resources.Requests[v1.ResourceMemory] = resource.MustParse("200Mi")
	}

	spec, err := makeStatefulSetSpec(am, config)
	if err != nil {
		return nil, err
	}

	boolTrue := true
	statefulset := &v1beta1.StatefulSet{
		ObjectMeta: metav1.ObjectMeta{
			Name:        prefixedName(am.Name),
			Labels:      config.Labels.Merge(am.ObjectMeta.Labels),
			Annotations: am.ObjectMeta.Annotations,
			OwnerReferences: []metav1.OwnerReference{
				{
					APIVersion:         am.APIVersion,
					BlockOwnerDeletion: &boolTrue,
					Controller:         &boolTrue,
					Kind:               am.Kind,
					Name:               am.Name,
					UID:                am.UID,
				},
			},
		},
		Spec: *spec,
	}

	if am.Spec.ImagePullSecrets != nil && len(am.Spec.ImagePullSecrets) > 0 {
		statefulset.Spec.Template.Spec.ImagePullSecrets = am.Spec.ImagePullSecrets
	}

	storageSpec := am.Spec.Storage
	if storageSpec == nil {
		statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, v1.Volume{
			Name: volumeName(am.Name),
			VolumeSource: v1.VolumeSource{
				EmptyDir: &v1.EmptyDirVolumeSource{},
			},
		})
	} else if storageSpec.EmptyDir != nil {
		emptyDir := storageSpec.EmptyDir
		statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, v1.Volume{
			Name: volumeName(am.Name),
			VolumeSource: v1.VolumeSource{
				EmptyDir: emptyDir,
			},
		})
	} else {
		pvcTemplate := storageSpec.VolumeClaimTemplate
		pvcTemplate.Name = volumeName(am.Name)
		pvcTemplate.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}
		pvcTemplate.Spec.Resources = storageSpec.VolumeClaimTemplate.Spec.Resources
		pvcTemplate.Spec.Selector = storageSpec.VolumeClaimTemplate.Spec.Selector
		statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, pvcTemplate)
	}

	if old != nil {
		statefulset.Annotations = old.Annotations
	}

	return statefulset, nil
}

func makeStatefulSetService(p *monitoringv1.Alertmanager, config Config) *v1.Service {
	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name: governingServiceName,
			Labels: config.Labels.Merge(map[string]string{
				"operated-alertmanager": "true",
			}),
		},
		Spec: v1.ServiceSpec{
			ClusterIP: "None",
			Ports: []v1.ServicePort{
				{
					Name:       "web",
					Port:       9093,
					TargetPort: intstr.FromInt(9093),
					Protocol:   v1.ProtocolTCP,
				},
				{
					Name:       "mesh",
					Port:       6783,
					TargetPort: intstr.FromInt(6783),
					Protocol:   v1.ProtocolTCP,
				},
			},
			Selector: map[string]string{
				"app": "alertmanager",
			},
		},
	}
	return svc
}

func makeStatefulSetSpec(a *monitoringv1.Alertmanager, config Config) (*v1beta1.StatefulSetSpec, error) {
	image := fmt.Sprintf("%s:%s", a.Spec.BaseImage, a.Spec.Version)
	versionStr := strings.TrimLeft(a.Spec.Version, "v")

	version, err := semver.Parse(versionStr)
	if err != nil {
		return nil, errors.Wrap(err, "parse version")
	}

	amArgs := []string{
		fmt.Sprintf("-config.file=%s", alertmanagerConfFile),
		fmt.Sprintf("-web.listen-address=:%d", 9093),
		fmt.Sprintf("-mesh.listen-address=:%d", 6783),
		fmt.Sprintf("-storage.path=%s", alertmanagerStorageDir),
	}

	if a.Spec.ExternalURL != "" {
		amArgs = append(amArgs, "-web.external-url="+a.Spec.ExternalURL)
	}

	webRoutePrefix := "/"
	if a.Spec.RoutePrefix != "" {
		webRoutePrefix = a.Spec.RoutePrefix
	}

	switch version.Major {
	case 0:
		if version.Minor >= 7 {
			amArgs = append(amArgs, "-web.route-prefix="+webRoutePrefix)
		}
	default:
		return nil, errors.Errorf("unsupported Alertmanager major version %s", version)
	}

	localReloadURL := &url.URL{
		Scheme: "http",
		Host:   "localhost:9093",
		Path:   path.Clean(webRoutePrefix + "/-/reload"),
	}

	probeHandler := v1.Handler{
		HTTPGet: &v1.HTTPGetAction{
			Path: path.Clean(webRoutePrefix + "/api/v1/status"),
			Port: intstr.FromString("web"),
		},
	}

	podAnnotations := map[string]string{}
	podLabels := map[string]string{}
	if a.Spec.PodMetadata != nil {
		if a.Spec.PodMetadata.Labels != nil {
			for k, v := range a.Spec.PodMetadata.Labels {
				podLabels[k] = v
			}
		}
		if a.Spec.PodMetadata.Annotations != nil {
			for k, v := range a.Spec.PodMetadata.Annotations {
				podAnnotations[k] = v
			}
		}
	}
	podLabels["app"] = "alertmanager"
	podLabels["alertmanager"] = a.Name

	for i := int32(0); i < *a.Spec.Replicas; i++ {
		amArgs = append(amArgs, fmt.Sprintf("-mesh.peer=%s-%d.%s.%s.svc", prefixedName(a.Name), i, governingServiceName, a.Namespace))
	}

	terminationGracePeriod := int64(0)
	return &v1beta1.StatefulSetSpec{
		ServiceName: governingServiceName,
		Replicas:    a.Spec.Replicas,
		UpdateStrategy: v1beta1.StatefulSetUpdateStrategy{
			Type: v1beta1.RollingUpdateStatefulSetStrategyType,
		},
		Template: v1.PodTemplateSpec{
			ObjectMeta: metav1.ObjectMeta{
				Labels:      config.Labels.Merge(podLabels),
				Annotations: podAnnotations,
			},
			Spec: v1.PodSpec{
				NodeSelector:                  a.Spec.NodeSelector,
				TerminationGracePeriodSeconds: &terminationGracePeriod,
				Containers: []v1.Container{
					{
						Args:  amArgs,
						Name:  "alertmanager",
						Image: image,
						Ports: []v1.ContainerPort{
							{
								Name:          "web",
								ContainerPort: 9093,
								Protocol:      v1.ProtocolTCP,
							},
							{
								Name:          "mesh",
								ContainerPort: 6783,
								Protocol:      v1.ProtocolTCP,
							},
						},
						VolumeMounts: []v1.VolumeMount{
							{
								Name:      "config-volume",
								MountPath: alertmanagerConfDir,
							},
							{
								Name:      volumeName(a.Name),
								MountPath: alertmanagerStorageDir,
								SubPath:   subPathForStorage(a.Spec.Storage),
							},
						},
						LivenessProbe: &v1.Probe{
							Handler:          probeHandler,
							TimeoutSeconds:   probeTimeoutSeconds,
							FailureThreshold: 10,
						},
						ReadinessProbe: &v1.Probe{
							Handler:             probeHandler,
							InitialDelaySeconds: 3,
							TimeoutSeconds:      3,
							PeriodSeconds:       5,
							FailureThreshold:    10,
						},
						Resources: a.Spec.Resources,
					}, {
						Name:  "config-reloader",
						Image: config.ConfigReloaderImage,
						Args: []string{
							fmt.Sprintf("-webhook-url=%s", localReloadURL),
							fmt.Sprintf("-volume-dir=%s", alertmanagerConfDir),
						},
						VolumeMounts: []v1.VolumeMount{
							{
								Name:      "config-volume",
								ReadOnly:  true,
								MountPath: alertmanagerConfDir,
							},
						},
						Resources: v1.ResourceRequirements{
							Limits: v1.ResourceList{
								v1.ResourceCPU:    resource.MustParse("5m"),
								v1.ResourceMemory: resource.MustParse("10Mi"),
							},
						},
					},
				},
				Volumes: []v1.Volume{
					{
						Name: "config-volume",
						VolumeSource: v1.VolumeSource{
							Secret: &v1.SecretVolumeSource{
								SecretName: configSecretName(a.Name),
							},
						},
					},
				},
				Tolerations: a.Spec.Tolerations,
				Affinity:    a.Spec.Affinity,
			},
		},
	}, nil
}

func configSecretName(name string) string {
	return prefixedName(name)
}

func volumeName(name string) string {
	return fmt.Sprintf("%s-db", prefixedName(name))
}

func prefixedName(name string) string {
	return fmt.Sprintf("alertmanager-%s", name)
}

func subPathForStorage(s *monitoringv1.StorageSpec) string {
	if s == nil {
		return ""
	}

	return "alertmanager-db"
}
<commit_msg>alertmanager: Use double dashed flags for >=v0.13.0<commit_after>// Copyright 2016 The prometheus-operator 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 alertmanager

import (
	"fmt"
	"net/url"
	"path"
	"strings"

	"k8s.io/api/apps/v1beta1"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"

	"github.com/blang/semver"
	monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
	"github.com/pkg/errors"
)

const (
	governingServiceName   = "alertmanager-operated"
	defaultVersion         = "v0.13.0"
	alertmanagerConfDir    = "/etc/alertmanager/config"
	alertmanagerConfFile   = alertmanagerConfDir + "/alertmanager.yaml"
	alertmanagerStorageDir = "/var/alertmanager/data"
)

var (
	minReplicas         int32 = 1
	probeTimeoutSeconds int32 = 3
)

func makeStatefulSet(am *monitoringv1.Alertmanager, old *v1beta1.StatefulSet, config Config) (*v1beta1.StatefulSet, error) {
	// TODO(fabxc): is this the right point to inject defaults?
	// Ideally we would do it before storing but that's currently not possible.
	// Potentially an update handler on first insertion.

	if am.Spec.BaseImage == "" {
		am.Spec.BaseImage = config.AlertmanagerDefaultBaseImage
	}
	if am.Spec.Version == "" {
		am.Spec.Version = defaultVersion
	}
	if am.Spec.Replicas == nil {
		am.Spec.Replicas = &minReplicas
	}
	intZero := int32(0)
	if am.Spec.Replicas != nil && *am.Spec.Replicas < 0 {
		am.Spec.Replicas = &intZero
	}
	if am.Spec.Resources.Requests == nil {
		am.Spec.Resources.Requests = v1.ResourceList{}
	}
	if _, ok := am.Spec.Resources.Requests[v1.ResourceMemory]; !ok {
		am.Spec.Resources.Requests[v1.ResourceMemory] = resource.MustParse("200Mi")
	}

	spec, err := makeStatefulSetSpec(am, config)
	if err != nil {
		return nil, err
	}

	boolTrue := true
	statefulset := &v1beta1.StatefulSet{
		ObjectMeta: metav1.ObjectMeta{
			Name:        prefixedName(am.Name),
			Labels:      config.Labels.Merge(am.ObjectMeta.Labels),
			Annotations: am.ObjectMeta.Annotations,
			OwnerReferences: []metav1.OwnerReference{
				{
					APIVersion:         am.APIVersion,
					BlockOwnerDeletion: &boolTrue,
					Controller:         &boolTrue,
					Kind:               am.Kind,
					Name:               am.Name,
					UID:                am.UID,
				},
			},
		},
		Spec: *spec,
	}

	if am.Spec.ImagePullSecrets != nil && len(am.Spec.ImagePullSecrets) > 0 {
		statefulset.Spec.Template.Spec.ImagePullSecrets = am.Spec.ImagePullSecrets
	}

	storageSpec := am.Spec.Storage
	if storageSpec == nil {
		statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, v1.Volume{
			Name: volumeName(am.Name),
			VolumeSource: v1.VolumeSource{
				EmptyDir: &v1.EmptyDirVolumeSource{},
			},
		})
	} else if storageSpec.EmptyDir != nil {
		emptyDir := storageSpec.EmptyDir
		statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, v1.Volume{
			Name: volumeName(am.Name),
			VolumeSource: v1.VolumeSource{
				EmptyDir: emptyDir,
			},
		})
	} else {
		pvcTemplate := storageSpec.VolumeClaimTemplate
		pvcTemplate.Name = volumeName(am.Name)
		pvcTemplate.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}
		pvcTemplate.Spec.Resources = storageSpec.VolumeClaimTemplate.Spec.Resources
		pvcTemplate.Spec.Selector = storageSpec.VolumeClaimTemplate.Spec.Selector
		statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, pvcTemplate)
	}

	if old != nil {
		statefulset.Annotations = old.Annotations
	}

	return statefulset, nil
}

func makeStatefulSetService(p *monitoringv1.Alertmanager, config Config) *v1.Service {
	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name: governingServiceName,
			Labels: config.Labels.Merge(map[string]string{
				"operated-alertmanager": "true",
			}),
		},
		Spec: v1.ServiceSpec{
			ClusterIP: "None",
			Ports: []v1.ServicePort{
				{
					Name:       "web",
					Port:       9093,
					TargetPort: intstr.FromInt(9093),
					Protocol:   v1.ProtocolTCP,
				},
				{
					Name:       "mesh",
					Port:       6783,
					TargetPort: intstr.FromInt(6783),
					Protocol:   v1.ProtocolTCP,
				},
			},
			Selector: map[string]string{
				"app": "alertmanager",
			},
		},
	}
	return svc
}

func makeStatefulSetSpec(a *monitoringv1.Alertmanager, config Config) (*v1beta1.StatefulSetSpec, error) {
	image := fmt.Sprintf("%s:%s", a.Spec.BaseImage, a.Spec.Version)
	versionStr := strings.TrimLeft(a.Spec.Version, "v")

	version, err := semver.Parse(versionStr)
	if err != nil {
		return nil, errors.Wrap(err, "parse version")
	}

	amArgs := []string{
		fmt.Sprintf("-config.file=%s", alertmanagerConfFile),
		fmt.Sprintf("-web.listen-address=:%d", 9093),
		fmt.Sprintf("-mesh.listen-address=:%d", 6783),
		fmt.Sprintf("-storage.path=%s", alertmanagerStorageDir),
	}

	if a.Spec.ExternalURL != "" {
		amArgs = append(amArgs, "-web.external-url="+a.Spec.ExternalURL)
	}

	webRoutePrefix := "/"
	if a.Spec.RoutePrefix != "" {
		webRoutePrefix = a.Spec.RoutePrefix
	}

	localReloadURL := &url.URL{
		Scheme: "http",
		Host:   "localhost:9093",
		Path:   path.Clean(webRoutePrefix + "/-/reload"),
	}

	probeHandler := v1.Handler{
		HTTPGet: &v1.HTTPGetAction{
			Path: path.Clean(webRoutePrefix + "/api/v1/status"),
			Port: intstr.FromString("web"),
		},
	}

	podAnnotations := map[string]string{}
	podLabels := map[string]string{}
	if a.Spec.PodMetadata != nil {
		if a.Spec.PodMetadata.Labels != nil {
			for k, v := range a.Spec.PodMetadata.Labels {
				podLabels[k] = v
			}
		}
		if a.Spec.PodMetadata.Annotations != nil {
			for k, v := range a.Spec.PodMetadata.Annotations {
				podAnnotations[k] = v
			}
		}
	}
	podLabels["app"] = "alertmanager"
	podLabels["alertmanager"] = a.Name

	for i := int32(0); i < *a.Spec.Replicas; i++ {
		amArgs = append(amArgs, fmt.Sprintf("-mesh.peer=%s-%d.%s.%s.svc", prefixedName(a.Name), i, governingServiceName, a.Namespace))
	}

	switch version.Major {
	case 0:
		if version.Minor >= 7 {
			amArgs = append(amArgs, "-web.route-prefix="+webRoutePrefix)
		}
		if version.Minor >= 13 {
			for i := range amArgs {
				// starting with v0.13.0 of Alertmanager all flags are with double dashes.
				amArgs[i] = "-" + amArgs[i]
			}
		}
	default:
		return nil, errors.Errorf("unsupported Alertmanager major version %s", version)
	}

	terminationGracePeriod := int64(0)
	return &v1beta1.StatefulSetSpec{
		ServiceName: governingServiceName,
		Replicas:    a.Spec.Replicas,
		UpdateStrategy: v1beta1.StatefulSetUpdateStrategy{
			Type: v1beta1.RollingUpdateStatefulSetStrategyType,
		},
		Template: v1.PodTemplateSpec{
			ObjectMeta: metav1.ObjectMeta{
				Labels:      config.Labels.Merge(podLabels),
				Annotations: podAnnotations,
			},
			Spec: v1.PodSpec{
				NodeSelector:                  a.Spec.NodeSelector,
				TerminationGracePeriodSeconds: &terminationGracePeriod,
				Containers: []v1.Container{
					{
						Args:  amArgs,
						Name:  "alertmanager",
						Image: image,
						Ports: []v1.ContainerPort{
							{
								Name:          "web",
								ContainerPort: 9093,
								Protocol:      v1.ProtocolTCP,
							},
							{
								Name:          "mesh",
								ContainerPort: 6783,
								Protocol:      v1.ProtocolTCP,
							},
						},
						VolumeMounts: []v1.VolumeMount{
							{
								Name:      "config-volume",
								MountPath: alertmanagerConfDir,
							},
							{
								Name:      volumeName(a.Name),
								MountPath: alertmanagerStorageDir,
								SubPath:   subPathForStorage(a.Spec.Storage),
							},
						},
						LivenessProbe: &v1.Probe{
							Handler:          probeHandler,
							TimeoutSeconds:   probeTimeoutSeconds,
							FailureThreshold: 10,
						},
						ReadinessProbe: &v1.Probe{
							Handler:             probeHandler,
							InitialDelaySeconds: 3,
							TimeoutSeconds:      3,
							PeriodSeconds:       5,
							FailureThreshold:    10,
						},
						Resources: a.Spec.Resources,
					}, {
						Name:  "config-reloader",
						Image: config.ConfigReloaderImage,
						Args: []string{
							fmt.Sprintf("-webhook-url=%s", localReloadURL),
							fmt.Sprintf("-volume-dir=%s", alertmanagerConfDir),
						},
						VolumeMounts: []v1.VolumeMount{
							{
								Name:      "config-volume",
								ReadOnly:  true,
								MountPath: alertmanagerConfDir,
							},
						},
						Resources: v1.ResourceRequirements{
							Limits: v1.ResourceList{
								v1.ResourceCPU:    resource.MustParse("5m"),
								v1.ResourceMemory: resource.MustParse("10Mi"),
							},
						},
					},
				},
				Volumes: []v1.Volume{
					{
						Name: "config-volume",
						VolumeSource: v1.VolumeSource{
							Secret: &v1.SecretVolumeSource{
								SecretName: configSecretName(a.Name),
							},
						},
					},
				},
				Tolerations: a.Spec.Tolerations,
				Affinity:    a.Spec.Affinity,
			},
		},
	}, nil
}

func configSecretName(name string) string {
	return prefixedName(name)
}

func volumeName(name string) string {
	return fmt.Sprintf("%s-db", prefixedName(name))
}

func prefixedName(name string) string {
	return fmt.Sprintf("alertmanager-%s", name)
}

func subPathForStorage(s *monitoringv1.StorageSpec) string {
	if s == nil {
		return ""
	}

	return "alertmanager-db"
}
<|endoftext|>


<commit_before>package sso

import (
	"crypto/subtle"
	"encoding/json"
	"net/http"
	"net/url"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/lestrrat-go/jwx/jwk"

	"github.com/skygeario/skygear-server/pkg/core/config"
	"github.com/skygeario/skygear-server/pkg/core/errors"
	corejwt "github.com/skygeario/skygear-server/pkg/core/jwt"
	coreUrl "github.com/skygeario/skygear-server/pkg/core/url"
)

type OIDCAuthParams struct {
	ProviderConfig config.OAuthProviderConfiguration
	RedirectURI    string
	Nonce          string
	EncodedState   string
	ExtraParams    map[string]string
}

type OIDCDiscoveryDocument struct {
	AuthorizationEndpoint string `json:"authorization_endpoint"`
	TokenEndpoint         string `json:"token_endpoint"`
	JWKSUri               string `json:"jwks_uri"`
}

func FetchOIDCDiscoveryDocument(client *http.Client, endpoint string) (*OIDCDiscoveryDocument, error) {
	resp, err := client.Get(endpoint)
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		return nil, errors.Newf("unexpected status code: %d", resp.StatusCode)
	}
	var document OIDCDiscoveryDocument
	err = json.NewDecoder(resp.Body).Decode(&document)
	if err != nil {
		return nil, err
	}
	return &document, nil
}

func (d *OIDCDiscoveryDocument) MakeOAuthURL(params OIDCAuthParams) string {
	v := coreUrl.Query{}
	v.Add("response_type", "code")
	v.Add("client_id", params.ProviderConfig.ClientID)
	v.Add("redirect_uri", params.RedirectURI)
	v.Add("scope", params.ProviderConfig.Scope)
	v.Add("nonce", params.Nonce)
	v.Add("response_mode", "form_post")
	for key, value := range params.ExtraParams {
		v.Add(key, value)
	}
	v.Add("state", params.EncodedState)

	return d.AuthorizationEndpoint + "?" + v.Encode()
}

func (d *OIDCDiscoveryDocument) FetchJWKs(client *http.Client) (*jwk.Set, error) {
	resp, err := client.Get(d.JWKSUri)
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		return nil, errors.Newf("unexpected status code: %d", resp.StatusCode)
	}
	return jwk.Parse(resp.Body)
}

func (d *OIDCDiscoveryDocument) ExchangeCode(
	client *http.Client,
	code string,
	jwks *jwk.Set,
	urlPrefix *url.URL,
	clientID string,
	clientSecret string,
	redirectURI string,
	nonce string,
	nowUTC func() time.Time,
	tokenResp *AccessTokenResp,
) (corejwt.MapClaims, error) {
	body := url.Values{}
	body.Set("grant_type", "authorization_code")
	body.Set("client_id", clientID)
	body.Set("code", code)
	body.Set("redirect_uri", redirectURI)
	body.Set("client_secret", clientSecret)

	resp, err := client.PostForm(d.TokenEndpoint, body)
	if err != nil {
		return nil, NewSSOFailed(NetworkFailed, "failed to connect authorization server")
	}
	defer resp.Body.Close()

	if resp.StatusCode == 200 {
		err = json.NewDecoder(resp.Body).Decode(&tokenResp)
		if err != nil {
			return nil, err
		}
	} else {
		var errorResp oauthErrorResp
		err = json.NewDecoder(resp.Body).Decode(&errorResp)
		if err != nil {
			return nil, err
		}
		err = errorResp.AsError()
		return nil, err
	}

	idToken := tokenResp.IDToken()
	keyFunc := func(token *jwt.Token) (interface{}, error) {
		keyID, ok := token.Header["kid"].(string)
		if !ok {
			return nil, NewSSOFailed(SSOUnauthorized, "no kid")
		}
		if key := jwks.LookupKeyID(keyID); len(key) == 1 {
			return key[0].Materialize()
		}
		return nil, NewSSOFailed(SSOUnauthorized, "failed to find signing key")
	}

	mapClaims := corejwt.MapClaims{}
	_, err = jwt.ParseWithClaims(idToken, mapClaims, keyFunc)
	if err != nil {
		return nil, NewSSOFailed(SSOUnauthorized, "invalid JWT signature")
	}

	if !mapClaims.VerifyAudience(clientID, true) {
		return nil, NewSSOFailed(SSOUnauthorized, "invalid aud")
	}

	hashedNonce, ok := mapClaims["nonce"].(string)
	if !ok {
		return nil, NewSSOFailed(InvalidParams, "no nonce")
	}
	if subtle.ConstantTimeCompare([]byte(hashedNonce), []byte(nonce)) != 1 {
		return nil, NewSSOFailed(SSOUnauthorized, "invalid nonce")
	}

	return mapClaims, nil
}
<commit_msg>Fix JWT claim parsing<commit_after>package sso

import (
	"crypto/subtle"
	"encoding/json"
	"net/http"
	"net/url"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/lestrrat-go/jwx/jwk"

	"github.com/skygeario/skygear-server/pkg/core/config"
	"github.com/skygeario/skygear-server/pkg/core/errors"
	corejwt "github.com/skygeario/skygear-server/pkg/core/jwt"
	coreUrl "github.com/skygeario/skygear-server/pkg/core/url"
)

type OIDCAuthParams struct {
	ProviderConfig config.OAuthProviderConfiguration
	RedirectURI    string
	Nonce          string
	EncodedState   string
	ExtraParams    map[string]string
}

type OIDCDiscoveryDocument struct {
	AuthorizationEndpoint string `json:"authorization_endpoint"`
	TokenEndpoint         string `json:"token_endpoint"`
	JWKSUri               string `json:"jwks_uri"`
}

func FetchOIDCDiscoveryDocument(client *http.Client, endpoint string) (*OIDCDiscoveryDocument, error) {
	resp, err := client.Get(endpoint)
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		return nil, errors.Newf("unexpected status code: %d", resp.StatusCode)
	}
	var document OIDCDiscoveryDocument
	err = json.NewDecoder(resp.Body).Decode(&document)
	if err != nil {
		return nil, err
	}
	return &document, nil
}

func (d *OIDCDiscoveryDocument) MakeOAuthURL(params OIDCAuthParams) string {
	v := coreUrl.Query{}
	v.Add("response_type", "code")
	v.Add("client_id", params.ProviderConfig.ClientID)
	v.Add("redirect_uri", params.RedirectURI)
	v.Add("scope", params.ProviderConfig.Scope)
	v.Add("nonce", params.Nonce)
	v.Add("response_mode", "form_post")
	for key, value := range params.ExtraParams {
		v.Add(key, value)
	}
	v.Add("state", params.EncodedState)

	return d.AuthorizationEndpoint + "?" + v.Encode()
}

func (d *OIDCDiscoveryDocument) FetchJWKs(client *http.Client) (*jwk.Set, error) {
	resp, err := client.Get(d.JWKSUri)
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		return nil, errors.Newf("unexpected status code: %d", resp.StatusCode)
	}
	return jwk.Parse(resp.Body)
}

func (d *OIDCDiscoveryDocument) ExchangeCode(
	client *http.Client,
	code string,
	jwks *jwk.Set,
	urlPrefix *url.URL,
	clientID string,
	clientSecret string,
	redirectURI string,
	nonce string,
	nowUTC func() time.Time,
	tokenResp *AccessTokenResp,
) (corejwt.MapClaims, error) {
	body := url.Values{}
	body.Set("grant_type", "authorization_code")
	body.Set("client_id", clientID)
	body.Set("code", code)
	body.Set("redirect_uri", redirectURI)
	body.Set("client_secret", clientSecret)

	resp, err := client.PostForm(d.TokenEndpoint, body)
	if err != nil {
		return nil, NewSSOFailed(NetworkFailed, "failed to connect authorization server")
	}
	defer resp.Body.Close()

	if resp.StatusCode == 200 {
		err = json.NewDecoder(resp.Body).Decode(&tokenResp)
		if err != nil {
			return nil, err
		}
	} else {
		var errorResp oauthErrorResp
		err = json.NewDecoder(resp.Body).Decode(&errorResp)
		if err != nil {
			return nil, err
		}
		err = errorResp.AsError()
		return nil, err
	}

	idToken := tokenResp.IDToken()
	keyFunc := func(token *jwt.Token) (interface{}, error) {
		keyID, ok := token.Header["kid"].(string)
		if !ok {
			return nil, NewSSOFailed(SSOUnauthorized, "no kid")
		}
		if key := jwks.LookupKeyID(keyID); len(key) == 1 {
			return key[0].Materialize()
		}
		return nil, NewSSOFailed(SSOUnauthorized, "failed to find signing key")
	}

	mapClaims := corejwt.MapClaims{}
	_, err = jwt.ParseWithClaims(idToken, &mapClaims, keyFunc)
	if err != nil {
		return nil, NewSSOFailed(SSOUnauthorized, "invalid JWT signature")
	}

	if !mapClaims.VerifyAudience(clientID, true) {
		return nil, NewSSOFailed(SSOUnauthorized, "invalid aud")
	}

	hashedNonce, ok := mapClaims["nonce"].(string)
	if !ok {
		return nil, NewSSOFailed(InvalidParams, "no nonce")
	}
	if subtle.ConstantTimeCompare([]byte(hashedNonce), []byte(nonce)) != 1 {
		return nil, NewSSOFailed(SSOUnauthorized, "invalid nonce")
	}

	return mapClaims, nil
}
<|endoftext|>