<commit_before>package main

import (
	// General
	"github.com/golang/glog"
	"flag"
	"github.com/iambc/xerrors"
	"reflect"
	"os"

	//API
	"net/http"
	"encoding/json"

	//DB
	"database/sql"
	_ "github.com/lib/pq"
)

/*
TODO:
2) Add different input/output formats for the API
3) Add settings to the boards
4) Add settings to the threads
5) Improve the error handling
6) quote of the day
*/

type image_board_clusters struct {
    Id int
    Descr string
    LongDescr string
    BoardLimitCount int
}

type boards struct {
    Id int
    Name string
    Descr string
    ImageBoardClusterId string
    MaxThreadCount int //to be checked in insert thread
    MaxActiveThreadCount int //to be checked  in insert thread
    MaxPostsPerThread int // to be checked in insert thread
    AreAttachmentsAllowed bool // to be checked in insert post
    PostLimitsReachedActionId int // to be checked in insert post
}

type threads struct{
    Id int
    Name string
    Descr string
    Board_id int
    MaxPostsPerThread int
    AreAttachmentsAllowed bool
    LimitsReachedActionId int
}

type thread_posts struct{
    Id int
    Body string
    ThreadId int
    AttachmentUrl int
}

type thread_limits_reached_actions struct{
    Id	    int
    Name    string
    Descr   string
}

type api_request struct{
    Status  string
    Msg	    *string
    Payload interface{}
}

func getBoards(res http.ResponseWriter, req *http.Request)  ([]byte, error) {
    if req == nil || res == nil {
	return []byte{}, xerrors.NewSysErr()
    }

    dbh, err := sql.Open("postgres", dbConnString)
    if err != nil {
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    values := req.URL.Query()
    api_key := values[`api_key`][0]
    rows, err := dbh.Query("select b.id, b.name, b.descr from boards b join image_board_clusters ibc on ibc.id = b.id where api_key = $1;", api_key)
    if err != nil {
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `002`, true)
    }
    defer rows.Close()

    var curr_boards []boards
    for rows.Next() {
	var board boards
	err = rows.Scan(&board.Id, &board.Name, &board.Descr)
	if err != nil {
	    return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `003`, true)
	}
	curr_boards = append(curr_boards, board)
    }
    bytes, err1 := json.Marshal(api_request{"ok", nil, &curr_boards})
    if err1 != nil {
	return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `004`, true)
    }
    return bytes, nil
}

func getActiveThreadsForBoard(res http.ResponseWriter, req *http.Request)  ([]byte, error) {
    if req == nil || res == nil {
        return []byte{}, xerrors.NewSysErr()
    }
    values := req.URL.Query()
    board_id, is_passed := values[`board_id`]
    if !is_passed {
	return []byte{}, xerrors.NewUIErr(`Invalid params: No board_id given!`, `Invalid params: No board_id given!`, `005`, true)
    }

    dbh, err := sql.Open("postgres", dbConnString)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    api_key := values[`api_key`][0]
    rows, err := dbh.Query(`select t.id, t.name from threads t
				join boards b on b.id = t.board_id
				join image_board_clusters ibc on ibc.id = b.id
			    where t.is_active = TRUE and t.board_id = $1 and ibc.api_key = $2;`, board_id[0], api_key)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }
    defer rows.Close()

    var active_threads []threads
    for rows.Next() {
	glog.Info("Popped new thread")
        var thread threads
        err = rows.Scan(&thread.Id, &thread.Name)
        if err != nil {
            return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
        }
        active_threads = append(active_threads, thread)
    }
    var bytes []byte
    var err1 error
    if(len(active_threads) == 0){
        errMsg := "No objects returned."
        bytes, err1 = json.Marshal(api_request{"error", &errMsg, &active_threads})
    }else {
        bytes, err1 = json.Marshal(api_request{"ok", nil, &active_threads})
    }

    if err1 != nil {
        return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `001`, true)
    }

    return bytes, nil
}

func getPostsForThread(res http.ResponseWriter, req *http.Request)  ([]byte, error) {
    if req == nil || res == nil {
        return []byte{}, xerrors.NewSysErr()
    }
    values := req.URL.Query()
    thread_id, is_passed := values[`thread_id`]
    if !is_passed {
        return []byte{},xerrors.NewUIErr(`Invalid params: No thread_id given!`, `Invalid params: No thread_id given!`, `006`, true)
    }

    dbh, err := sql.Open("postgres", dbConnString)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    api_key := values[`api_key`][0]
    rows, err := dbh.Query(`select tp.id, tp.body
			    from thread_posts tp join threads t on t.id = tp.thread_id
						 join boards b on b.id = t.board_id
						 join image_board_clusters ibc on ibc.id = b.id
			    where tp.thread_id = $1 and ibc.api_key = $2;`, thread_id[0], api_key)
    if err != nil {
        return []byte{}, xerrors.NewSysErr()
    }
    defer rows.Close()

    var curr_posts []thread_posts
    for rows.Next() {
        var curr_post thread_posts
        err = rows.Scan(&curr_post.Id, &curr_post.Body)
        if err != nil {
            return []byte{}, xerrors.NewSysErr()
        }
        curr_posts = append(curr_posts, curr_post)
    }

    var bytes []byte
    var err1 error
    if(len(curr_posts) == 0){
	errMsg := "No objects returned."
	bytes, err1 = json.Marshal(api_request{"error", &errMsg, &curr_posts})
    }else {
	bytes, err1 = json.Marshal(api_request{"ok", nil, &curr_posts})
    }

    if err1 != nil {
        return []byte{}, xerrors.NewSysErr()
    }

    return bytes, nil
}

func addPostToThread(res http.ResponseWriter, req *http.Request) ([]byte,error) {
    if req == nil || res == nil{
        return []byte{}, xerrors.NewSysErr()
    }
    values := req.URL.Query()
    thread_id, is_passed := values[`thread_id`]
    if !is_passed {
        return []byte{}, xerrors.NewUIErr(`Invalid params: No thread_id given!`, `Invalid params: No thread_id given!`, `001`, true)
    }

    thread_body_post, is_passed := values[`thread_post_body`]
    if !is_passed {
        return []byte{}, xerrors.NewUIErr(`Invalid params: No thread_post_body given!`, `Invalid params: No thread_post_body given!`, `001`, true)
    }

    attachment_urls, is_passed := values[`attachment_url`]
    var attachment_url *string
    if !is_passed{
	attachment_url = nil
    }else{
	attachment_url = &attachment_urls[0]
    }

    dbh, err := sql.Open("postgres", dbConnString)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    var is_limit_reached bool
    err = dbh.QueryRow("select count(*) +1 >= max(T.max_posts_per_thread) from thread_posts TP JOIN threads T ON T.id = TP.thread_id where thread_id = $1;", thread_id[0]).Scan(&is_limit_reached)
    if err != nil {
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    if is_limit_reached {
	return []byte{}, xerrors.NewUIErr(`Thread post limit reached!`, `Thread post limit reached!`, `002`, true)
    }

    _, err = dbh.Query("INSERT INTO thread_posts(body, thread_id, attachment_url) VALUES($1, $2, $3)", thread_body_post[0], thread_id[0], attachment_url)

    if err != nil {
	glog.Error("!!!!!!!!!!!!!!!!")
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `002`, true)
    }

    bytes, err1 := json.Marshal(api_request{"ok", nil, nil})
    if err1 != nil {
        return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `001`, true)
    }

    return bytes, nil
}

func addThread(res http.ResponseWriter, req *http.Request) ([]byte,error) {
    if req == nil || res == nil{
        return []byte{}, xerrors.NewSysErr()
    }
    values := req.URL.Query()

    dbh, err := sql.Open("postgres", dbConnString)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    thread_name, is_passed := values[`thread_name`]
    if !is_passed {
        return []byte{}, xerrors.NewUIErr(`Invalid params: No thread_name given!`, `Invalid params: No thread_name given!`, `001`, true)
    }

    board_id, is_passed := values[`board_id`]
    if !is_passed {
        return []byte{}, xerrors.NewUIErr(`Invalid params: No board_id given!`, `Invalid params: No board_id given!`, `001`, true)
    }

    var is_limit_reached bool
    err = dbh.QueryRow("select count(*) +1 >= max(B.thread_setting_max_thread_count) from threads T JOIN boards B ON B.id = T.board_id where board_id = ?", board_id).Scan(&is_limit_reached)
    if err != nil {
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    if is_limit_reached {
	return []byte{}, xerrors.NewUIErr(`Thread limit reached!`, `Thread limit reached!`, `002`, true)
    }

    _, err = dbh.Query("INSERT INTO threads(name, board_id, limits_reached_action_id) VALUES($1, $2, 1)", thread_name[0], board_id[0])

    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    bytes, err1 := json.Marshal(api_request{"ok", nil, nil})
    if err1 != nil {
        return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `001`, true)
    }

    return bytes, nil
}

var dbConnString = ``

// sample usage
func main() {
    flag.Parse()

    commands := map[string]func(http.ResponseWriter, *http.Request) ([]byte, error){
				"getBoards": getBoards,
				"getActiveThreadsForBoard": getActiveThreadsForBoard,
				"getPostsForThread": getPostsForThread,
				"addPostToThread": addPostToThread,
				"addThread": addThread,
			       }

    http.HandleFunc("/api", func(res http.ResponseWriter, req *http.Request) {
					values := req.URL.Query()
					command, is_passed := values[`command`]
					if !is_passed {
					    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'command' is undefined.","Payload":null}`))
					    return
					}

					_, is_passed = values[`api_key`]
					if !is_passed {
					    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'api_key' is undefined.","Payload":null}`))
					    return
					}

					_, is_passed = commands[command[0]]
					if !is_passed{
					    res.Write([]byte(`{"Status":"error","Msg":"No such command exists.","Payload":null}`))
					    glog.Error("command: ", command[0])
					    return
					}

					res.Header().Set("Access-Control-Allow-Origin", "*")
					bytes, err := commands[command[0]](res, req)

					if err != nil{
					    if string(reflect.TypeOf(err).Name())  == `SysErr` {
						res.Write([]byte(`{"Status":"error","Msg":"` + err.Error()  +`","Payload":null}`))
					    } else if string(reflect.TypeOf(err).Name())  == `UIErr` {
						res.Write([]byte(`{"Status":"error","Msg":"`+ err.Error() +`","Payload":null}`))
					    } else {
						res.Write([]byte(`{"Status":"error","Msg":"Application Error!","Payload":null}`))
					    }
					    glog.Error(err)
					    return
					}

					glog.Info(string(bytes))
					res.Write(bytes)
    })

    dbConnString = os.Getenv("ABC_DB_CONN_STRING") // DB will return error if empty string
    http.ListenAndServe(`:`+ os.Getenv("ABC_SERVER_ENDPOINT_URL"), nil)
}

<commit_msg>fix: bugs in sql board queries<commit_after>package main

import (
	// General
	"github.com/golang/glog"
	"flag"
	"github.com/iambc/xerrors"
	"reflect"
	"os"

	//API
	"net/http"
	"encoding/json"

	//DB
	"database/sql"
	_ "github.com/lib/pq"
)

/*
TODO:
2) Add different input/output formats for the API
3) Add settings to the boards
4) Add settings to the threads
5) Improve the error handling
6) quote of the day
*/

type image_board_clusters struct {
    Id int
    Descr string
    LongDescr string
    BoardLimitCount int
}

type boards struct {
    Id int
    Name string
    Descr string
    ImageBoardClusterId string
    MaxThreadCount int //to be checked in insert thread
    MaxActiveThreadCount int //to be checked  in insert thread
    MaxPostsPerThread int // to be checked in insert thread
    AreAttachmentsAllowed bool // to be checked in insert post
    PostLimitsReachedActionId int // to be checked in insert post
}

type threads struct{
    Id int
    Name string
    Descr string
    Board_id int
    MaxPostsPerThread int
    AreAttachmentsAllowed bool
    LimitsReachedActionId int
}

type thread_posts struct{
    Id int
    Body string
    ThreadId int
    AttachmentUrl int
}

type thread_limits_reached_actions struct{
    Id	    int
    Name    string
    Descr   string
}

type api_request struct{
    Status  string
    Msg	    *string
    Payload interface{}
}

func getBoards(res http.ResponseWriter, req *http.Request)  ([]byte, error) {
    if req == nil || res == nil {
	return []byte{}, xerrors.NewSysErr()
    }

    dbh, err := sql.Open("postgres", dbConnString)
    if err != nil {
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    values := req.URL.Query()
    api_key := values[`api_key`][0]
    rows, err := dbh.Query("select b.id, b.name, b.descr from boards b join image_board_clusters ibc on ibc.id = b.image_board_cluster_id where api_key = $1;", api_key)
    if err != nil {
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `002`, true)
    }
    defer rows.Close()

    var curr_boards []boards
    for rows.Next() {
	var board boards
	err = rows.Scan(&board.Id, &board.Name, &board.Descr)
	if err != nil {
	    return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `003`, true)
	}
	curr_boards = append(curr_boards, board)
    }
    bytes, err1 := json.Marshal(api_request{"ok", nil, &curr_boards})
    if err1 != nil {
	return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `004`, true)
    }
    return bytes, nil
}

func getActiveThreadsForBoard(res http.ResponseWriter, req *http.Request)  ([]byte, error) {
    if req == nil || res == nil {
        return []byte{}, xerrors.NewSysErr()
    }
    values := req.URL.Query()
    board_id, is_passed := values[`board_id`]
    if !is_passed {
	return []byte{}, xerrors.NewUIErr(`Invalid params: No board_id given!`, `Invalid params: No board_id given!`, `005`, true)
    }

    dbh, err := sql.Open("postgres", dbConnString)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    api_key := values[`api_key`][0]
    rows, err := dbh.Query(`select t.id, t.name from threads t
				join boards b on b.id = t.board_id
				join image_board_clusters ibc on ibc.id = b.image_board_cluster_id
			    where t.is_active = TRUE and t.board_id = $1 and ibc.api_key = $2;`, board_id[0], api_key)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }
    defer rows.Close()

    var active_threads []threads
    for rows.Next() {
	glog.Info("Popped new thread")
        var thread threads
        err = rows.Scan(&thread.Id, &thread.Name)
        if err != nil {
            return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
        }
        active_threads = append(active_threads, thread)
    }
    var bytes []byte
    var err1 error
    if(len(active_threads) == 0){
        errMsg := "No objects returned."
        bytes, err1 = json.Marshal(api_request{"error", &errMsg, &active_threads})
    }else {
        bytes, err1 = json.Marshal(api_request{"ok", nil, &active_threads})
    }

    if err1 != nil {
        return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `001`, true)
    }

    return bytes, nil
}

func getPostsForThread(res http.ResponseWriter, req *http.Request)  ([]byte, error) {
    if req == nil || res == nil {
        return []byte{}, xerrors.NewSysErr()
    }
    values := req.URL.Query()
    thread_id, is_passed := values[`thread_id`]
    if !is_passed {
        return []byte{},xerrors.NewUIErr(`Invalid params: No thread_id given!`, `Invalid params: No thread_id given!`, `006`, true)
    }

    dbh, err := sql.Open("postgres", dbConnString)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    api_key := values[`api_key`][0]
    rows, err := dbh.Query(`select tp.id, tp.body
			    from thread_posts tp join threads t on t.id = tp.thread_id
						 join boards b on b.id = t.board_id
						 join image_board_clusters ibc on ibc.id = b.id
			    where tp.thread_id = $1 and ibc.api_key = $2;`, thread_id[0], api_key)
    if err != nil {
        return []byte{}, xerrors.NewSysErr()
    }
    defer rows.Close()

    var curr_posts []thread_posts
    for rows.Next() {
        var curr_post thread_posts
        err = rows.Scan(&curr_post.Id, &curr_post.Body)
        if err != nil {
            return []byte{}, xerrors.NewSysErr()
        }
        curr_posts = append(curr_posts, curr_post)
    }

    var bytes []byte
    var err1 error
    if(len(curr_posts) == 0){
	errMsg := "No objects returned."
	bytes, err1 = json.Marshal(api_request{"error", &errMsg, &curr_posts})
    }else {
	bytes, err1 = json.Marshal(api_request{"ok", nil, &curr_posts})
    }

    if err1 != nil {
        return []byte{}, xerrors.NewSysErr()
    }

    return bytes, nil
}

func addPostToThread(res http.ResponseWriter, req *http.Request) ([]byte,error) {
    if req == nil || res == nil{
        return []byte{}, xerrors.NewSysErr()
    }
    values := req.URL.Query()
    thread_id, is_passed := values[`thread_id`]
    if !is_passed {
        return []byte{}, xerrors.NewUIErr(`Invalid params: No thread_id given!`, `Invalid params: No thread_id given!`, `001`, true)
    }

    thread_body_post, is_passed := values[`thread_post_body`]
    if !is_passed {
        return []byte{}, xerrors.NewUIErr(`Invalid params: No thread_post_body given!`, `Invalid params: No thread_post_body given!`, `001`, true)
    }

    attachment_urls, is_passed := values[`attachment_url`]
    var attachment_url *string
    if !is_passed{
	attachment_url = nil
    }else{
	attachment_url = &attachment_urls[0]
    }

    dbh, err := sql.Open("postgres", dbConnString)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    var is_limit_reached bool
    err = dbh.QueryRow("select (select count(*) from thread_posts  where thread_id = $1) > max_posts_per_thread  from threads where id = $1;", thread_id[0]).Scan(&is_limit_reached)
    if err != nil {
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    if is_limit_reached {
	return []byte{}, xerrors.NewUIErr(`Thread post limit reached!`, `Thread post limit reached!`, `002`, true)
    }

    _, err = dbh.Query("INSERT INTO thread_posts(body, thread_id, attachment_url) VALUES($1, $2, $3)", thread_body_post[0], thread_id[0], attachment_url)

    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `002`, true)
    }

    bytes, err1 := json.Marshal(api_request{"ok", nil, nil})
    if err1 != nil {
        return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `001`, true)
    }

    return bytes, nil
}

func addThread(res http.ResponseWriter, req *http.Request) ([]byte,error) {
    if req == nil || res == nil{
        return []byte{}, xerrors.NewSysErr()
    }
    values := req.URL.Query()

    dbh, err := sql.Open("postgres", dbConnString)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    thread_name, is_passed := values[`thread_name`]
    if !is_passed {
        return []byte{}, xerrors.NewUIErr(`Invalid params: No thread_name given!`, `Invalid params: No thread_name given!`, `001`, true)
    }

    board_id, is_passed := values[`board_id`]
    if !is_passed {
        return []byte{}, xerrors.NewUIErr(`Invalid params: No board_id given!`, `Invalid params: No board_id given!`, `001`, true)
    }

    var is_limit_reached bool
    err = dbh.QueryRow("select count(*) +1 >= max(B.thread_setting_max_thread_count) from threads T JOIN boards B ON B.id = T.board_id where board_id = ?", board_id).Scan(&is_limit_reached)
    if err != nil {
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    if is_limit_reached {
	return []byte{}, xerrors.NewUIErr(`Thread limit reached!`, `Thread limit reached!`, `002`, true)
    }

    _, err = dbh.Query("INSERT INTO threads(name, board_id, limits_reached_action_id) VALUES($1, $2, 1)", thread_name[0], board_id[0])

    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `001`, true)
    }

    bytes, err1 := json.Marshal(api_request{"ok", nil, nil})
    if err1 != nil {
        return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `001`, true)
    }

    return bytes, nil
}

var dbConnString = ``

// sample usage
func main() {
    flag.Parse()

    commands := map[string]func(http.ResponseWriter, *http.Request) ([]byte, error){
				"getBoards": getBoards,
				"getActiveThreadsForBoard": getActiveThreadsForBoard,
				"getPostsForThread": getPostsForThread,
				"addPostToThread": addPostToThread,
				"addThread": addThread,
			       }

    http.HandleFunc("/api", func(res http.ResponseWriter, req *http.Request) {
					values := req.URL.Query()
					command, is_passed := values[`command`]
					if !is_passed {
					    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'command' is undefined.","Payload":null}`))
					    return
					}

					_, is_passed = values[`api_key`]
					if !is_passed {
					    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'api_key' is undefined.","Payload":null}`))
					    return
					}

					_, is_passed = commands[command[0]]
					if !is_passed{
					    res.Write([]byte(`{"Status":"error","Msg":"No such command exists.","Payload":null}`))
					    glog.Error("command: ", command[0])
					    return
					}

					res.Header().Set("Access-Control-Allow-Origin", "*")
					bytes, err := commands[command[0]](res, req)

					if err != nil{
					    if string(reflect.TypeOf(err).Name())  == `SysErr` {
						res.Write([]byte(`{"Status":"error","Msg":"` + err.Error()  +`","Payload":null}`))
					    } else if string(reflect.TypeOf(err).Name())  == `UIErr` {
						res.Write([]byte(`{"Status":"error","Msg":"`+ err.Error() +`","Payload":null}`))
					    } else {
						res.Write([]byte(`{"Status":"error","Msg":"Application Error!","Payload":null}`))
					    }
					    glog.Error(err)
					    return
					}

					glog.Info(string(bytes))
					res.Write(bytes)
    })

    dbConnString = os.Getenv("ABC_DB_CONN_STRING") // DB will return error if empty string
    http.ListenAndServe(`:`+ os.Getenv("ABC_SERVER_ENDPOINT_URL"), nil)
}

<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

var server Server = NewServer()

type Server interface {
	SendDistributeRequests(req *http.Request, port, proxyServiceName string) (status int, err error)
}

type Serve struct{}

func (m *Serve) SendDistributeRequests(req *http.Request, port, proxyServiceName string) (status int, err error) {
	values := req.URL.Query()
	values.Set("distribute", "false")
	req.URL.RawQuery = values.Encode()
	dns := fmt.Sprintf("tasks.%s", proxyServiceName)
	failedDns := []string{}
	method := req.Method
	body := ""
	if req.Body != nil {
		defer func() { req.Body.Close() }()
		reqBody, _ := ioutil.ReadAll(req.Body)
		body = string(reqBody)
	}
	if ips, err := lookupHost(dns); err == nil {
		for i := 0; i < len(ips); i++ {
			req.URL.Host = fmt.Sprintf("%s:%s", ips[i], port)
			client := &http.Client{}
			addr := fmt.Sprintf("http://%s:%s%s?%s", ips[i], port, req.URL.Path, req.URL.RawQuery)
			logPrintf("Sending distribution request to %s", addr)
			req, _ := http.NewRequest(method, addr, strings.NewReader(body))
			if resp, err := client.Do(req); err != nil || resp.StatusCode >= 300 {
				failedDns = append(failedDns, ips[i])
			}
		}
	} else {
		return http.StatusBadRequest, fmt.Errorf("Could not perform DNS %s lookup", dns)
	}
	if len(failedDns) > 0 {
		return http.StatusBadRequest, fmt.Errorf("Could not send distribute request to the following addresses: %s", failedDns)
	}
	return http.StatusOK, err
}

func NewServer() *Serve {
	return &Serve{}
}
<commit_msg>Improve error message for #76-type situations.<commit_after>package server

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

var server Server = NewServer()

type Server interface {
	SendDistributeRequests(req *http.Request, port, proxyServiceName string) (status int, err error)
}

type Serve struct{}

func (m *Serve) SendDistributeRequests(req *http.Request, port, proxyServiceName string) (status int, err error) {
	values := req.URL.Query()
	values.Set("distribute", "false")
	req.URL.RawQuery = values.Encode()
	dns := fmt.Sprintf("tasks.%s", proxyServiceName)
	failedDns := []string{}
	method := req.Method
	body := ""
	if req.Body != nil {
		defer func() { req.Body.Close() }()
		reqBody, _ := ioutil.ReadAll(req.Body)
		body = string(reqBody)
	}
	if ips, err := lookupHost(dns); err == nil {
		for i := 0; i < len(ips); i++ {
			req.URL.Host = fmt.Sprintf("%s:%s", ips[i], port)
			client := &http.Client{}
			addr := fmt.Sprintf("http://%s:%s%s?%s", ips[i], port, req.URL.Path, req.URL.RawQuery)
			logPrintf("Sending distribution request to %s", addr)
			req, _ := http.NewRequest(method, addr, strings.NewReader(body))
			if resp, err := client.Do(req); err != nil || resp.StatusCode >= 300 {
				failedDns = append(failedDns, ips[i])
			}
		}
	} else {
		return http.StatusBadRequest, fmt.Errorf("Could not perform DNS %s lookup. If the proxy is not called 'proxy', you must set SERVICE_NAME=<name-of-the-proxy>.", dns)
	}
	if len(failedDns) > 0 {
		return http.StatusBadRequest, fmt.Errorf("Could not send distribute request to the following addresses: %s", failedDns)
	}
	return http.StatusOK, err
}

func NewServer() *Serve {
	return &Serve{}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	r "github.com/garyburd/redigo/redis"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"

	"github.com/vieux/gocover.io/server/redis"
)

var (
	docker_socket = flag.String("s", "", "Dockerd socket (e.g., /var/run/docker.sock)")
	docker_addr   = flag.String("d", "", "Dockerd addr (e.g., 127.0.0.1:2375)")
	serveAddr     = flag.String("p", ":8080", "Address and port to serve")
	serveSAddr    = flag.String("ps", ":80443", "Address and port to serve HTTPS")
	redisAddr     = flag.String("r", "127.0.0.1:6379", "redis address")
	redisPass     = flag.String("rp", "", "redis password")
	certPath      = flag.String("tls", "", "cert path")
)

func docker(repo, version string, pool *r.Pool) (int, string) {
	var (
		worker = "vieux/gocover"
		conn   = pool.Get()
	)

	defer conn.Close()

	if version != "" {
		worker = worker + ":" + version
	}

	if version == "" {
		if cached, fresh, err := redis.GetRepo(conn, repo); err != nil {
			return 500, err.Error()
		} else if fresh {
			return 200, string(cached)
		}
	}

	host := ""

	if *docker_socket != "" {
		host = "unix://" + *docker_socket
	} else if *docker_addr != "" {
		host = "tcp://" + *docker_addr
	} else {
		return 500, "cannot connect to docker daemon"
	}

	out, err := exec.Command("docker", "-H", host, "run", "--rm", "-a", "stdout", "-a", "stderr", worker, repo).CombinedOutput()
	if err != nil {
		if strings.Contains(string(out), "Unable to find image") {
			return 500, "go version '" + version + "' not found"
		}
		return 500, string(out)
	}
	re, err := regexp.Compile("\\")
	if err != nil {
		return 500, err.Error()
	}
	content := re.ReplaceAllString(string(out), "")
	content = strings.Replace(content, "background: black;", "background: #222222;", 2)

	content = strings.Replace(content, ".cov1 { color: rgb(128, 128, 128) }", ".cov1 { color: #52987D }", 2)
	content = strings.Replace(content, ".cov2 { color: rgb(128, 128, 128) }", ".cov2 { color: #4BA180 }", 2)
	content = strings.Replace(content, ".cov3 { color: rgb(128, 128, 128) }", ".cov3 { color: #44AA83 }", 2)
	content = strings.Replace(content, ".cov4 { color: rgb(128, 128, 128) }", ".cov4 { color: #3DB487 }", 2)
	content = strings.Replace(content, ".cov5 { color: rgb(128, 128, 128) }", ".cov5 { color: #36BD8A }", 2)
	content = strings.Replace(content, ".cov6 { color: rgb(128, 128, 128) }", ".cov6 { color: #2FC68D }", 2)
	content = strings.Replace(content, ".cov7 { color: rgb(128, 128, 128) }", ".cov7 { color: #28D091 }", 2)
	content = strings.Replace(content, ".cov8 { color: rgb(128, 128, 128) }", ".cov8 { color: #21D994 }", 2)
	content = strings.Replace(content, ".cov9 { color: rgb(128, 128, 128) }", ".cov9 { color: #1AE297 }", 2)
	content = strings.Replace(content, "<option value=\"file0\">", "<option value=\"file0\" select=\"selected\">", -1)
	content = strings.Replace(content, "\">"+repo, "\">", -1)

	re = regexp.MustCompile("-- cov:([0-9.]*) --")
	matches := re.FindStringSubmatch(content)
	if len(matches) == 2 {
		cov, err := strconv.ParseFloat(matches[1], 64)
		if err == nil {
			content = strings.Replace(content, "<select id=", fmt.Sprintf("%s%% | <select id=", int((cov-0.0001)/10), matches[1]), 1)
		}
		if version != "" {
			content = strings.Replace(content, "<select id=", fmt.Sprintf("%s | <select id=", version), 1)
		} else {
			redis.SetCache(conn, repo, content, matches[1])
		}
	} else if version != "" {
		content = strings.Replace(content, "<select id=", fmt.Sprintf("%s | <select id=", version), 1)
	} else {
		redis.SetCache(conn, repo, content, "-1")
	}
	if version == "" {
		redis.SetStats(conn, repo)
	}
	return 200, content
}

func main() {
	flag.Parse()

	var (
		m         = martini.Classic()
		pool, err = redis.NewPool("tcp", *redisAddr, *redisPass)
	)

	if err != nil {
		log.Fatalf("%v", err)
	}

	m.Use(martini.Static("static"))
	m.Use(render.Renderer(render.Options{Layout: "layout"}))
	m.Get("/about", func(r render.Render) {
		r.HTML(200, "about", map[string]interface{}{"about_active": "active"})
	})
	m.Get("/", func(r render.Render) {
		conn := pool.Get()
		defer conn.Close()

		top, err := redis.Top(conn, "top", 4)
		if err != nil {
			log.Println(err.Error())
		}
		last, err := redis.Top(conn, "last", 4)
		if err != nil {
			log.Println(err.Error())
		}
		r.HTML(200, "cover", map[string]interface{}{"top": top, "last": last, "cover_active": "active"})
	})
	m.Post("/_webhook", func(req *http.Request) (int, string) {

		github := struct {
			Repository struct {
				Full_Name string
			}
		}{}

		if err := json.NewDecoder(req.Body).Decode(&github); err != nil {
			return 500, err.Error()
		}

		go docker(github.Repository.Full_Name, "", pool)

		return 202, github.Repository.Full_Name
	})
	m.Get("/_badge/**", func(params martini.Params, r render.Render) {
		var (
			repo = params["_1"]
			conn = pool.Get()
		)

		defer conn.Close()
		if coverage, err := redis.GetCoverage(conn, repo); err != nil {
			r.Redirect(fmt.Sprintf("https://img.shields.io/badge/coverage-error-lightgrey.svg?style=flat"))
		} else if coverage < 25.0 {
			r.Redirect(fmt.Sprintf("https://img.shields.io/badge/coverage-%.1f%%-red.svg?style=flat", coverage))
		} else if coverage < 50.0 {
			r.Redirect(fmt.Sprintf("https://img.shields.io/badge/coverage-%.1f%%-orange.svg?style=flat", coverage))
		} else if coverage < 75.0 {
			r.Redirect(fmt.Sprintf("https://img.shields.io/badge/coverage-%.1f%%-green.svg?style=flat", coverage))
		} else {
			r.Redirect(fmt.Sprintf("https://img.shields.io/badge/coverage-%.1f%%-brightgreen.svg?style=flat", coverage))
		}

	})
	m.Get("/_cache/**", func(req *http.Request, params martini.Params) (int, string) {
		var (
			repo = params["_1"]
			conn = pool.Get()
		)
		defer conn.Close()

		if req.FormValue("version") == "" {
			if cached, _, err := redis.GetRepo(conn, repo); err != nil {
				return 500, err.Error()
			} else if cached != "" {
				redis.SetStats(conn, repo)
				return 200, string(cached)
			}
		}
		return 404, "No cached version of " + repo
	})
	m.Get("/_/**", func(req *http.Request, params martini.Params) (int, string) {
		return docker(params["_1"], req.FormValue("version"), pool)
	})
	m.Get("/**", func(req *http.Request, params martini.Params, r render.Render) {
		var (
			repo    = params["_1"]
			conn    = pool.Get()
			version = ""
		)
		defer conn.Close()

		if req.ParseForm() == nil {
			version = req.FormValue("version")
		}

		if cached, fresh, err := redis.GetRepo(conn, repo+version); err != nil {
			r.HTML(500, "", map[string]interface{}{"cover_active": "active", "error": err})
		} else if fresh {
			redis.SetStats(conn, repo)
			r.HTML(200, "cached", map[string]interface{}{"repo": repo, "cover_active": "active", "cache": template.HTML(cached), "version": version})
		} else {
			contexts := map[string]interface{}{"repo": repo, "cover_active": "active", "version": version}
			if cached != "" {
				contexts["cache"] = "ok"
			}
			r.HTML(200, "loading", contexts)
		}
	})
	if *certPath != "" {
		go func() {
			log.Println(http.ListenAndServe(*serveAddr, http.HandlerFunc(redir)))
		}()
		log.Fatal(http.ListenAndServeTLS(*serveSAddr, filepath.Join(*certPath, "fullchain.pem"), filepath.Join(*certPath, "privkey.pem"), m))
	} else {
		log.Fatal(http.ListenAndServe(*serveAddr, m))
	}
}

func redir(w http.ResponseWriter, req *http.Request) {
	http.Redirect(w, req, "https://gocover.io"+req.RequestURI, http.StatusMovedPermanently)
}
<commit_msg>Ensure badge redirects are correctly escaped<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	r "github.com/garyburd/redigo/redis"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"

	"github.com/vieux/gocover.io/server/redis"
)

var (
	docker_socket = flag.String("s", "", "Dockerd socket (e.g., /var/run/docker.sock)")
	docker_addr   = flag.String("d", "", "Dockerd addr (e.g., 127.0.0.1:2375)")
	serveAddr     = flag.String("p", ":8080", "Address and port to serve")
	serveSAddr    = flag.String("ps", ":80443", "Address and port to serve HTTPS")
	redisAddr     = flag.String("r", "127.0.0.1:6379", "redis address")
	redisPass     = flag.String("rp", "", "redis password")
	certPath      = flag.String("tls", "", "cert path")
)

func docker(repo, version string, pool *r.Pool) (int, string) {
	var (
		worker = "vieux/gocover"
		conn   = pool.Get()
	)

	defer conn.Close()

	if version != "" {
		worker = worker + ":" + version
	}

	if version == "" {
		if cached, fresh, err := redis.GetRepo(conn, repo); err != nil {
			return 500, err.Error()
		} else if fresh {
			return 200, string(cached)
		}
	}

	host := ""

	if *docker_socket != "" {
		host = "unix://" + *docker_socket
	} else if *docker_addr != "" {
		host = "tcp://" + *docker_addr
	} else {
		return 500, "cannot connect to docker daemon"
	}

	out, err := exec.Command("docker", "-H", host, "run", "--rm", "-a", "stdout", "-a", "stderr", worker, repo).CombinedOutput()
	if err != nil {
		if strings.Contains(string(out), "Unable to find image") {
			return 500, "go version '" + version + "' not found"
		}
		return 500, string(out)
	}
	re, err := regexp.Compile("\\")
	if err != nil {
		return 500, err.Error()
	}
	content := re.ReplaceAllString(string(out), "")
	content = strings.Replace(content, "background: black;", "background: #222222;", 2)

	content = strings.Replace(content, ".cov1 { color: rgb(128, 128, 128) }", ".cov1 { color: #52987D }", 2)
	content = strings.Replace(content, ".cov2 { color: rgb(128, 128, 128) }", ".cov2 { color: #4BA180 }", 2)
	content = strings.Replace(content, ".cov3 { color: rgb(128, 128, 128) }", ".cov3 { color: #44AA83 }", 2)
	content = strings.Replace(content, ".cov4 { color: rgb(128, 128, 128) }", ".cov4 { color: #3DB487 }", 2)
	content = strings.Replace(content, ".cov5 { color: rgb(128, 128, 128) }", ".cov5 { color: #36BD8A }", 2)
	content = strings.Replace(content, ".cov6 { color: rgb(128, 128, 128) }", ".cov6 { color: #2FC68D }", 2)
	content = strings.Replace(content, ".cov7 { color: rgb(128, 128, 128) }", ".cov7 { color: #28D091 }", 2)
	content = strings.Replace(content, ".cov8 { color: rgb(128, 128, 128) }", ".cov8 { color: #21D994 }", 2)
	content = strings.Replace(content, ".cov9 { color: rgb(128, 128, 128) }", ".cov9 { color: #1AE297 }", 2)
	content = strings.Replace(content, "<option value=\"file0\">", "<option value=\"file0\" select=\"selected\">", -1)
	content = strings.Replace(content, "\">"+repo, "\">", -1)

	re = regexp.MustCompile("-- cov:([0-9.]*) --")
	matches := re.FindStringSubmatch(content)
	if len(matches) == 2 {
		cov, err := strconv.ParseFloat(matches[1], 64)
		if err == nil {
			content = strings.Replace(content, "<select id=", fmt.Sprintf("%s%% | <select id=", int((cov-0.0001)/10), matches[1]), 1)
		}
		if version != "" {
			content = strings.Replace(content, "<select id=", fmt.Sprintf("%s | <select id=", version), 1)
		} else {
			redis.SetCache(conn, repo, content, matches[1])
		}
	} else if version != "" {
		content = strings.Replace(content, "<select id=", fmt.Sprintf("%s | <select id=", version), 1)
	} else {
		redis.SetCache(conn, repo, content, "-1")
	}
	if version == "" {
		redis.SetStats(conn, repo)
	}
	return 200, content
}

func main() {
	flag.Parse()

	var (
		m         = martini.Classic()
		pool, err = redis.NewPool("tcp", *redisAddr, *redisPass)
	)

	if err != nil {
		log.Fatalf("%v", err)
	}

	m.Use(martini.Static("static"))
	m.Use(render.Renderer(render.Options{Layout: "layout"}))
	m.Get("/about", func(r render.Render) {
		r.HTML(200, "about", map[string]interface{}{"about_active": "active"})
	})
	m.Get("/", func(r render.Render) {
		conn := pool.Get()
		defer conn.Close()

		top, err := redis.Top(conn, "top", 4)
		if err != nil {
			log.Println(err.Error())
		}
		last, err := redis.Top(conn, "last", 4)
		if err != nil {
			log.Println(err.Error())
		}
		r.HTML(200, "cover", map[string]interface{}{"top": top, "last": last, "cover_active": "active"})
	})
	m.Post("/_webhook", func(req *http.Request) (int, string) {

		github := struct {
			Repository struct {
				Full_Name string
			}
		}{}

		if err := json.NewDecoder(req.Body).Decode(&github); err != nil {
			return 500, err.Error()
		}

		go docker(github.Repository.Full_Name, "", pool)

		return 202, github.Repository.Full_Name
	})
	m.Get("/_badge/**", func(params martini.Params, r render.Render) {
		var (
			repo = params["_1"]
			conn = pool.Get()
		)

		defer conn.Close()
		if coverage, err := redis.GetCoverage(conn, repo); err != nil {
			r.Redirect(fmt.Sprintf("https://img.shields.io/badge/coverage-error-lightgrey.svg?style=flat"))
		} else if coverage < 25.0 {
			r.Redirect(fmt.Sprintf("https://img.shields.io/badge/coverage-%.1f%%25-red.svg?style=flat", coverage))
		} else if coverage < 50.0 {
			r.Redirect(fmt.Sprintf("https://img.shields.io/badge/coverage-%.1f%%25-orange.svg?style=flat", coverage))
		} else if coverage < 75.0 {
			r.Redirect(fmt.Sprintf("https://img.shields.io/badge/coverage-%.1f%%25-green.svg?style=flat", coverage))
		} else {
			r.Redirect(fmt.Sprintf("https://img.shields.io/badge/coverage-%.1f%%25-brightgreen.svg?style=flat", coverage))
		}

	})
	m.Get("/_cache/**", func(req *http.Request, params martini.Params) (int, string) {
		var (
			repo = params["_1"]
			conn = pool.Get()
		)
		defer conn.Close()

		if req.FormValue("version") == "" {
			if cached, _, err := redis.GetRepo(conn, repo); err != nil {
				return 500, err.Error()
			} else if cached != "" {
				redis.SetStats(conn, repo)
				return 200, string(cached)
			}
		}
		return 404, "No cached version of " + repo
	})
	m.Get("/_/**", func(req *http.Request, params martini.Params) (int, string) {
		return docker(params["_1"], req.FormValue("version"), pool)
	})
	m.Get("/**", func(req *http.Request, params martini.Params, r render.Render) {
		var (
			repo    = params["_1"]
			conn    = pool.Get()
			version = ""
		)
		defer conn.Close()

		if req.ParseForm() == nil {
			version = req.FormValue("version")
		}

		if cached, fresh, err := redis.GetRepo(conn, repo+version); err != nil {
			r.HTML(500, "", map[string]interface{}{"cover_active": "active", "error": err})
		} else if fresh {
			redis.SetStats(conn, repo)
			r.HTML(200, "cached", map[string]interface{}{"repo": repo, "cover_active": "active", "cache": template.HTML(cached), "version": version})
		} else {
			contexts := map[string]interface{}{"repo": repo, "cover_active": "active", "version": version}
			if cached != "" {
				contexts["cache"] = "ok"
			}
			r.HTML(200, "loading", contexts)
		}
	})
	if *certPath != "" {
		go func() {
			log.Println(http.ListenAndServe(*serveAddr, http.HandlerFunc(redir)))
		}()
		log.Fatal(http.ListenAndServeTLS(*serveSAddr, filepath.Join(*certPath, "fullchain.pem"), filepath.Join(*certPath, "privkey.pem"), m))
	} else {
		log.Fatal(http.ListenAndServe(*serveAddr, m))
	}
}

func redir(w http.ResponseWriter, req *http.Request) {
	http.Redirect(w, req, "https://gocover.io"+req.RequestURI, http.StatusMovedPermanently)
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rsa

// This file implements the PSS signature scheme [1].
//
// [1] https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf

import (
	"bytes"
	"crypto"
	"errors"
	"hash"
	"io"
	"math/big"
)

func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) {
	// See [1], section 9.1.1
	hLen := hash.Size()
	sLen := len(salt)
	emLen := (emBits + 7) / 8

	// 1.  If the length of M is greater than the input limitation for the
	//     hash function (2^61 - 1 octets for SHA-1), output "message too
	//     long" and stop.
	//
	// 2.  Let mHash = Hash(M), an octet string of length hLen.

	if len(mHash) != hLen {
		return nil, errors.New("crypto/rsa: input must be hashed message")
	}

	// 3.  If emLen < hLen + sLen + 2, output "encoding error" and stop.

	if emLen < hLen+sLen+2 {
		return nil, errors.New("crypto/rsa: encoding error")
	}

	em := make([]byte, emLen)
	db := em[:emLen-sLen-hLen-2+1+sLen]
	h := em[emLen-sLen-hLen-2+1+sLen : emLen-1]

	// 4.  Generate a random octet string salt of length sLen; if sLen = 0,
	//     then salt is the empty string.
	//
	// 5.  Let
	//       M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt;
	//
	//     M' is an octet string of length 8 + hLen + sLen with eight
	//     initial zero octets.
	//
	// 6.  Let H = Hash(M'), an octet string of length hLen.

	var prefix [8]byte

	hash.Write(prefix[:])
	hash.Write(mHash)
	hash.Write(salt)

	h = hash.Sum(h[:0])
	hash.Reset()

	// 7.  Generate an octet string PS consisting of emLen - sLen - hLen - 2
	//     zero octets. The length of PS may be 0.
	//
	// 8.  Let DB = PS || 0x01 || salt; DB is an octet string of length
	//     emLen - hLen - 1.

	db[emLen-sLen-hLen-2] = 0x01
	copy(db[emLen-sLen-hLen-1:], salt)

	// 9.  Let dbMask = MGF(H, emLen - hLen - 1).
	//
	// 10. Let maskedDB = DB \xor dbMask.

	mgf1XOR(db, hash, h)

	// 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in
	//     maskedDB to zero.

	db[0] &= (0xFF >> uint(8*emLen-emBits))

	// 12. Let EM = maskedDB || H || 0xbc.
	em[emLen-1] = 0xBC

	// 13. Output EM.
	return em, nil
}

func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error {
	// 1.  If the length of M is greater than the input limitation for the
	//     hash function (2^61 - 1 octets for SHA-1), output "inconsistent"
	//     and stop.
	//
	// 2.  Let mHash = Hash(M), an octet string of length hLen.
	hLen := hash.Size()
	if hLen != len(mHash) {
		return ErrVerification
	}

	// 3.  If emLen < hLen + sLen + 2, output "inconsistent" and stop.
	emLen := (emBits + 7) / 8
	if emLen < hLen+sLen+2 {
		return ErrVerification
	}

	// 4.  If the rightmost octet of EM does not have hexadecimal value
	//     0xbc, output "inconsistent" and stop.
	if em[len(em)-1] != 0xBC {
		return ErrVerification
	}

	// 5.  Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
	//     let H be the next hLen octets.
	db := em[:emLen-hLen-1]
	h := em[emLen-hLen-1 : len(em)-1]

	// 6.  If the leftmost 8 * emLen - emBits bits of the leftmost octet in
	//     maskedDB are not all equal to zero, output "inconsistent" and
	//     stop.
	if em[0]&(0xFF<<uint(8-(8*emLen-emBits))) != 0 {
		return ErrVerification
	}

	// 7.  Let dbMask = MGF(H, emLen - hLen - 1).
	//
	// 8.  Let DB = maskedDB \xor dbMask.
	mgf1XOR(db, hash, h)

	// 9.  Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB
	//     to zero.
	db[0] &= (0xFF >> uint(8*emLen-emBits))

	if sLen == PSSSaltLengthAuto {
	FindSaltLength:
		for sLen = emLen - (hLen + 2); sLen >= 0; sLen-- {
			switch db[emLen-hLen-sLen-2] {
			case 1:
				break FindSaltLength
			case 0:
				continue
			default:
				return ErrVerification
			}
		}
		if sLen < 0 {
			return ErrVerification
		}
	} else {
		// 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
		//     or if the octet at position emLen - hLen - sLen - 1 (the leftmost
		//     position is "position 1") does not have hexadecimal value 0x01,
		//     output "inconsistent" and stop.
		for _, e := range db[:emLen-hLen-sLen-2] {
			if e != 0x00 {
				return ErrVerification
			}
		}
		if db[emLen-hLen-sLen-2] != 0x01 {
			return ErrVerification
		}
	}

	// 11.  Let salt be the last sLen octets of DB.
	salt := db[len(db)-sLen:]

	// 12.  Let
	//          M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
	//     M' is an octet string of length 8 + hLen + sLen with eight
	//     initial zero octets.
	//
	// 13. Let H' = Hash(M'), an octet string of length hLen.
	var prefix [8]byte
	hash.Write(prefix[:])
	hash.Write(mHash)
	hash.Write(salt)

	h0 := hash.Sum(nil)

	// 14. If H = H', output "consistent." Otherwise, output "inconsistent."
	if !bytes.Equal(h0, h) {
		return ErrVerification
	}
	return nil
}

// signPSSWithSalt calculates the signature of hashed using PSS [1] with specified salt.
// Note that hashed must be the result of hashing the input message using the
// given hash function. salt is a random sequence of bytes whose length will be
// later used to verify the signature.
func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) (s []byte, err error) {
	nBits := priv.N.BitLen()
	em, err := emsaPSSEncode(hashed, nBits-1, salt, hash.New())
	if err != nil {
		return
	}
	m := new(big.Int).SetBytes(em)
	c, err := decryptAndCheck(rand, priv, m)
	if err != nil {
		return
	}
	s = make([]byte, (nBits+7)/8)
	copyWithLeftPad(s, c.Bytes())
	return
}

const (
	// PSSSaltLengthAuto causes the salt in a PSS signature to be as large
	// as possible when signing, and to be auto-detected when verifying.
	PSSSaltLengthAuto = 0
	// PSSSaltLengthEqualsHash causes the salt length to equal the length
	// of the hash used in the signature.
	PSSSaltLengthEqualsHash = -1
)

// PSSOptions contains options for creating and verifying PSS signatures.
type PSSOptions struct {
	// SaltLength controls the length of the salt used in the PSS
	// signature. It can either be a number of bytes, or one of the special
	// PSSSaltLength constants.
	SaltLength int

	// Hash, if not zero, overrides the hash function passed to SignPSS.
	// This is the only way to specify the hash function when using the
	// crypto.Signer interface.
	Hash crypto.Hash
}

// HashFunc returns pssOpts.Hash so that PSSOptions implements
// crypto.SignerOpts.
func (pssOpts *PSSOptions) HashFunc() crypto.Hash {
	return pssOpts.Hash
}

func (opts *PSSOptions) saltLength() int {
	if opts == nil {
		return PSSSaltLengthAuto
	}
	return opts.SaltLength
}

// SignPSS calculates the signature of hashed using RSASSA-PSS [1].
// Note that hashed must be the result of hashing the input message using the
// given hash function. The opts argument may be nil, in which case sensible
// defaults are used.
func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, opts *PSSOptions) ([]byte, error) {
	saltLength := opts.saltLength()
	switch saltLength {
	case PSSSaltLengthAuto:
		saltLength = (priv.N.BitLen()+7)/8 - 2 - hash.Size()
	case PSSSaltLengthEqualsHash:
		saltLength = hash.Size()
	}

	if opts != nil && opts.Hash != 0 {
		hash = opts.Hash
	}

	salt := make([]byte, saltLength)
	if _, err := io.ReadFull(rand, salt); err != nil {
		return nil, err
	}
	return signPSSWithSalt(rand, priv, hash, hashed, salt)
}

// VerifyPSS verifies a PSS signature.
// hashed is the result of hashing the input message using the given hash
// function and sig is the signature. A valid signature is indicated by
// returning a nil error. The opts argument may be nil, in which case sensible
// defaults are used.
func VerifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, opts *PSSOptions) error {
	return verifyPSS(pub, hash, hashed, sig, opts.saltLength())
}

// verifyPSS verifies a PSS signature with the given salt length.
func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error {
	nBits := pub.N.BitLen()
	if len(sig) != (nBits+7)/8 {
		return ErrVerification
	}
	s := new(big.Int).SetBytes(sig)
	m := encrypt(new(big.Int), pub, s)
	emBits := nBits - 1
	emLen := (emBits + 7) / 8
	if emLen < len(m.Bytes()) {
		return ErrVerification
	}
	em := make([]byte, emLen)
	copyWithLeftPad(em, m.Bytes())
	if saltLen == PSSSaltLengthEqualsHash {
		saltLen = hash.Size()
	}
	return emsaPSSVerify(hashed, em, emBits, saltLen, hash.New())
}
<commit_msg>crypto/rsa: improve error message for keys too short for PSS<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rsa

// This file implements the PSS signature scheme [1].
//
// [1] https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf

import (
	"bytes"
	"crypto"
	"errors"
	"hash"
	"io"
	"math/big"
)

func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) {
	// See [1], section 9.1.1
	hLen := hash.Size()
	sLen := len(salt)
	emLen := (emBits + 7) / 8

	// 1.  If the length of M is greater than the input limitation for the
	//     hash function (2^61 - 1 octets for SHA-1), output "message too
	//     long" and stop.
	//
	// 2.  Let mHash = Hash(M), an octet string of length hLen.

	if len(mHash) != hLen {
		return nil, errors.New("crypto/rsa: input must be hashed message")
	}

	// 3.  If emLen < hLen + sLen + 2, output "encoding error" and stop.

	if emLen < hLen+sLen+2 {
		return nil, errors.New("crypto/rsa: key size too small for PSS signature")
	}

	em := make([]byte, emLen)
	db := em[:emLen-sLen-hLen-2+1+sLen]
	h := em[emLen-sLen-hLen-2+1+sLen : emLen-1]

	// 4.  Generate a random octet string salt of length sLen; if sLen = 0,
	//     then salt is the empty string.
	//
	// 5.  Let
	//       M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt;
	//
	//     M' is an octet string of length 8 + hLen + sLen with eight
	//     initial zero octets.
	//
	// 6.  Let H = Hash(M'), an octet string of length hLen.

	var prefix [8]byte

	hash.Write(prefix[:])
	hash.Write(mHash)
	hash.Write(salt)

	h = hash.Sum(h[:0])
	hash.Reset()

	// 7.  Generate an octet string PS consisting of emLen - sLen - hLen - 2
	//     zero octets. The length of PS may be 0.
	//
	// 8.  Let DB = PS || 0x01 || salt; DB is an octet string of length
	//     emLen - hLen - 1.

	db[emLen-sLen-hLen-2] = 0x01
	copy(db[emLen-sLen-hLen-1:], salt)

	// 9.  Let dbMask = MGF(H, emLen - hLen - 1).
	//
	// 10. Let maskedDB = DB \xor dbMask.

	mgf1XOR(db, hash, h)

	// 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in
	//     maskedDB to zero.

	db[0] &= (0xFF >> uint(8*emLen-emBits))

	// 12. Let EM = maskedDB || H || 0xbc.
	em[emLen-1] = 0xBC

	// 13. Output EM.
	return em, nil
}

func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error {
	// 1.  If the length of M is greater than the input limitation for the
	//     hash function (2^61 - 1 octets for SHA-1), output "inconsistent"
	//     and stop.
	//
	// 2.  Let mHash = Hash(M), an octet string of length hLen.
	hLen := hash.Size()
	if hLen != len(mHash) {
		return ErrVerification
	}

	// 3.  If emLen < hLen + sLen + 2, output "inconsistent" and stop.
	emLen := (emBits + 7) / 8
	if emLen < hLen+sLen+2 {
		return ErrVerification
	}

	// 4.  If the rightmost octet of EM does not have hexadecimal value
	//     0xbc, output "inconsistent" and stop.
	if em[len(em)-1] != 0xBC {
		return ErrVerification
	}

	// 5.  Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
	//     let H be the next hLen octets.
	db := em[:emLen-hLen-1]
	h := em[emLen-hLen-1 : len(em)-1]

	// 6.  If the leftmost 8 * emLen - emBits bits of the leftmost octet in
	//     maskedDB are not all equal to zero, output "inconsistent" and
	//     stop.
	if em[0]&(0xFF<<uint(8-(8*emLen-emBits))) != 0 {
		return ErrVerification
	}

	// 7.  Let dbMask = MGF(H, emLen - hLen - 1).
	//
	// 8.  Let DB = maskedDB \xor dbMask.
	mgf1XOR(db, hash, h)

	// 9.  Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB
	//     to zero.
	db[0] &= (0xFF >> uint(8*emLen-emBits))

	if sLen == PSSSaltLengthAuto {
	FindSaltLength:
		for sLen = emLen - (hLen + 2); sLen >= 0; sLen-- {
			switch db[emLen-hLen-sLen-2] {
			case 1:
				break FindSaltLength
			case 0:
				continue
			default:
				return ErrVerification
			}
		}
		if sLen < 0 {
			return ErrVerification
		}
	} else {
		// 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
		//     or if the octet at position emLen - hLen - sLen - 1 (the leftmost
		//     position is "position 1") does not have hexadecimal value 0x01,
		//     output "inconsistent" and stop.
		for _, e := range db[:emLen-hLen-sLen-2] {
			if e != 0x00 {
				return ErrVerification
			}
		}
		if db[emLen-hLen-sLen-2] != 0x01 {
			return ErrVerification
		}
	}

	// 11.  Let salt be the last sLen octets of DB.
	salt := db[len(db)-sLen:]

	// 12.  Let
	//          M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
	//     M' is an octet string of length 8 + hLen + sLen with eight
	//     initial zero octets.
	//
	// 13. Let H' = Hash(M'), an octet string of length hLen.
	var prefix [8]byte
	hash.Write(prefix[:])
	hash.Write(mHash)
	hash.Write(salt)

	h0 := hash.Sum(nil)

	// 14. If H = H', output "consistent." Otherwise, output "inconsistent."
	if !bytes.Equal(h0, h) {
		return ErrVerification
	}
	return nil
}

// signPSSWithSalt calculates the signature of hashed using PSS [1] with specified salt.
// Note that hashed must be the result of hashing the input message using the
// given hash function. salt is a random sequence of bytes whose length will be
// later used to verify the signature.
func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) (s []byte, err error) {
	nBits := priv.N.BitLen()
	em, err := emsaPSSEncode(hashed, nBits-1, salt, hash.New())
	if err != nil {
		return
	}
	m := new(big.Int).SetBytes(em)
	c, err := decryptAndCheck(rand, priv, m)
	if err != nil {
		return
	}
	s = make([]byte, (nBits+7)/8)
	copyWithLeftPad(s, c.Bytes())
	return
}

const (
	// PSSSaltLengthAuto causes the salt in a PSS signature to be as large
	// as possible when signing, and to be auto-detected when verifying.
	PSSSaltLengthAuto = 0
	// PSSSaltLengthEqualsHash causes the salt length to equal the length
	// of the hash used in the signature.
	PSSSaltLengthEqualsHash = -1
)

// PSSOptions contains options for creating and verifying PSS signatures.
type PSSOptions struct {
	// SaltLength controls the length of the salt used in the PSS
	// signature. It can either be a number of bytes, or one of the special
	// PSSSaltLength constants.
	SaltLength int

	// Hash, if not zero, overrides the hash function passed to SignPSS.
	// This is the only way to specify the hash function when using the
	// crypto.Signer interface.
	Hash crypto.Hash
}

// HashFunc returns pssOpts.Hash so that PSSOptions implements
// crypto.SignerOpts.
func (pssOpts *PSSOptions) HashFunc() crypto.Hash {
	return pssOpts.Hash
}

func (opts *PSSOptions) saltLength() int {
	if opts == nil {
		return PSSSaltLengthAuto
	}
	return opts.SaltLength
}

// SignPSS calculates the signature of hashed using RSASSA-PSS [1].
// Note that hashed must be the result of hashing the input message using the
// given hash function. The opts argument may be nil, in which case sensible
// defaults are used.
func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, opts *PSSOptions) ([]byte, error) {
	saltLength := opts.saltLength()
	switch saltLength {
	case PSSSaltLengthAuto:
		saltLength = (priv.N.BitLen()+7)/8 - 2 - hash.Size()
	case PSSSaltLengthEqualsHash:
		saltLength = hash.Size()
	}

	if opts != nil && opts.Hash != 0 {
		hash = opts.Hash
	}

	salt := make([]byte, saltLength)
	if _, err := io.ReadFull(rand, salt); err != nil {
		return nil, err
	}
	return signPSSWithSalt(rand, priv, hash, hashed, salt)
}

// VerifyPSS verifies a PSS signature.
// hashed is the result of hashing the input message using the given hash
// function and sig is the signature. A valid signature is indicated by
// returning a nil error. The opts argument may be nil, in which case sensible
// defaults are used.
func VerifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, opts *PSSOptions) error {
	return verifyPSS(pub, hash, hashed, sig, opts.saltLength())
}

// verifyPSS verifies a PSS signature with the given salt length.
func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error {
	nBits := pub.N.BitLen()
	if len(sig) != (nBits+7)/8 {
		return ErrVerification
	}
	s := new(big.Int).SetBytes(sig)
	m := encrypt(new(big.Int), pub, s)
	emBits := nBits - 1
	emLen := (emBits + 7) / 8
	if emLen < len(m.Bytes()) {
		return ErrVerification
	}
	em := make([]byte, emLen)
	copyWithLeftPad(em, m.Bytes())
	if saltLen == PSSSaltLengthEqualsHash {
		saltLen = hash.Size()
	}
	return emsaPSSVerify(hashed, em, emBits, saltLen, hash.New())
}
<|endoftext|>


<commit_before>package server

import (
	"context"

	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/noop"
	"github.com/influxdata/chronograf/organizations"
	"github.com/influxdata/chronograf/roles"
)

// hasOrganizationContext retrieves organization specified on context
// under the organizations.ContextKey
func hasOrganizationContext(ctx context.Context) (string, bool) {
	// prevents panic in case of nil context
	if ctx == nil {
		return "", false
	}
	orgID, ok := ctx.Value(organizations.ContextKey).(string)
	// should never happen
	if !ok {
		return "", false
	}
	if orgID == "" {
		return "", false
	}
	return orgID, true
}

// hasRoleContext retrieves organization specified on context
// under the organizations.ContextKey
func hasRoleContext(ctx context.Context) (string, bool) {
	// prevents panic in case of nil context
	if ctx == nil {
		return "", false
	}
	role, ok := ctx.Value(roles.ContextKey).(string)
	// should never happen
	if !ok {
		return "", false
	}
	switch role {
	case roles.MemberRoleName, roles.ViewerRoleName, roles.EditorRoleName, roles.AdminRoleName:
		return role, true
	default:
		return "", false
	}
}

type userContextKey string

// UserContextKey is the context key for retrieving the user off of context
const UserContextKey = userContextKey("user")

// hasUserContext speficies if the context contains
// the UserContextKey and that the value stored there is chronograf.User
func hasUserContext(ctx context.Context) (*chronograf.User, bool) {
	// prevents panic in case of nil context
	if ctx == nil {
		return nil, false
	}
	u, ok := ctx.Value(UserContextKey).(*chronograf.User)
	// should never happen
	if !ok {
		return nil, false
	}
	if u == nil {
		return nil, false
	}
	return u, true
}

// hasSuperAdminContext speficies if the context contains
// the UserContextKey user is a super admin
func hasSuperAdminContext(ctx context.Context) bool {
	u, ok := hasUserContext(ctx)
	if !ok {
		return false
	}
	return u.SuperAdmin
}

// DataStore is collection of resources that are used by the Service
// Abstracting this into an interface was useful for isolated testing
type DataStore interface {
	Sources(ctx context.Context) chronograf.SourcesStore
	Servers(ctx context.Context) chronograf.ServersStore
	Layouts(ctx context.Context) chronograf.LayoutsStore
	Users(ctx context.Context) chronograf.UsersStore
	Organizations(ctx context.Context) chronograf.OrganizationsStore
	Mappings(ctx context.Context) chronograf.MappingsStore
	Dashboards(ctx context.Context) chronograf.DashboardsStore
	Config(ctx context.Context) chronograf.ConfigStore
	OrganizationConfig(ctx context.Context) chronograf.OrganizationConfigStore
}

// ensure that Store implements a DataStore
var _ DataStore = &Store{}

// Store implements the DataStore interface
type Store struct {
	SourcesStore            chronograf.SourcesStore
	ServersStore            chronograf.ServersStore
	LayoutsStore            chronograf.LayoutsStore
	UsersStore              chronograf.UsersStore
	DashboardsStore         chronograf.DashboardsStore
	MappingsStore           chronograf.MappingsStore
	OrganizationsStore      chronograf.OrganizationsStore
	ConfigStore             chronograf.ConfigStore
	OrganizationConfigStore chronograf.OrganizationConfigStore
}

// Sources returns a noop.SourcesStore if the context has no organization specified
// and an organization.SourcesStore otherwise.
func (s *Store) Sources(ctx context.Context) chronograf.SourcesStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.SourcesStore
	}
	if org, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewSourcesStore(s.SourcesStore, org)
	}

	return &noop.SourcesStore{}
}

// Servers returns a noop.ServersStore if the context has no organization specified
// and an organization.ServersStore otherwise.
func (s *Store) Servers(ctx context.Context) chronograf.ServersStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.ServersStore
	}
	if org, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewServersStore(s.ServersStore, org)
	}

	return &noop.ServersStore{}
}

// Layouts returns all layouts in the underlying layouts store.
func (s *Store) Layouts(ctx context.Context) chronograf.LayoutsStore {
	return s.LayoutsStore
}

// Users returns a chronograf.UsersStore.
// If the context is a server context, then the underlying chronograf.UsersStore
// is returned.
// If there is an organization specified on context, then an organizations.UsersStore
// is returned.
// If niether are specified, a noop.UsersStore is returned.
func (s *Store) Users(ctx context.Context) chronograf.UsersStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.UsersStore
	}
	if org, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewUsersStore(s.UsersStore, org)
	}

	return &noop.UsersStore{}
}

// Dashboards returns a noop.DashboardsStore if the context has no organization specified
// and an organization.DashboardsStore otherwise.
func (s *Store) Dashboards(ctx context.Context) chronograf.DashboardsStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.DashboardsStore
	}
	if org, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewDashboardsStore(s.DashboardsStore, org)
	}

	return &noop.DashboardsStore{}
}

// OrganizationConfig returns a noop.OrganizationConfigStore if the context has no organization specified
// and an organization.OrganizationConfigStore otherwise.
func (s *Store) OrganizationConfig(ctx context.Context) chronograf.OrganizationConfigStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.OrganizationConfigStore
	}
	if orgID, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewOrganizationConfigStore(s.OrganizationConfigStore, orgID)
	}

	return &noop.OrganizationConfigStore{}
}

// Organizations returns the underlying OrganizationsStore.
func (s *Store) Organizations(ctx context.Context) chronograf.OrganizationsStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.OrganizationsStore
	}
	if isSuperAdmin := hasSuperAdminContext(ctx); isSuperAdmin {
		return s.OrganizationsStore
	}
	if org, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewOrganizationsStore(s.OrganizationsStore, org)
	}
	return &noop.OrganizationsStore{}
}

// Config returns the underlying ConfigStore.
func (s *Store) Config(ctx context.Context) chronograf.ConfigStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.ConfigStore
	}
	if isSuperAdmin := hasSuperAdminContext(ctx); isSuperAdmin {
		return s.ConfigStore
	}

	return &noop.ConfigStore{}
}

// Mappings returns the underlying MappingsStore.
func (s *Store) Mappings(ctx context.Context) chronograf.MappingsStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.MappingsStore
	}
	if isSuperAdmin := hasSuperAdminContext(ctx); isSuperAdmin {
		return s.MappingsStore
	}
	return &noop.MappingsStore{}
}
<commit_msg>Fix OrganizationConfig store to never manipulate global org config<commit_after>package server

import (
	"context"

	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/noop"
	"github.com/influxdata/chronograf/organizations"
	"github.com/influxdata/chronograf/roles"
)

// hasOrganizationContext retrieves organization specified on context
// under the organizations.ContextKey
func hasOrganizationContext(ctx context.Context) (string, bool) {
	// prevents panic in case of nil context
	if ctx == nil {
		return "", false
	}
	orgID, ok := ctx.Value(organizations.ContextKey).(string)
	// should never happen
	if !ok {
		return "", false
	}
	if orgID == "" {
		return "", false
	}
	return orgID, true
}

// hasRoleContext retrieves organization specified on context
// under the organizations.ContextKey
func hasRoleContext(ctx context.Context) (string, bool) {
	// prevents panic in case of nil context
	if ctx == nil {
		return "", false
	}
	role, ok := ctx.Value(roles.ContextKey).(string)
	// should never happen
	if !ok {
		return "", false
	}
	switch role {
	case roles.MemberRoleName, roles.ViewerRoleName, roles.EditorRoleName, roles.AdminRoleName:
		return role, true
	default:
		return "", false
	}
}

type userContextKey string

// UserContextKey is the context key for retrieving the user off of context
const UserContextKey = userContextKey("user")

// hasUserContext speficies if the context contains
// the UserContextKey and that the value stored there is chronograf.User
func hasUserContext(ctx context.Context) (*chronograf.User, bool) {
	// prevents panic in case of nil context
	if ctx == nil {
		return nil, false
	}
	u, ok := ctx.Value(UserContextKey).(*chronograf.User)
	// should never happen
	if !ok {
		return nil, false
	}
	if u == nil {
		return nil, false
	}
	return u, true
}

// hasSuperAdminContext speficies if the context contains
// the UserContextKey user is a super admin
func hasSuperAdminContext(ctx context.Context) bool {
	u, ok := hasUserContext(ctx)
	if !ok {
		return false
	}
	return u.SuperAdmin
}

// DataStore is collection of resources that are used by the Service
// Abstracting this into an interface was useful for isolated testing
type DataStore interface {
	Sources(ctx context.Context) chronograf.SourcesStore
	Servers(ctx context.Context) chronograf.ServersStore
	Layouts(ctx context.Context) chronograf.LayoutsStore
	Users(ctx context.Context) chronograf.UsersStore
	Organizations(ctx context.Context) chronograf.OrganizationsStore
	Mappings(ctx context.Context) chronograf.MappingsStore
	Dashboards(ctx context.Context) chronograf.DashboardsStore
	Config(ctx context.Context) chronograf.ConfigStore
	OrganizationConfig(ctx context.Context) chronograf.OrganizationConfigStore
}

// ensure that Store implements a DataStore
var _ DataStore = &Store{}

// Store implements the DataStore interface
type Store struct {
	SourcesStore            chronograf.SourcesStore
	ServersStore            chronograf.ServersStore
	LayoutsStore            chronograf.LayoutsStore
	UsersStore              chronograf.UsersStore
	DashboardsStore         chronograf.DashboardsStore
	MappingsStore           chronograf.MappingsStore
	OrganizationsStore      chronograf.OrganizationsStore
	ConfigStore             chronograf.ConfigStore
	OrganizationConfigStore chronograf.OrganizationConfigStore
}

// Sources returns a noop.SourcesStore if the context has no organization specified
// and an organization.SourcesStore otherwise.
func (s *Store) Sources(ctx context.Context) chronograf.SourcesStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.SourcesStore
	}
	if org, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewSourcesStore(s.SourcesStore, org)
	}

	return &noop.SourcesStore{}
}

// Servers returns a noop.ServersStore if the context has no organization specified
// and an organization.ServersStore otherwise.
func (s *Store) Servers(ctx context.Context) chronograf.ServersStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.ServersStore
	}
	if org, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewServersStore(s.ServersStore, org)
	}

	return &noop.ServersStore{}
}

// Layouts returns all layouts in the underlying layouts store.
func (s *Store) Layouts(ctx context.Context) chronograf.LayoutsStore {
	return s.LayoutsStore
}

// Users returns a chronograf.UsersStore.
// If the context is a server context, then the underlying chronograf.UsersStore
// is returned.
// If there is an organization specified on context, then an organizations.UsersStore
// is returned.
// If niether are specified, a noop.UsersStore is returned.
func (s *Store) Users(ctx context.Context) chronograf.UsersStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.UsersStore
	}
	if org, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewUsersStore(s.UsersStore, org)
	}

	return &noop.UsersStore{}
}

// Dashboards returns a noop.DashboardsStore if the context has no organization specified
// and an organization.DashboardsStore otherwise.
func (s *Store) Dashboards(ctx context.Context) chronograf.DashboardsStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.DashboardsStore
	}
	if org, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewDashboardsStore(s.DashboardsStore, org)
	}

	return &noop.DashboardsStore{}
}

// OrganizationConfig returns a noop.OrganizationConfigStore if the context has no organization specified
// and an organization.OrganizationConfigStore otherwise.
func (s *Store) OrganizationConfig(ctx context.Context) chronograf.OrganizationConfigStore {
	if orgID, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewOrganizationConfigStore(s.OrganizationConfigStore, orgID)
	}

	return &noop.OrganizationConfigStore{}
}

// Organizations returns the underlying OrganizationsStore.
func (s *Store) Organizations(ctx context.Context) chronograf.OrganizationsStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.OrganizationsStore
	}
	if isSuperAdmin := hasSuperAdminContext(ctx); isSuperAdmin {
		return s.OrganizationsStore
	}
	if org, ok := hasOrganizationContext(ctx); ok {
		return organizations.NewOrganizationsStore(s.OrganizationsStore, org)
	}
	return &noop.OrganizationsStore{}
}

// Config returns the underlying ConfigStore.
func (s *Store) Config(ctx context.Context) chronograf.ConfigStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.ConfigStore
	}
	if isSuperAdmin := hasSuperAdminContext(ctx); isSuperAdmin {
		return s.ConfigStore
	}

	return &noop.ConfigStore{}
}

// Mappings returns the underlying MappingsStore.
func (s *Store) Mappings(ctx context.Context) chronograf.MappingsStore {
	if isServer := hasServerContext(ctx); isServer {
		return s.MappingsStore
	}
	if isSuperAdmin := hasSuperAdminContext(ctx); isSuperAdmin {
		return s.MappingsStore
	}
	return &noop.MappingsStore{}
}
<|endoftext|>


<commit_before>package clusterf

import (
    "github.com/qmsk/clusterf/config"
    "github.com/qmsk/clusterf/ipvs"
	"net"
	"reflect"
    "syscall"
    "testing"
)

var testConfigServices = []struct{
	routes			Routes
	options			IPVSOptions
	config			map[string]config.Service
	services		Services
}{
	// trivial testcase with a single service with a single backend on startup
	{
		routes:		Routes{},
		options:	IPVSOptions{
			SchedName:	"wlc",
			FwdMethod:	ipvs.IP_VS_CONN_F_MASQ,
		},
		config:		map[string]config.Service{
			"test": config.Service{
				Frontend:	config.ServiceFrontend{IPv4:"10.0.1.1", TCP:80},
				Backends:	map[string]config.ServiceBackend{
					"test1":	config.ServiceBackend{IPv4:"10.1.0.1", TCP:80, Weight:10},
				},
			},
		},
		services:	Services{
			"inet+tcp://10.0.1.1:80": Service{
				Service: ipvs.Service{
					Af:			syscall.AF_INET,
					Protocol:	syscall.IPPROTO_TCP,
					Addr:		net.IP{10,0,1,1},
					Port:		80,

					SchedName:	"wlc",
					Flags:		ipvs.Flags{0, 0xffffffff},
					Netmask:	0xffffffff,
				},
				dests: ServiceDests{
					"10.1.0.1:80":	Dest{
						Dest: ipvs.Dest{
							Addr:		net.IP{10,1,0,1},
							Port:		80,
							FwdMethod:	ipvs.IP_VS_CONN_F_MASQ,
							Weight:		10,
						},
					},
				},
			},
		},
	},
}

func TestConfigServices(t *testing.T) {
	for _, test := range testConfigServices {
		services, err := configServices(test.config, test.routes, test.options)
		if err != nil {
			t.Fatalf("configServices error: %v\n", err)
		}

		if !reflect.DeepEqual(services, test.services) {
			t.Errorf("configServices mismatch:\n\t+ %#v\n\t- %#v\n", services, test.services)
		}
	}
}

// Test adding a new ConfigServiceFrontend after sync
// https://github.com/qmsk/clusterf/issues/4
/* func TestServiceAdd(t *testing.T) {
    serviceFrontend := config.ServiceFrontend{IPv4:"10.0.1.1", TCP:80}
    serviceBackend := config.ServiceBackend{IPv4:"10.1.0.1", TCP:80}

    services := NewServices()

    // sync
    ipvsDriver, err := services.SyncIPVS(IpvsConfig{FwdMethod: "masq", SchedName: "wlc", mock: true})
    if err != nil {
        t.Fatalf("services.SyncIPVS: %v", err)
    }

    // add
    services.ConfigEvent(config.Event{Action:config.SetConfig, Config:&config.ConfigServiceFrontend{ConfigSource:"test", ServiceName:"test", Frontend:serviceFrontend}})
    services.ConfigEvent(config.Event{Action:config.SetConfig, Config:&config.ConfigServiceBackend{ConfigSource:"test", ServiceName:"test", BackendName:"test1", Backend:serviceBackend}})

    // test ipvsDriver.dests
    ipvsKey := ipvsKey{"inet+tcp://10.0.1.1:80", "10.1.0.1:80"}

    if len(ipvsDriver.dests) != 1 {
        t.Errorf("incorrect sync dests: %v", ipvsDriver.dests)
    }
    if ipvsDriver.dests[ipvsKey] == nil {
        t.Errorf("missing sync dest: %v", ipvsKey)
    }
} */
<commit_msg>better test cases with diffs<commit_after>package clusterf

import (
    "github.com/qmsk/clusterf/config"
    "github.com/qmsk/clusterf/ipvs"
	"net"
	"github.com/kylelemons/godebug/pretty"
    "syscall"
    "testing"
)

var testConfigServices = map[string]struct{
	options			IPVSOptions
	configRoutes	map[string]config.Route
	config			map[string]config.Service
	services		Services
}{
	"simple": {
		options:	IPVSOptions{
			SchedName:	"wlc",
			FwdMethod:	ipvs.IP_VS_CONN_F_MASQ,
		},
		configRoutes: map[string]config.Route{

		},
		config:		map[string]config.Service{
			"test": config.Service{
				Frontend:	config.ServiceFrontend{IPv4:"10.0.0.1", TCP:80, UDP:80},
				Backends:	map[string]config.ServiceBackend{
					"test1":	config.ServiceBackend{IPv4:"10.1.0.1", TCP:8080, UDP:8081, Weight:10},
					"test2":	config.ServiceBackend{IPv4:"10.1.0.2", TCP:8082, Weight:10},
				},
			},
		},
		services:	Services{
			"inet+tcp://10.0.0.1:80": Service{
				Service: ipvs.Service{
					Af:			syscall.AF_INET,
					Protocol:	syscall.IPPROTO_TCP,
					Addr:		net.IP{10,0,0,1},
					Port:		80,

					SchedName:	"wlc",
					Flags:		ipvs.Flags{0, 0xffffffff},
					Netmask:	0xffffffff,
				},
				dests: ServiceDests{
					"10.1.0.1:8080": Dest{
						Dest: ipvs.Dest{
							Addr:		net.IP{10,1,0,1},
							Port:		8080,
							FwdMethod:	ipvs.IP_VS_CONN_F_MASQ,
							Weight:		10,
						},
					},
					"10.1.0.2:8082": Dest{
						Dest: ipvs.Dest{
							Addr:		net.IP{10,1,0,2},
							Port:		8082,
							FwdMethod:	ipvs.IP_VS_CONN_F_MASQ,
							Weight:		10,
						},
					},
				},
			},
			"inet+udp://10.0.0.1:80": Service{
				Service: ipvs.Service{
					Af:			syscall.AF_INET,
					Protocol:	syscall.IPPROTO_UDP,
					Addr:		net.IP{10,0,0,1},
					Port:		80,

					SchedName:	"wlc",
					Flags:		ipvs.Flags{0, 0xffffffff},
					Netmask:	0xffffffff,
				},
				dests: ServiceDests{
					"10.1.0.1:8081": Dest{
						Dest: ipvs.Dest{
							Addr:		net.IP{10,1,0,1},
							Port:		8081,
							FwdMethod:	ipvs.IP_VS_CONN_F_MASQ,
							Weight:		10,
						},
					},
				},
			},
		},
	},
}

func TestConfigServices(t *testing.T) {
	for testName, test := range testConfigServices {
		routes, err := configRoutes(test.configRoutes)
		if err != nil {
			t.Fatalf("%v configRoutes: %v\n", testName, err)
		}

		services, err := configServices(test.config, routes, test.options)
		if err != nil {
			t.Fatalf("%v configServices error: %v\n", testName, err)
		}

		if diff := pretty.Compare(test.services, services); diff != "" {
			t.Errorf("%v configServices incorrect:\n%s", testName, diff)
		}
	}
}

// Test adding a new ConfigServiceFrontend after sync
// https://github.com/qmsk/clusterf/issues/4
/* func TestServiceAdd(t *testing.T) {
    serviceFrontend := config.ServiceFrontend{IPv4:"10.0.1.1", TCP:80}
    serviceBackend := config.ServiceBackend{IPv4:"10.1.0.1", TCP:80}

    services := NewServices()

    // sync
    ipvsDriver, err := services.SyncIPVS(IpvsConfig{FwdMethod: "masq", SchedName: "wlc", mock: true})
    if err != nil {
        t.Fatalf("services.SyncIPVS: %v", err)
    }

    // add
    services.ConfigEvent(config.Event{Action:config.SetConfig, Config:&config.ConfigServiceFrontend{ConfigSource:"test", ServiceName:"test", Frontend:serviceFrontend}})
    services.ConfigEvent(config.Event{Action:config.SetConfig, Config:&config.ConfigServiceBackend{ConfigSource:"test", ServiceName:"test", BackendName:"test1", Backend:serviceBackend}})

    // test ipvsDriver.dests
    ipvsKey := ipvsKey{"inet+tcp://10.0.1.1:80", "10.1.0.1:80"}

    if len(ipvsDriver.dests) != 1 {
        t.Errorf("incorrect sync dests: %v", ipvsDriver.dests)
    }
    if ipvsDriver.dests[ipvsKey] == nil {
        t.Errorf("missing sync dest: %v", ipvsKey)
    }
} */
<|endoftext|>


<commit_before>package demoinfocs

import (
	"bytes"
	"encoding/binary"
	"io"
	"strconv"

	bit "github.com/markus-wa/demoinfocs-golang/bitread"
	msg "github.com/markus-wa/demoinfocs-golang/msg"
)

const (
	stNameInstanceBaseline = "instancebaseline"
	stNameUserInfo         = "userinfo"
	stNameModelPreCache    = "modelprecache"
)

type playerInfo struct {
	version     int64
	xuid        int64
	name        string
	userID      int
	guid        string
	friendsID   int
	friendsName string
	// Custom files stuff (CRC)
	customFiles0 int
	customFiles1 int
	customFiles2 int
	customFiles3 int
	// Amount of downloaded files from the server
	filesDownloaded byte
	// Bots
	isFakePlayer bool
	// HLTV Proxy
	isHltv bool
}

func (p *Parser) parseStringTables() {
	p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)
	tables := int(p.bitReader.ReadSingleByte())
	for i := 0; i < tables; i++ {
		tableName := p.bitReader.ReadString()
		p.parseSingleStringTable(tableName)
	}
	p.bitReader.EndChunk()
}

func (p *Parser) parseSingleStringTable(name string) {
	strings := p.bitReader.ReadSignedInt(16)
	for i := 0; i < strings; i++ {
		stringName := p.bitReader.ReadString()
		if len(stringName) >= 100 {
			panic("Someone said that Roy said I should panic")
		}
		if p.bitReader.ReadBit() {
			userDataSize := p.bitReader.ReadSignedInt(16)
			data := p.bitReader.ReadBytes(userDataSize)
			switch name {
			case stNameUserInfo:
				player := parsePlayerInfo(bytes.NewReader(data))
				pid, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse id from string")
				}
				p.rawPlayers[int(pid)] = player

			case stNameInstanceBaseline:
				pid, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse id from string")
				}
				p.instanceBaselines[int(pid)] = data

			case stNameModelPreCache:
				p.modelPreCache = append(p.modelPreCache, stringName)

			default:
				// Irrelevant table
			}
		}
	}

	// Client side stuff, dgaf
	if p.bitReader.ReadBit() {
		strings2 := p.bitReader.ReadSignedInt(16)
		for i := 0; i < strings2; i++ {
			p.bitReader.ReadString()
			if p.bitReader.ReadBit() {
				p.bitReader.Skip(p.bitReader.ReadSignedInt(16))
			}
		}
	}
}

func (p *Parser) handleUpdateStringTable(tab *msg.CSVCMsg_UpdateStringTable) {
	defer func() {
		p.setError(recoverFromUnexpectedEOF(recover()))
	}()

	cTab := p.stringTables[tab.TableId]
	switch cTab.Name {
	case stNameUserInfo:
		fallthrough
	case stNameModelPreCache:
		fallthrough
	case stNameInstanceBaseline:
		// Only handle updates for the above types
		p.handleCreateStringTable(cTab)
	}
}

func (p *Parser) handleCreateStringTable(tab *msg.CSVCMsg_CreateStringTable) {
	defer func() {
		p.setError(recoverFromUnexpectedEOF(recover()))
	}()

	if tab.Name == stNameModelPreCache {
		for i := len(p.modelPreCache); i < int(tab.MaxEntries); i++ {
			p.modelPreCache = append(p.modelPreCache, "")
		}
	}

	br := bit.NewSmallBitReader(bytes.NewReader(tab.StringData))

	if br.ReadBit() {
		panic("Can't decode")
	}

	nTmp := tab.MaxEntries
	nEntryBits := 0

	for nTmp != 0 {
		nTmp = nTmp >> 1
		nEntryBits++
	}
	if nEntryBits > 0 {
		nEntryBits--
	}

	hist := make([]string, 0)
	lastEntry := -1
	for i := 0; i < int(tab.NumEntries); i++ {
		entryIndex := lastEntry + 1
		if !br.ReadBit() {
			entryIndex = int(br.ReadInt(nEntryBits))
		}

		lastEntry = entryIndex

		var entry string
		if entryIndex < 0 || entryIndex >= int(tab.MaxEntries) {
			panic("Something went to shit")
		}
		if br.ReadBit() {
			if br.ReadBit() {
				idx := br.ReadInt(5)
				bytes2cp := int(br.ReadInt(5))
				entry = hist[idx][:bytes2cp]

				entry += br.ReadString()
			} else {
				entry = br.ReadString()
			}
		}

		if len(hist) > 31 {
			hist = hist[1:]
		}
		hist = append(hist, entry)

		var userdat []byte
		if br.ReadBit() {
			if tab.UserDataFixedSize {
				// Should always be < 8 bits => use faster ReadBitsToByte() over ReadBits()
				userdat = []byte{br.ReadBitsToByte(int(tab.UserDataSizeBits))}
			} else {
				userdat = br.ReadBytes(int(br.ReadInt(14)))
			}
		}

		if len(userdat) == 0 {
			break
		}

		switch tab.Name {
		case stNameUserInfo:
			p.rawPlayers[entryIndex] = parsePlayerInfo(bytes.NewReader(userdat))

		case stNameInstanceBaseline:
			classid, err := strconv.ParseInt(entry, 10, 64)
			if err != nil {
				panic("WTF VOLVO PLS")
			}
			p.instanceBaselines[int(classid)] = userdat

		case stNameModelPreCache:
			p.modelPreCache[entryIndex] = entry
		}
	}
	br.Pool()

	p.stringTables = append(p.stringTables, tab)
}

func parsePlayerInfo(reader io.Reader) *playerInfo {
	br := bit.NewSmallBitReader(reader)

	res := &playerInfo{
		version:     int64(binary.BigEndian.Uint64(br.ReadBytes(8))),
		xuid:        int64(binary.BigEndian.Uint64(br.ReadBytes(8))),
		name:        br.ReadCString(128),
		userID:      int(int32(binary.BigEndian.Uint32(br.ReadBytes(4)))),
		guid:        br.ReadCString(33),
		friendsID:   int(int32(binary.BigEndian.Uint32(br.ReadBytes(4)))),
		friendsName: br.ReadCString(128),

		isFakePlayer: br.ReadSingleByte()&0xff != 0,
		isHltv:       br.ReadSingleByte()&0xff != 0,

		customFiles0: int(br.ReadInt(32)),
		customFiles1: int(br.ReadInt(32)),
		customFiles2: int(br.ReadInt(32)),
		customFiles3: int(br.ReadInt(32)),

		filesDownloaded: br.ReadSingleByte(),
	}

	br.Pool()
	return res
}
<commit_msg>Fixed UpdateStringTables not being handled correctly<commit_after>package demoinfocs

import (
	"bytes"
	"encoding/binary"
	"io"
	"strconv"

	bit "github.com/markus-wa/demoinfocs-golang/bitread"
	msg "github.com/markus-wa/demoinfocs-golang/msg"
)

const (
	stNameInstanceBaseline = "instancebaseline"
	stNameUserInfo         = "userinfo"
	stNameModelPreCache    = "modelprecache"
)

type playerInfo struct {
	version     int64
	xuid        int64
	name        string
	userID      int
	guid        string
	friendsID   int
	friendsName string
	// Custom files stuff (CRC)
	customFiles0 int
	customFiles1 int
	customFiles2 int
	customFiles3 int
	// Amount of downloaded files from the server
	filesDownloaded byte
	// Bots
	isFakePlayer bool
	// HLTV Proxy
	isHltv bool
}

func (p *Parser) parseStringTables() {
	p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)
	tables := int(p.bitReader.ReadSingleByte())
	for i := 0; i < tables; i++ {
		tableName := p.bitReader.ReadString()
		p.parseSingleStringTable(tableName)
	}
	p.bitReader.EndChunk()
}

func (p *Parser) parseSingleStringTable(name string) {
	strings := p.bitReader.ReadSignedInt(16)
	for i := 0; i < strings; i++ {
		stringName := p.bitReader.ReadString()
		if len(stringName) >= 100 {
			panic("Someone said that Roy said I should panic")
		}
		if p.bitReader.ReadBit() {
			userDataSize := p.bitReader.ReadSignedInt(16)
			data := p.bitReader.ReadBytes(userDataSize)
			switch name {
			case stNameUserInfo:
				player := parsePlayerInfo(bytes.NewReader(data))
				pid, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse id from string")
				}
				p.rawPlayers[int(pid)] = player

			case stNameInstanceBaseline:
				pid, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse id from string")
				}
				p.instanceBaselines[int(pid)] = data

			case stNameModelPreCache:
				p.modelPreCache = append(p.modelPreCache, stringName)

			default:
				// Irrelevant table
			}
		}
	}

	// Client side stuff, dgaf
	if p.bitReader.ReadBit() {
		strings2 := p.bitReader.ReadSignedInt(16)
		for i := 0; i < strings2; i++ {
			p.bitReader.ReadString()
			if p.bitReader.ReadBit() {
				p.bitReader.Skip(p.bitReader.ReadSignedInt(16))
			}
		}
	}
}

func (p *Parser) handleUpdateStringTable(tab *msg.CSVCMsg_UpdateStringTable) {
	// No need for recoverFromUnexpectedEOF here as we do that in processStringTable already

	cTab := p.stringTables[tab.TableId]
	switch cTab.Name {
	case stNameUserInfo:
		fallthrough
	case stNameModelPreCache:
		fallthrough
	case stNameInstanceBaseline:
		// Only handle updates for the above types
		// Create fake CreateStringTable and handle it like one of those
		cTab.NumEntries = tab.NumChangedEntries
		cTab.StringData = tab.StringData
		p.processStringTable(cTab)
	}
}

func (p *Parser) handleCreateStringTable(tab *msg.CSVCMsg_CreateStringTable) {
	// No need for recoverFromUnexpectedEOF here as we do that in processStringTable already

	p.processStringTable(tab)

	p.stringTables = append(p.stringTables, tab)
}

func (p *Parser) processStringTable(tab *msg.CSVCMsg_CreateStringTable) {
	defer func() {
		p.setError(recoverFromUnexpectedEOF(recover()))
	}()

	if tab.Name == stNameModelPreCache {
		for i := len(p.modelPreCache); i < int(tab.MaxEntries); i++ {
			p.modelPreCache = append(p.modelPreCache, "")
		}
	}

	br := bit.NewSmallBitReader(bytes.NewReader(tab.StringData))

	if br.ReadBit() {
		panic("Can't decode")
	}

	nTmp := tab.MaxEntries
	nEntryBits := 0

	for nTmp != 0 {
		nTmp = nTmp >> 1
		nEntryBits++
	}
	if nEntryBits > 0 {
		nEntryBits--
	}

	hist := make([]string, 0)
	lastEntry := -1
	for i := 0; i < int(tab.NumEntries); i++ {
		entryIndex := lastEntry + 1
		if !br.ReadBit() {
			entryIndex = int(br.ReadInt(nEntryBits))
		}

		lastEntry = entryIndex

		var entry string
		if entryIndex < 0 || entryIndex >= int(tab.MaxEntries) {
			panic("Something went to shit")
		}
		if br.ReadBit() {
			if br.ReadBit() {
				idx := br.ReadInt(5)
				bytes2cp := int(br.ReadInt(5))
				entry = hist[idx][:bytes2cp]

				entry += br.ReadString()
			} else {
				entry = br.ReadString()
			}
		}

		if len(hist) > 31 {
			hist = hist[1:]
		}
		hist = append(hist, entry)

		var userdat []byte
		if br.ReadBit() {
			if tab.UserDataFixedSize {
				// Should always be < 8 bits => use faster ReadBitsToByte() over ReadBits()
				userdat = []byte{br.ReadBitsToByte(int(tab.UserDataSizeBits))}
			} else {
				userdat = br.ReadBytes(int(br.ReadInt(14)))
			}
		}

		if len(userdat) == 0 {
			break
		}

		switch tab.Name {
		case stNameUserInfo:
			p.rawPlayers[entryIndex] = parsePlayerInfo(bytes.NewReader(userdat))

		case stNameInstanceBaseline:
			classID, err := strconv.ParseInt(entry, 10, 64)
			if err != nil {
				panic("WTF VOLVO PLS")
			}
			p.instanceBaselines[int(classID)] = userdat

		case stNameModelPreCache:
			p.modelPreCache[entryIndex] = entry
		}
	}
	br.Pool()
}

func parsePlayerInfo(reader io.Reader) *playerInfo {
	br := bit.NewSmallBitReader(reader)

	res := &playerInfo{
		version:     int64(binary.BigEndian.Uint64(br.ReadBytes(8))),
		xuid:        int64(binary.BigEndian.Uint64(br.ReadBytes(8))),
		name:        br.ReadCString(128),
		userID:      int(int32(binary.BigEndian.Uint32(br.ReadBytes(4)))),
		guid:        br.ReadCString(33),
		friendsID:   int(int32(binary.BigEndian.Uint32(br.ReadBytes(4)))),
		friendsName: br.ReadCString(128),

		isFakePlayer: br.ReadSingleByte()&0xff != 0,
		isHltv:       br.ReadSingleByte()&0xff != 0,

		customFiles0: int(br.ReadInt(32)),
		customFiles1: int(br.ReadInt(32)),
		customFiles2: int(br.ReadInt(32)),
		customFiles3: int(br.ReadInt(32)),

		filesDownloaded: br.ReadSingleByte(),
	}

	br.Pool()
	return res
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"sync"
)

type StubHandler struct {
	stubs           map[string]Stub
	fallbackHandler http.Handler
	stubMutex       sync.Mutex
}

type Stub struct {
	Method string `json:"method"`
	Path   string `json:"path"`
	Body   string `json:"body"`
}

func (h *StubHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// Always lock the stubs map for the duration of the request.
	// Note that this effectively serializes all calls through StubHandler.
	// As this is only used for testing this is deemed acceptable.
	h.stubMutex.Lock()
	defer h.stubMutex.Unlock()

	if req.URL.Path == "/stubs" {
		switch req.Method {
		case "POST":
			h.createStubHandler(w, req)
		case "GET":
			h.listStubsHandler(w, req)
		case "DELETE":
			h.deleteStubsHandler(w, req)
		}
	} else {
		h.returnStubHandler(w, req)
	}
}

func (h *StubHandler) createStubHandler(w http.ResponseWriter, req *http.Request) {
	var stub Stub
	jsonDecoder := json.NewDecoder(req.Body)
	err := jsonDecoder.Decode(&stub)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		_, err = fmt.Fprintf(w, "Invalid JSON: %v", err)
		if err != nil {
			log.Printf("Could not send response to client due to: %v", err)
		}
	}

	h.stubs[stub.Method+stub.Path] = stub
}

func (h *StubHandler) returnStubHandler(w http.ResponseWriter, req *http.Request) {
	if stub, ok := h.stubs[req.Method+req.URL.Path]; ok {
		_, err := io.WriteString(w, stub.Body)
		if err != nil {
			log.Printf("Could not send response to client due to: %v", err)
		}
	} else {
		if h.fallbackHandler == nil {
			w.WriteHeader(http.StatusNotFound)
		} else {
			h.fallbackHandler.ServeHTTP(w, req)
		}
	}
}

func (h *StubHandler) listStubsHandler(w http.ResponseWriter, req *http.Request) {
	var stubs []Stub
	encoder := json.NewEncoder(w)
	for _, v := range h.stubs {
		stubs = append(stubs, v)
	}

	encoder.Encode(stubs)
}

func (h *StubHandler) deleteStubsHandler(w http.ResponseWriter, req *http.Request) {
	h.stubs = make(map[string]Stub)
}

func NewStubHandler() *StubHandler {
	var s StubHandler
	s.stubs = make(map[string]Stub)
	return &s
}
<commit_msg>GET /stubs should return [] when empty not null<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"sync"
)

type StubHandler struct {
	stubs           map[string]Stub
	fallbackHandler http.Handler
	stubMutex       sync.Mutex
}

type Stub struct {
	Method string `json:"method"`
	Path   string `json:"path"`
	Body   string `json:"body"`
}

func (h *StubHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// Always lock the stubs map for the duration of the request.
	// Note that this effectively serializes all calls through StubHandler.
	// As this is only used for testing this is deemed acceptable.
	h.stubMutex.Lock()
	defer h.stubMutex.Unlock()

	if req.URL.Path == "/stubs" {
		switch req.Method {
		case "POST":
			h.createStubHandler(w, req)
		case "GET":
			h.listStubsHandler(w, req)
		case "DELETE":
			h.deleteStubsHandler(w, req)
		}
	} else {
		h.returnStubHandler(w, req)
	}
}

func (h *StubHandler) createStubHandler(w http.ResponseWriter, req *http.Request) {
	var stub Stub
	jsonDecoder := json.NewDecoder(req.Body)
	err := jsonDecoder.Decode(&stub)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		_, err = fmt.Fprintf(w, "Invalid JSON: %v", err)
		if err != nil {
			log.Printf("Could not send response to client due to: %v", err)
		}
	}

	h.stubs[stub.Method+stub.Path] = stub
}

func (h *StubHandler) returnStubHandler(w http.ResponseWriter, req *http.Request) {
	if stub, ok := h.stubs[req.Method+req.URL.Path]; ok {
		_, err := io.WriteString(w, stub.Body)
		if err != nil {
			log.Printf("Could not send response to client due to: %v", err)
		}
	} else {
		if h.fallbackHandler == nil {
			w.WriteHeader(http.StatusNotFound)
		} else {
			h.fallbackHandler.ServeHTTP(w, req)
		}
	}
}

func (h *StubHandler) listStubsHandler(w http.ResponseWriter, req *http.Request) {
	stubs := make([]Stub, 0, len(h.stubs))
	encoder := json.NewEncoder(w)
	for _, v := range h.stubs {
		stubs = append(stubs, v)
	}

	encoder.Encode(stubs)
}

func (h *StubHandler) deleteStubsHandler(w http.ResponseWriter, req *http.Request) {
	h.stubs = make(map[string]Stub)
}

func NewStubHandler() *StubHandler {
	var s StubHandler
	s.stubs = make(map[string]Stub)
	return &s
}
<|endoftext|>