<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	_ "github.com/lib/pq"
	"golang.org/x/crypto/bcrypt"
	"io/ioutil"
	"log"
	"net/http"
)

var (
	ServerVersion string = "pre version"
	APIVersion    string = "pre api"
)

func main() {
	fmt.Println("TRACKMON SERVER\nCopyright (c) 2017, Paul Kramme under BSD 2-Clause")
	fmt.Println("Please report bugs to https://github.com/trackmon/trackmon-server")

	// Configure flags
	CreateConfigFlag := flag.Bool("createconfig", false, "Creates a standard configuration and exits")
	ConfigLocation := flag.String("config", "./trackmonserv.conf", "Location of config file. Standard is ./trackmonserv")
	ShowLicenses := flag.Bool("licenses", false, "Shows licenses and exits")
	ShowVersion := flag.Bool("version", false, "Shows version and exits")
	ShowJsonVersion := flag.Bool("versionjson", false, "Shows version in json and exits")

	// Check flags
	flag.Parse()
	if *CreateConfigFlag == true {
		CreateConfig()
		return
	}
	if *ShowLicenses == true {
		fmt.Println("trackmon servers license\n")
		fmt.Print(trackmonlicense)
		fmt.Println("\n")

		fmt.Println("This project uses github.com/gorilla/mux\n")
		fmt.Print(muxlicense)
		fmt.Println("\n")

		return
	}
	if *ShowVersion == true {
		fmt.Printf("Server Version: %s\nAPI Version: %s\n", ServerVersion, APIVersion)
		return
	}
	if *ShowJsonVersion == true {
		fmt.Printf("{\"serverversion\":\"%s\",\"apiversion\":\"%s\"}", ServerVersion, APIVersion)
	}

	// Load config
	var Config Configuration
	Configfile, err := ioutil.ReadFile(*ConfigLocation)
	if err != nil {
		fmt.Println("Couldn't find or open config file. Create one with -createconfig")
		panic(err)
	}
	err = fromjson(string(Configfile), &Config)
	if err != nil {
		panic(err)
	}

	// Setup database connection
	DatabaseConnectionString := fmt.Sprintf("dbname=trackmon_server_production user=trackmon host=%s password=%s", Config.DatabaseAddress, Config.DatabasePassword)
	db, err := sql.Open("postgres", DatabaseConnectionString)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	// Configure router and server
	r := mux.NewRouter()

	r.HandleFunc("/", RootHandler) // Returnes 200 OK, can be used for health checks
	r.HandleFunc("/version", VersionHandler)

	r.HandleFunc("/user/{user_id}", func(w http.ResponseWriter, r *http.Request) {
		UserHandler(w, r, db)
	})
	r.HandleFunc("/user/{user_id}/{account}", func(w http.ResponseWriter, r *http.Request) {
		AccountHandler(w, r, db)
	})
	r.HandleFunc("/signup", func(w http.ResponseWriter, r *http.Request) {
		NewUserHandler(w, r, db)
	})

	srv := &http.Server{
		Handler: r,
		Addr:    Config.ListeningAddress,
	}

	// Start the server
	log.Println("Initialization complete")
	log.Fatal(srv.ListenAndServe())
}

/*
 ██████  ██████  ███    ██ ███████ ██  ██████
██      ██    ██ ████   ██ ██      ██ ██
██      ██    ██ ██ ██  ██ █████   ██ ██   ███
██      ██    ██ ██  ██ ██ ██      ██ ██    ██
 ██████  ██████  ██   ████ ██      ██  ██████
*/

type Configuration struct {
	ListeningAddress string
	DatabaseAddress  string
	DatabasePassword string
}

func CreateConfig() {
	var Config Configuration

	// Standard config
	Config.ListeningAddress = ":80"
	Config.DatabaseAddress = "localhost"
	Config.DatabasePassword = ""

	ByteJsonConfig, err := toprettyjson(Config)
	if err != nil {
		panic(err)
	}
	fmt.Println("Writing configuration to ./trackmonserv.conf")
	err = ioutil.WriteFile("./trackmonserv.conf", ByteJsonConfig, 0644)
	if err != nil {
		panic(err)
	}
}

/*
██   ██  █████  ███    ██ ██████  ██      ███████ ██████
██   ██ ██   ██ ████   ██ ██   ██ ██      ██      ██   ██
███████ ███████ ██ ██  ██ ██   ██ ██      █████   ██████
██   ██ ██   ██ ██  ██ ██ ██   ██ ██      ██      ██   ██
██   ██ ██   ██ ██   ████ ██████  ███████ ███████ ██   ██
*/

func RootHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func VersionHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "{\"serverversion\":\"%s\",\"apiversion\":\"%s\"}", ServerVersion, APIVersion)
	w.WriteHeader(http.StatusOK)
}

func NewUserHandler(w http.ResponseWriter, r *http.Request, db *sql.DB) {
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	HashedPassword, err := HashPassword(password)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	log.Println(username, HashedPassword)
	// TODO: Check if user exist in database
	// TODO: IF NOT Create new user and write to database
}

func UserHandler(w http.ResponseWriter, r *http.Request, db *sql.DB) {
	variables := mux.Vars(r)
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	} // All requests below here have given basic auth
	log.Printf("User %s with pw %s wants info about all accounts of %s\n", username, password, string(variables["user_id"]))
	if username != string(variables["user_id"]) {
		w.WriteHeader(http.StatusForbidden)
	}
	// TODO: Check if user exists, if and if password correct, give him info
}

func AccountHandler(w http.ResponseWriter, r *http.Request, db *sql.DB) {
	variables := mux.Vars(r)
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	} // All requests below here have given basic auth
	log.Printf("User %s with pw %s wants info about account %s from %s\n", username, password, string(variables["account"]), string(variables["user_id"]))
}

/*
 ██████ ██████  ██    ██ ██████  ████████
██      ██   ██  ██  ██  ██   ██    ██
██      ██████    ████   ██████     ██
██      ██   ██    ██    ██         ██
 ██████ ██   ██    ██    ██         ██
*/

func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10)
	return string(bytes), err
}

func CheckPasswordHash(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}

/*
     ██ ███████  ██████  ███    ██
     ██ ██      ██    ██ ████   ██
     ██ ███████ ██    ██ ██ ██  ██
██   ██      ██ ██    ██ ██  ██ ██
 █████  ███████  ██████  ██   ████
*/

func fromjson(src string, v interface{}) error {
	return json.Unmarshal([]byte(src), v)
}

func tojson(v interface{}) ([]byte, error) {
	return json.Marshal(v)
}

func toprettyjson(v interface{}) ([]byte, error) {
	return json.MarshalIndent(v, "", "\t")
}

/*
██      ██  ██████ ███████ ███    ██ ███████ ███████
██      ██ ██      ██      ████   ██ ██      ██
██      ██ ██      █████   ██ ██  ██ ███████ █████
██      ██ ██      ██      ██  ██ ██      ██ ██
███████ ██  ██████ ███████ ██   ████ ███████ ███████
*/

const (
	muxlicense string = `Copyright (c) 2012 Rodrigo Moraes. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

	 * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
	 * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
	 * Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`
	trackmonlicense string = `BSD 2-Clause License

	Copyright (c) 2017, Paul Kramme
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	* Redistributions of source code must retain the above copyright notice, this
	  list of conditions and the following disclaimer.

	* Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
	FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
	DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
	OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
`
)
<commit_msg>Add RecreateFunction stub<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	_ "github.com/lib/pq"
	"golang.org/x/crypto/bcrypt"
	"io/ioutil"
	"log"
	"net/http"
)

var (
	ServerVersion string = "pre version"
	APIVersion    string = "pre api"
)

func main() {
	fmt.Println("TRACKMON SERVER\nCopyright (c) 2017, Paul Kramme under BSD 2-Clause")
	fmt.Println("Please report bugs to https://github.com/trackmon/trackmon-server")

	// Configure flags
	CreateConfigFlag := flag.Bool("createconfig", false, "Creates a standard configuration and exits")
	ConfigLocation := flag.String("config", "./trackmonserv.conf", "Location of config file. Standard is ./trackmonserv")
	ShowLicenses := flag.Bool("licenses", false, "Shows licenses and exits")
	ShowVersion := flag.Bool("version", false, "Shows version and exits")
	ShowJsonVersion := flag.Bool("versionjson", false, "Shows version in json and exits")

	// Check flags
	flag.Parse()
	if *CreateConfigFlag == true {
		CreateConfig()
		return
	}
	if *ShowLicenses == true {
		fmt.Println("trackmon servers license\n")
		fmt.Print(trackmonlicense)
		fmt.Println("\n")

		fmt.Println("This project uses github.com/gorilla/mux\n")
		fmt.Print(muxlicense)
		fmt.Println("\n")

		return
	}
	if *ShowVersion == true {
		fmt.Printf("Server Version: %s\nAPI Version: %s\n", ServerVersion, APIVersion)
		return
	}
	if *ShowJsonVersion == true {
		fmt.Printf("{\"serverversion\":\"%s\",\"apiversion\":\"%s\"}", ServerVersion, APIVersion)
	}

	// Load config
	var Config Configuration
	Configfile, err := ioutil.ReadFile(*ConfigLocation)
	if err != nil {
		fmt.Println("Couldn't find or open config file. Create one with -createconfig")
		panic(err)
	}
	err = fromjson(string(Configfile), &Config)
	if err != nil {
		panic(err)
	}

	// Setup database connection
	DatabaseConnectionString := fmt.Sprintf("dbname=trackmon_server_production user=trackmon host=%s password=%s", Config.DatabaseAddress, Config.DatabasePassword)
	db, err := sql.Open("postgres", DatabaseConnectionString)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	// Configure router and server
	r := mux.NewRouter()

	r.HandleFunc("/", RootHandler) // Returnes 200 OK, can be used for health checks
	r.HandleFunc("/version", VersionHandler)

	r.HandleFunc("/user/{user_id}", func(w http.ResponseWriter, r *http.Request) {
		UserHandler(w, r, db)
	})
	r.HandleFunc("/user/{user_id}/{account}", func(w http.ResponseWriter, r *http.Request) {
		AccountHandler(w, r, db)
	})
	r.HandleFunc("/signup", func(w http.ResponseWriter, r *http.Request) {
		NewUserHandler(w, r, db)
	})

	srv := &http.Server{
		Handler: r,
		Addr:    Config.ListeningAddress,
	}

	// Start the server
	log.Println("Initialization complete")
	log.Fatal(srv.ListenAndServe())
}

/*
 ██████  ██████  ███    ██ ███████ ██  ██████
██      ██    ██ ████   ██ ██      ██ ██
██      ██    ██ ██ ██  ██ █████   ██ ██   ███
██      ██    ██ ██  ██ ██ ██      ██ ██    ██
 ██████  ██████  ██   ████ ██      ██  ██████
*/

type Configuration struct {
	ListeningAddress string
	DatabaseAddress  string
	DatabasePassword string
}

func RecreateConfig(path string) {
	return
}

func CreateConfig() {
	var Config Configuration

	// Standard config
	Config.ListeningAddress = ":80"
	Config.DatabaseAddress = "localhost"
	Config.DatabasePassword = ""

	ByteJsonConfig, err := toprettyjson(Config)
	if err != nil {
		panic(err)
	}
	fmt.Println("Writing configuration to ./trackmonserv.conf")
	err = ioutil.WriteFile("./trackmonserv.conf", ByteJsonConfig, 0644)
	if err != nil {
		panic(err)
	}
}

/*
██   ██  █████  ███    ██ ██████  ██      ███████ ██████
██   ██ ██   ██ ████   ██ ██   ██ ██      ██      ██   ██
███████ ███████ ██ ██  ██ ██   ██ ██      █████   ██████
██   ██ ██   ██ ██  ██ ██ ██   ██ ██      ██      ██   ██
██   ██ ██   ██ ██   ████ ██████  ███████ ███████ ██   ██
*/

func RootHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func VersionHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "{\"serverversion\":\"%s\",\"apiversion\":\"%s\"}", ServerVersion, APIVersion)
	w.WriteHeader(http.StatusOK)
}

func NewUserHandler(w http.ResponseWriter, r *http.Request, db *sql.DB) {
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	HashedPassword, err := HashPassword(password)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	log.Println(username, HashedPassword)
	// TODO: Check if user exist in database
	// TODO: IF NOT Create new user and write to database
}

func UserHandler(w http.ResponseWriter, r *http.Request, db *sql.DB) {
	variables := mux.Vars(r)
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	} // All requests below here have given basic auth
	log.Printf("User %s with pw %s wants info about all accounts of %s\n", username, password, string(variables["user_id"]))
	if username != string(variables["user_id"]) {
		w.WriteHeader(http.StatusForbidden)
	}
	// TODO: Check if user exists, if and if password correct, give him info
}

func AccountHandler(w http.ResponseWriter, r *http.Request, db *sql.DB) {
	variables := mux.Vars(r)
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	} // All requests below here have given basic auth
	log.Printf("User %s with pw %s wants info about account %s from %s\n", username, password, string(variables["account"]), string(variables["user_id"]))
}

/*
 ██████ ██████  ██    ██ ██████  ████████
██      ██   ██  ██  ██  ██   ██    ██
██      ██████    ████   ██████     ██
██      ██   ██    ██    ██         ██
 ██████ ██   ██    ██    ██         ██
*/

func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10)
	return string(bytes), err
}

func CheckPasswordHash(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}

/*
     ██ ███████  ██████  ███    ██
     ██ ██      ██    ██ ████   ██
     ██ ███████ ██    ██ ██ ██  ██
██   ██      ██ ██    ██ ██  ██ ██
 █████  ███████  ██████  ██   ████
*/

func fromjson(src string, v interface{}) error {
	return json.Unmarshal([]byte(src), v)
}

func tojson(v interface{}) ([]byte, error) {
	return json.Marshal(v)
}

func toprettyjson(v interface{}) ([]byte, error) {
	return json.MarshalIndent(v, "", "\t")
}

/*
██      ██  ██████ ███████ ███    ██ ███████ ███████
██      ██ ██      ██      ████   ██ ██      ██
██      ██ ██      █████   ██ ██  ██ ███████ █████
██      ██ ██      ██      ██  ██ ██      ██ ██
███████ ██  ██████ ███████ ██   ████ ███████ ███████
*/

const (
	muxlicense string = `Copyright (c) 2012 Rodrigo Moraes. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

	 * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
	 * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
	 * Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`
	trackmonlicense string = `BSD 2-Clause License

	Copyright (c) 2017, Paul Kramme
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	* Redistributions of source code must retain the above copyright notice, this
	  list of conditions and the following disclaimer.

	* Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
	FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
	DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
	OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
`
)
<|endoftext|>


<commit_before>// Copyright 2017 The go-interpreter 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 wagon

import (
	"bytes"
	"os/exec"
	"testing"
)

func TestGofmt(t *testing.T) {
	exe, err := exec.LookPath("goimports")
	if err != nil {
		switch e := err.(type) {
		case *exec.Error:
			if e.Err == exec.ErrNotFound {
				exe, err = exec.LookPath("gofmt")
			}
		}
	}
	if err != nil {
		t.Fatal(err)
	}

	cmd := exec.Command(exe, "-d", ".")
	buf := new(bytes.Buffer)
	cmd.Stdout = buf
	cmd.Stderr = buf

	err = cmd.Run()
	if err != nil {
		t.Fatalf("error running %s:\n%s\n%v", exe, string(buf.Bytes()), err)
	}

	if len(buf.Bytes()) != 0 {
		t.Errorf("some files were not gofmt'ed:\n%s\n", string(buf.Bytes()))
	}
}
<commit_msg>wagon: add go-vet test<commit_after>// Copyright 2017 The go-interpreter 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 wagon

import (
	"bytes"
	"os/exec"
	"testing"
)

func TestGovet(t *testing.T) {
	cmd := exec.Command("go", "vet", "./...")
	buf := new(bytes.Buffer)
	cmd.Stdout = buf
	cmd.Stderr = buf
	err := cmd.Run()
	if err != nil {
		t.Fatalf("error running %s:\n%s\n%v", "go vet", string(buf.Bytes()), err)
	}
}

func TestGofmt(t *testing.T) {
	exe, err := exec.LookPath("goimports")
	if err != nil {
		switch e := err.(type) {
		case *exec.Error:
			if e.Err == exec.ErrNotFound {
				exe, err = exec.LookPath("gofmt")
			}
		}
	}
	if err != nil {
		t.Fatal(err)
	}

	cmd := exec.Command(exe, "-d", ".")
	buf := new(bytes.Buffer)
	cmd.Stdout = buf
	cmd.Stderr = buf

	err = cmd.Run()
	if err != nil {
		t.Fatalf("error running %s:\n%s\n%v", exe, string(buf.Bytes()), err)
	}

	if len(buf.Bytes()) != 0 {
		t.Errorf("some files were not gofmt'ed:\n%s\n", string(buf.Bytes()))
	}
}
<|endoftext|>


<commit_before>package watch

import (
	"fmt"
	"log"
	"os"
	"reflect"
	"time"

	consulapi "github.com/hashicorp/consul/api"
)

const (
	// retryInterval is the base retry value
	retryInterval = 5 * time.Second

	// maximum back off time, this is to prevent
	// exponential runaway
	maxBackoffTime = 180 * time.Second
)

// Run is used to run a watch plan
func (p *WatchPlan) Run(address string) error {
	// Setup the client
	p.address = address
	conf := consulapi.DefaultConfig()
	conf.Address = address
	conf.Datacenter = p.Datacenter
	conf.Token = p.Token
	client, err := consulapi.NewClient(conf)
	if err != nil {
		return fmt.Errorf("Failed to connect to agent: %v", err)
	}
	p.client = client

	// Create the logger
	output := p.LogOutput
	if output == nil {
		output = os.Stderr
	}
	logger := log.New(output, "", log.LstdFlags)

	// Loop until we are canceled
	failures := 0
OUTER:
	for !p.shouldStop() {
		// Invoke the handler
		index, result, err := p.Func(p)

		// Check if we should terminate since the function
		// could have blocked for a while
		if p.shouldStop() {
			break
		}

		// Handle an error in the watch function
		if err != nil {
			// Perform an exponential backoff
			failures++
			p.lastIndex = 0
			retry := retryInterval * time.Duration(failures*failures)
			if retry > maxBackoffTime {
				retry = maxBackoffTime
			}
			logger.Printf("consul.watch: Watch (type: %s) errored: %v, retry in %v",
				p.Type, err, retry)
			select {
			case <-time.After(retry):
				continue OUTER
			case <-p.stopCh:
				return nil
			}
		}

		// Clear the failures
		failures = 0

		// If the index is unchanged do nothing
		if index == p.lastIndex {
			continue
		}

		// Update the index, look for change
		oldIndex := p.lastIndex
		p.lastIndex = index
		if oldIndex != 0 && reflect.DeepEqual(p.lastResult, result) {
			continue
		}

		// Handle the updated result
		p.lastResult = result
		if p.Handler != nil {
			p.Handler(index, result)
		}
	}
	return nil
}

// Stop is used to stop running the watch plan
func (p *WatchPlan) Stop() {
	p.stopLock.Lock()
	defer p.stopLock.Unlock()
	if p.stop {
		return
	}
	p.stop = true
	close(p.stopCh)
}

func (p *WatchPlan) shouldStop() bool {
	select {
	case <-p.stopCh:
		return true
	default:
		return false
	}
}
<commit_msg>reset the watch index when somehow the index goes backwards<commit_after>package watch

import (
	"fmt"
	"log"
	"os"
	"reflect"
	"time"

	consulapi "github.com/hashicorp/consul/api"
)

const (
	// retryInterval is the base retry value
	retryInterval = 5 * time.Second

	// maximum back off time, this is to prevent
	// exponential runaway
	maxBackoffTime = 180 * time.Second
)

// Run is used to run a watch plan
func (p *WatchPlan) Run(address string) error {
	// Setup the client
	p.address = address
	conf := consulapi.DefaultConfig()
	conf.Address = address
	conf.Datacenter = p.Datacenter
	conf.Token = p.Token
	client, err := consulapi.NewClient(conf)
	if err != nil {
		return fmt.Errorf("Failed to connect to agent: %v", err)
	}
	p.client = client

	// Create the logger
	output := p.LogOutput
	if output == nil {
		output = os.Stderr
	}
	logger := log.New(output, "", log.LstdFlags)

	// Loop until we are canceled
	failures := 0
OUTER:
	for !p.shouldStop() {
		// Invoke the handler
		index, result, err := p.Func(p)

		// Check if we should terminate since the function
		// could have blocked for a while
		if p.shouldStop() {
			break
		}

		// Handle an error in the watch function
		if err != nil {
			// Perform an exponential backoff
			failures++
			p.lastIndex = 0
			retry := retryInterval * time.Duration(failures*failures)
			if retry > maxBackoffTime {
				retry = maxBackoffTime
			}
			logger.Printf("consul.watch: Watch (type: %s) errored: %v, retry in %v",
				p.Type, err, retry)
			select {
			case <-time.After(retry):
				continue OUTER
			case <-p.stopCh:
				return nil
			}
		}

		// Clear the failures
		failures = 0

		// If the index is unchanged do nothing
		if index == p.lastIndex {
			continue
		}

		// Update the index, look for change
		oldIndex := p.lastIndex
		p.lastIndex = index
		if oldIndex != 0 && reflect.DeepEqual(p.lastResult, result) {
			continue
		}
		if p.lastIndex < oldIndex {
			p.lastIndex = 0
		}

		// Handle the updated result
		p.lastResult = result
		if p.Handler != nil {
			p.Handler(index, result)
		}
	}
	return nil
}

// Stop is used to stop running the watch plan
func (p *WatchPlan) Stop() {
	p.stopLock.Lock()
	defer p.stopLock.Unlock()
	if p.stop {
		return
	}
	p.stop = true
	close(p.stopCh)
}

func (p *WatchPlan) shouldStop() bool {
	select {
	case <-p.stopCh:
		return true
	default:
		return false
	}
}
<|endoftext|>


<commit_before>package main_test

import (
	"os"
	"os/exec"
	"path"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

func TestMain(t *testing.T) {
	RegisterFailHandler(Fail)
	dir, err := os.Getwd()
	Expect(err).NotTo(HaveOccurred())

	cmd := exec.Command("go", "build", "-o", path.Join(dir, "..", "fixtures", "plugins", "test"), path.Join(dir, "..", "fixtures", "plugins", "test.go"))
	err = cmd.Run()
	defer GinkgoRecover()
	Expect(err).NotTo(HaveOccurred())

	cmd = exec.Command("go", "build", "-o", path.Join(dir, "..", "fixtures", "plugins", "plugin2"), path.Join(dir, "..", "fixtures", "plugins", "plugin2.go"))
	err = cmd.Run()
	Expect(err).NotTo(HaveOccurred())

	RunSpecs(t, "Main Suite")
}
<commit_msg>Add debugging info to the main test<commit_after>package main_test

import (
	"os"
	"os/exec"
	"path"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

func TestMain(t *testing.T) {
	RegisterFailHandler(Fail)
	dir, err := os.Getwd()
	Expect(err).NotTo(HaveOccurred())

	cmd := exec.Command("go", "build", "-o", path.Join(dir, "..", "fixtures", "plugins", "test"), path.Join(dir, "..", "fixtures", "plugins", "test.go"))
	err = cmd.Run()
	if err != nil {
		println(err.Error())
	}
	Expect(err).NotTo(HaveOccurred())

	cmd = exec.Command("go", "build", "-o", path.Join(dir, "..", "fixtures", "plugins", "plugin2"), path.Join(dir, "..", "fixtures", "plugins", "plugin2.go"))
	err = cmd.Run()
	Expect(err).NotTo(HaveOccurred())

	RunSpecs(t, "Main Suite")
}
<|endoftext|>


<commit_before>package gateway

import (
	"errors"
	"net"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"

	"github.com/inconshreveable/muxado"
)

const (
	// the gateway will abort a connection attempt after this long
	dialTimeout = 2 * time.Minute
	// the gateway will sleep this long between incoming connections
	acceptInterval = 3 * time.Second
	// the gateway will not make outbound connections above this threshold
	wellConnectedThreshold = 8
	// the gateway will not accept inbound connections above this threshold
	fullyConnectedThreshold = 128
	// the gateway will ask for more addresses below this threshold
	minNodeListLen = 100
)

type peer struct {
	addr    modules.NetAddress
	sess    muxado.Session
	inbound bool
}

func (p *peer) open() (modules.PeerConn, error) {
	conn, err := p.sess.Open()
	if err != nil {
		return nil, err
	}
	return &peerConn{conn}, nil
}

func (p *peer) accept() (modules.PeerConn, error) {
	conn, err := p.sess.Accept()
	if err != nil {
		return nil, err
	}
	return &peerConn{conn}, nil
}

// addPeer adds a peer to the Gateway's peer list and spawns a listener thread
// to handle its requests.
func (g *Gateway) addPeer(p *peer) {
	g.peers[p.addr] = p
	go g.listenPeer(p)
}

// randomPeer returns a random peer from the gateway's peer list.
func (g *Gateway) randomPeer() (modules.NetAddress, error) {
	if len(g.peers) > 0 {
		r, _ := crypto.RandIntn(len(g.peers))
		for addr := range g.peers {
			if r <= 0 {
				return addr, nil
			}
			r--
		}
	}

	return "", errNoPeers
}

// randomInboundPeer returns a random peer that initiated its connection.
func (g *Gateway) randomInboundPeer() (modules.NetAddress, error) {
	if len(g.peers) > 0 {
		r, _ := crypto.RandIntn(len(g.peers))
		for addr, peer := range g.peers {
			// only select inbound peers
			if !peer.inbound {
				continue
			}
			if r <= 0 {
				return addr, nil
			}
			r--
		}
	}

	return "", errNoPeers
}

// listen handles incoming connection requests. If the connection is accepted,
// the peer will be added to the Gateway's peer list.
func (g *Gateway) listen() {
	for {
		conn, err := g.listener.Accept()
		if err != nil {
			return
		}

		go g.acceptConn(conn)

		// Sleep after each accept. This limits the rate at which the Gateway
		// will accept new connections. The intent here is to prevent new
		// incoming connections from kicking out old ones before they have a
		// chance to request additional nodes.
		time.Sleep(acceptInterval)
	}
}

// acceptConn adds a connecting node as a peer.
func (g *Gateway) acceptConn(conn net.Conn) {
	addr := modules.NetAddress(conn.RemoteAddr().String())
	g.log.Printf("INFO: %v wants to connect", addr)

	// read version
	var remoteVersion string
	if err := encoding.ReadObject(conn, &remoteVersion, maxAddrLength); err != nil {
		conn.Close()
		g.log.Printf("INFO: %v wanted to connect, but we could not read their version: %v", addr, err)
		return
	}

	// check that version is acceptable
	// NOTE: this version must be bumped whenever the gateway or consensus
	// breaks compatibility.
	if build.VersionCmp(remoteVersion, "0.3.3") < 0 {
		encoding.WriteObject(conn, "reject")
		conn.Close()
		g.log.Printf("INFO: %v wanted to connect, but their version (%v) was unacceptable", addr, remoteVersion)
		return
	}

	// respond with our version
	if err := encoding.WriteObject(conn, build.Version); err != nil {
		conn.Close()
		g.log.Printf("INFO: could not write version ack to %v: %v", addr, err)
		return
	}

	// If we are already fully connected, kick out an old peer to make room
	// for the new one. Importantly, prioritize kicking a peer with the same
	// IP as the connecting peer. This protects against Sybil attacks.
	id := g.mu.Lock()
	if len(g.peers) >= fullyConnectedThreshold {
		// first choose a random peer, preferably inbound. If have only
		// outbound peers, we'll wind up kicking an outbound peer; but
		// subsequent inbound connections will kick each other instead of
		// continuing to replace outbound peers.
		kick, err := g.randomInboundPeer()
		if err != nil {
			kick, _ = g.randomPeer()
		}
		// if another peer shares this IP, choose that one instead
		for p := range g.peers {
			if p.Host() == addr.Host() {
				kick = p
				break
			}
		}
		g.peers[kick].sess.Close()
		delete(g.peers, kick)
		g.log.Printf("INFO: disconnected from %v to make room for %v", kick, addr)
	}
	// add the peer
	g.addPeer(&peer{addr: addr, sess: muxado.Server(conn), inbound: true})
	g.mu.Unlock(id)

	g.log.Printf("INFO: accepted connection from new peer %v (v%v)", addr, remoteVersion)
}

// Connect establishes a persistent connection to a peer, and adds it to the
// Gateway's peer list.
func (g *Gateway) Connect(addr modules.NetAddress) error {
	if addr == g.Address() {
		return errors.New("can't connect to our own address")
	}
	if build.Release != "testing" && addr.IsLoopback() {
		return errors.New("can't connect to loopback address")
	}

	id := g.mu.RLock()
	_, exists := g.peers[addr]
	g.mu.RUnlock(id)
	if exists {
		return errors.New("peer already added")
	}

	conn, err := net.DialTimeout("tcp", string(addr), dialTimeout)
	if err != nil {
		return err
	}
	// send our version
	if err := encoding.WriteObject(conn, build.Version); err != nil {
		return err
	}
	// read version ack
	var remoteVersion string
	if err := encoding.ReadObject(conn, &remoteVersion, maxAddrLength); err != nil {
		return err
	}
	// decide whether to accept this version
	if remoteVersion == "reject" {
		return errors.New("peer rejected connection")
	}
	if build.VersionCmp(remoteVersion, "0.3.3") < 0 {
		conn.Close()
		return errors.New("unacceptable version: " + remoteVersion)
	}

	g.log.Println("INFO: connected to new peer", addr)

	id = g.mu.Lock()
	g.addPeer(&peer{addr: addr, sess: muxado.Client(conn), inbound: false})
	g.mu.Unlock(id)

	// call initRPCs
	id = g.mu.RLock()
	for name, fn := range g.initRPCs {
		go g.RPC(addr, name, fn)
	}
	g.mu.RUnlock(id)

	return nil
}

// Disconnect terminates a connection to a peer and removes it from the
// Gateway's peer list. The peer's address remains in the node list.
func (g *Gateway) Disconnect(addr modules.NetAddress) error {
	id := g.mu.RLock()
	p, exists := g.peers[addr]
	g.mu.RUnlock(id)
	if !exists {
		return errors.New("not connected to that node")
	}
	p.sess.Close()
	id = g.mu.Lock()
	delete(g.peers, addr)
	g.mu.Unlock(id)

	g.log.Println("INFO: disconnected from peer", addr)
	return nil
}

// threadedPeerManager tries to keep the Gateway well-connected. As long as
// the Gateway is not well-connected, it tries to connect to random nodes.
func (g *Gateway) threadedPeerManager() {
	for {
		// If we are well-connected, sleep in increments of five minutes until
		// we are no longer well-connected.
		id := g.mu.RLock()
		numOutboundPeers := 0
		for _, p := range g.peers {
			if !p.inbound {
				numOutboundPeers++
			}
		}
		addr, err := g.randomNode()
		g.mu.RUnlock(id)
		if numOutboundPeers >= wellConnectedThreshold {
			select {
			case <-time.After(5 * time.Minute):
			case <-g.closeChan:
				return
			}
			continue
		}

		// Try to connect to a random node. Instead of blocking on Connect, we
		// spawn a goroutine and sleep for five seconds. This allows us to
		// continue making connections if the node is unresponsive.
		if err == nil {
			go g.Connect(addr)
		}
		select {
		case <-time.After(5 * time.Second):
		case <-g.closeChan:
			return
		}
	}
}

// Peers returns the addresses currently connected to the Gateway.
func (g *Gateway) Peers() []modules.NetAddress {
	id := g.mu.RLock()
	defer g.mu.RUnlock(id)
	var peers []modules.NetAddress
	for addr := range g.peers {
		peers = append(peers, addr)
	}
	return peers
}
<commit_msg>Add version field to peer struct<commit_after>package gateway

import (
	"errors"
	"net"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"

	"github.com/inconshreveable/muxado"
)

const (
	// the gateway will abort a connection attempt after this long
	dialTimeout = 2 * time.Minute
	// the gateway will sleep this long between incoming connections
	acceptInterval = 3 * time.Second
	// the gateway will not make outbound connections above this threshold
	wellConnectedThreshold = 8
	// the gateway will not accept inbound connections above this threshold
	fullyConnectedThreshold = 128
	// the gateway will ask for more addresses below this threshold
	minNodeListLen = 100
)

type peer struct {
	addr    modules.NetAddress
	sess    muxado.Session
	inbound bool
	version string
}

func (p *peer) open() (modules.PeerConn, error) {
	conn, err := p.sess.Open()
	if err != nil {
		return nil, err
	}
	return &peerConn{conn}, nil
}

func (p *peer) accept() (modules.PeerConn, error) {
	conn, err := p.sess.Accept()
	if err != nil {
		return nil, err
	}
	return &peerConn{conn}, nil
}

// addPeer adds a peer to the Gateway's peer list and spawns a listener thread
// to handle its requests.
func (g *Gateway) addPeer(p *peer) {
	g.peers[p.addr] = p
	go g.listenPeer(p)
}

// randomPeer returns a random peer from the gateway's peer list.
func (g *Gateway) randomPeer() (modules.NetAddress, error) {
	if len(g.peers) > 0 {
		r, _ := crypto.RandIntn(len(g.peers))
		for addr := range g.peers {
			if r <= 0 {
				return addr, nil
			}
			r--
		}
	}

	return "", errNoPeers
}

// randomInboundPeer returns a random peer that initiated its connection.
func (g *Gateway) randomInboundPeer() (modules.NetAddress, error) {
	if len(g.peers) > 0 {
		r, _ := crypto.RandIntn(len(g.peers))
		for addr, peer := range g.peers {
			// only select inbound peers
			if !peer.inbound {
				continue
			}
			if r <= 0 {
				return addr, nil
			}
			r--
		}
	}

	return "", errNoPeers
}

// listen handles incoming connection requests. If the connection is accepted,
// the peer will be added to the Gateway's peer list.
func (g *Gateway) listen() {
	for {
		conn, err := g.listener.Accept()
		if err != nil {
			return
		}

		go g.acceptConn(conn)

		// Sleep after each accept. This limits the rate at which the Gateway
		// will accept new connections. The intent here is to prevent new
		// incoming connections from kicking out old ones before they have a
		// chance to request additional nodes.
		time.Sleep(acceptInterval)
	}
}

// acceptConn adds a connecting node as a peer.
func (g *Gateway) acceptConn(conn net.Conn) {
	addr := modules.NetAddress(conn.RemoteAddr().String())
	g.log.Printf("INFO: %v wants to connect", addr)

	// read version
	var remoteVersion string
	if err := encoding.ReadObject(conn, &remoteVersion, maxAddrLength); err != nil {
		conn.Close()
		g.log.Printf("INFO: %v wanted to connect, but we could not read their version: %v", addr, err)
		return
	}

	// check that version is acceptable
	// NOTE: this version must be bumped whenever the gateway or consensus
	// breaks compatibility.
	if build.VersionCmp(remoteVersion, "0.3.3") < 0 {
		encoding.WriteObject(conn, "reject")
		conn.Close()
		g.log.Printf("INFO: %v wanted to connect, but their version (%v) was unacceptable", addr, remoteVersion)
		return
	}

	// respond with our version
	if err := encoding.WriteObject(conn, build.Version); err != nil {
		conn.Close()
		g.log.Printf("INFO: could not write version ack to %v: %v", addr, err)
		return
	}

	// If we are already fully connected, kick out an old peer to make room
	// for the new one. Importantly, prioritize kicking a peer with the same
	// IP as the connecting peer. This protects against Sybil attacks.
	id := g.mu.Lock()
	if len(g.peers) >= fullyConnectedThreshold {
		// first choose a random peer, preferably inbound. If have only
		// outbound peers, we'll wind up kicking an outbound peer; but
		// subsequent inbound connections will kick each other instead of
		// continuing to replace outbound peers.
		kick, err := g.randomInboundPeer()
		if err != nil {
			kick, _ = g.randomPeer()
		}
		// if another peer shares this IP, choose that one instead
		for p := range g.peers {
			if p.Host() == addr.Host() {
				kick = p
				break
			}
		}
		g.peers[kick].sess.Close()
		delete(g.peers, kick)
		g.log.Printf("INFO: disconnected from %v to make room for %v", kick, addr)
	}
	// add the peer
	g.addPeer(&peer{
		addr:    addr,
		sess:    muxado.Server(conn),
		inbound: true,
		version: remoteVersion,
	})
	g.mu.Unlock(id)

	g.log.Printf("INFO: accepted connection from new peer %v (v%v)", addr, remoteVersion)
}

// Connect establishes a persistent connection to a peer, and adds it to the
// Gateway's peer list.
func (g *Gateway) Connect(addr modules.NetAddress) error {
	if addr == g.Address() {
		return errors.New("can't connect to our own address")
	}
	if build.Release != "testing" && addr.IsLoopback() {
		return errors.New("can't connect to loopback address")
	}

	id := g.mu.RLock()
	_, exists := g.peers[addr]
	g.mu.RUnlock(id)
	if exists {
		return errors.New("peer already added")
	}

	conn, err := net.DialTimeout("tcp", string(addr), dialTimeout)
	if err != nil {
		return err
	}
	// send our version
	if err := encoding.WriteObject(conn, build.Version); err != nil {
		return err
	}
	// read version ack
	var remoteVersion string
	if err := encoding.ReadObject(conn, &remoteVersion, maxAddrLength); err != nil {
		return err
	}
	// decide whether to accept this version
	if remoteVersion == "reject" {
		return errors.New("peer rejected connection")
	}
	if build.VersionCmp(remoteVersion, "0.3.3") < 0 {
		conn.Close()
		return errors.New("unacceptable version: " + remoteVersion)
	}

	g.log.Println("INFO: connected to new peer", addr)

	id = g.mu.Lock()
	g.addPeer(&peer{
		addr:    addr,
		sess:    muxado.Client(conn),
		inbound: false,
		version: remoteVersion,
	})
	g.mu.Unlock(id)

	// call initRPCs
	id = g.mu.RLock()
	for name, fn := range g.initRPCs {
		go g.RPC(addr, name, fn)
	}
	g.mu.RUnlock(id)

	return nil
}

// Disconnect terminates a connection to a peer and removes it from the
// Gateway's peer list. The peer's address remains in the node list.
func (g *Gateway) Disconnect(addr modules.NetAddress) error {
	id := g.mu.RLock()
	p, exists := g.peers[addr]
	g.mu.RUnlock(id)
	if !exists {
		return errors.New("not connected to that node")
	}
	p.sess.Close()
	id = g.mu.Lock()
	delete(g.peers, addr)
	g.mu.Unlock(id)

	g.log.Println("INFO: disconnected from peer", addr)
	return nil
}

// threadedPeerManager tries to keep the Gateway well-connected. As long as
// the Gateway is not well-connected, it tries to connect to random nodes.
func (g *Gateway) threadedPeerManager() {
	for {
		// If we are well-connected, sleep in increments of five minutes until
		// we are no longer well-connected.
		id := g.mu.RLock()
		numOutboundPeers := 0
		for _, p := range g.peers {
			if !p.inbound {
				numOutboundPeers++
			}
		}
		addr, err := g.randomNode()
		g.mu.RUnlock(id)
		if numOutboundPeers >= wellConnectedThreshold {
			select {
			case <-time.After(5 * time.Minute):
			case <-g.closeChan:
				return
			}
			continue
		}

		// Try to connect to a random node. Instead of blocking on Connect, we
		// spawn a goroutine and sleep for five seconds. This allows us to
		// continue making connections if the node is unresponsive.
		if err == nil {
			go g.Connect(addr)
		}
		select {
		case <-time.After(5 * time.Second):
		case <-g.closeChan:
			return
		}
	}
}

// Peers returns the addresses currently connected to the Gateway.
func (g *Gateway) Peers() []modules.NetAddress {
	id := g.mu.RLock()
	defer g.mu.RUnlock(id)
	var peers []modules.NetAddress
	for addr := range g.peers {
		peers = append(peers, addr)
	}
	return peers
}
<|endoftext|>


<commit_before>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author: polaris	polaris@studygolang.com

package controller

import (
	"errors"
	"http/middleware"
	"logic"
	"model"
	"net/http"
	"strconv"

	. "http"

	"github.com/labstack/echo"
	"github.com/polaris1119/goutils"
	"github.com/polaris1119/slices"
)

type CommentController struct{}

func (self CommentController) RegisterRoute(g *echo.Group) {
	g.Get("/at/users", self.AtUsers)
	g.Post("/comment/:objid", self.Create, middleware.NeedLogin(), middleware.Sensivite(), middleware.BalanceCheck(), middleware.PublishNotice())
	g.Get("/object/comments", self.CommentList)

	g.Get("/topics/:objid/comment/:cid", self.TopicDetail)
	g.Get("/articles/:objid/comment/:cid", self.ArticleDetail)
}

// AtUsers 评论或回复 @ 某人 suggest
func (CommentController) AtUsers(ctx echo.Context) error {
	term := ctx.QueryParam("term")
	isHttps := CheckIsHttps(ctx)
	users := logic.DefaultUser.GetUserMentions(term, 10, isHttps)
	return ctx.JSON(http.StatusOK, users)
}

// Create 评论（或回复）
func (CommentController) Create(ctx echo.Context) error {
	user := ctx.Get("user").(*model.Me)

	// 入库
	objid := goutils.MustInt(ctx.Param("objid"))
	if objid == 0 {
		return fail(ctx, 1, "参数有误，请刷新后重试！")
	}
	comment, err := logic.DefaultComment.Publish(ctx, user.Uid, objid, ctx.FormParams())
	if err != nil {
		return fail(ctx, 2, "服务器内部错误")
	}

	return success(ctx, comment)
}

// CommentList 获取某对象的评论信息
func (CommentController) CommentList(ctx echo.Context) error {
	objid := goutils.MustInt(ctx.QueryParam("objid"))
	objtype := goutils.MustInt(ctx.QueryParam("objtype"))

	commentList, err := logic.DefaultComment.FindObjectComments(ctx, objid, objtype)
	if err != nil {
		return fail(ctx, 1, "服务器内部错误")
	}

	uids := slices.StructsIntSlice(commentList, "Uid")
	users := logic.DefaultUser.FindUserInfos(ctx, uids)

	result := map[string]interface{}{
		"comments": commentList,
	}

	// json encode 不支持 map[int]...
	for uid, user := range users {
		result[strconv.Itoa(uid)] = user
	}

	return success(ctx, result)
}

func (self CommentController) TopicDetail(ctx echo.Context) error {
	objid := goutils.MustInt(ctx.Param("objid"))
	cid := goutils.MustInt(ctx.Param("cid"))

	topicMaps := logic.DefaultTopic.FindFullinfoByTids([]int{objid})
	if len(topicMaps) < 1 {
		return ctx.Redirect(http.StatusSeeOther, "/topics")
	}

	topic := topicMaps[0]
	topic["node"] = logic.GetNode(topic["nid"].(int))

	data := map[string]interface{}{
		"topic": topic,
	}
	data["appends"] = logic.DefaultTopic.FindAppend(ctx, objid)

	err := self.fillCommentAndUser(ctx, data, cid, objid, model.TypeTopic)

	if err != nil {
		return ctx.Redirect(http.StatusSeeOther, "/topics/"+strconv.Itoa(objid))
	}

	return render(ctx, "topics/comment.html", data)
}

func (self CommentController) ArticleDetail(ctx echo.Context) error {
	objid := goutils.MustInt(ctx.Param("objid"))
	cid := goutils.MustInt(ctx.Param("cid"))

	article, err := logic.DefaultArticle.FindById(ctx, objid)
	if err != nil {
		return ctx.Redirect(http.StatusSeeOther, "/articles")
	}
	articleGCTT := logic.DefaultArticle.FindArticleGCTT(ctx, article)

	data := map[string]interface{}{
		"article":      article,
		"article_gctt": articleGCTT,
	}

	err = self.fillCommentAndUser(ctx, data, cid, objid, model.TypeArticle)

	if err != nil {
		return ctx.Redirect(http.StatusSeeOther, "/articles/"+strconv.Itoa(objid))
	}

	return render(ctx, "articles/comment.html", data)
}

func (CommentController) fillCommentAndUser(ctx echo.Context, data map[string]interface{}, cid, objid, objtype int) error {
	comment, comments := logic.DefaultComment.FindComment(ctx, cid, objid, model.TypeArticle)

	if comment.Cid == 0 {
		return errors.New("comment not exists!")
	}

	uids := make([]int, 1+len(comments))
	uids[0] = comment.Uid
	for i, comment := range comments {
		uids[i+1] = comment.Uid
	}
	users := logic.DefaultUser.FindUserInfos(ctx, uids)

	data["comment"] = comment
	data["comments"] = comments
	data["users"] = users

	return nil
}
<commit_msg>bugfix<commit_after>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author: polaris	polaris@studygolang.com

package controller

import (
	"errors"
	"http/middleware"
	"logic"
	"model"
	"net/http"
	"strconv"

	. "http"

	"github.com/labstack/echo"
	"github.com/polaris1119/goutils"
	"github.com/polaris1119/slices"
)

type CommentController struct{}

func (self CommentController) RegisterRoute(g *echo.Group) {
	g.Get("/at/users", self.AtUsers)
	g.Post("/comment/:objid", self.Create, middleware.NeedLogin(), middleware.Sensivite(), middleware.BalanceCheck(), middleware.PublishNotice())
	g.Get("/object/comments", self.CommentList)

	g.Get("/topics/:objid/comment/:cid", self.TopicDetail)
	g.Get("/articles/:objid/comment/:cid", self.ArticleDetail)
}

// AtUsers 评论或回复 @ 某人 suggest
func (CommentController) AtUsers(ctx echo.Context) error {
	term := ctx.QueryParam("term")
	isHttps := CheckIsHttps(ctx)
	users := logic.DefaultUser.GetUserMentions(term, 10, isHttps)
	return ctx.JSON(http.StatusOK, users)
}

// Create 评论（或回复）
func (CommentController) Create(ctx echo.Context) error {
	user := ctx.Get("user").(*model.Me)

	// 入库
	objid := goutils.MustInt(ctx.Param("objid"))
	if objid == 0 {
		return fail(ctx, 1, "参数有误，请刷新后重试！")
	}
	comment, err := logic.DefaultComment.Publish(ctx, user.Uid, objid, ctx.FormParams())
	if err != nil {
		return fail(ctx, 2, "服务器内部错误")
	}

	return success(ctx, comment)
}

// CommentList 获取某对象的评论信息
func (CommentController) CommentList(ctx echo.Context) error {
	objid := goutils.MustInt(ctx.QueryParam("objid"))
	objtype := goutils.MustInt(ctx.QueryParam("objtype"))

	commentList, err := logic.DefaultComment.FindObjectComments(ctx, objid, objtype)
	if err != nil {
		return fail(ctx, 1, "服务器内部错误")
	}

	uids := slices.StructsIntSlice(commentList, "Uid")
	users := logic.DefaultUser.FindUserInfos(ctx, uids)

	result := map[string]interface{}{
		"comments": commentList,
	}

	// json encode 不支持 map[int]...
	for uid, user := range users {
		result[strconv.Itoa(uid)] = user
	}

	return success(ctx, result)
}

func (self CommentController) TopicDetail(ctx echo.Context) error {
	objid := goutils.MustInt(ctx.Param("objid"))
	cid := goutils.MustInt(ctx.Param("cid"))

	topicMaps := logic.DefaultTopic.FindFullinfoByTids([]int{objid})
	if len(topicMaps) < 1 {
		return ctx.Redirect(http.StatusSeeOther, "/topics")
	}

	topic := topicMaps[0]
	topic["node"] = logic.GetNode(topic["nid"].(int))

	data := map[string]interface{}{
		"topic": topic,
	}
	data["appends"] = logic.DefaultTopic.FindAppend(ctx, objid)

	err := self.fillCommentAndUser(ctx, data, cid, objid, model.TypeTopic)

	if err != nil {
		return ctx.Redirect(http.StatusSeeOther, "/topics/"+strconv.Itoa(objid))
	}

	return render(ctx, "topics/comment.html", data)
}

func (self CommentController) ArticleDetail(ctx echo.Context) error {
	objid := goutils.MustInt(ctx.Param("objid"))
	cid := goutils.MustInt(ctx.Param("cid"))

	article, err := logic.DefaultArticle.FindById(ctx, objid)
	if err != nil {
		return ctx.Redirect(http.StatusSeeOther, "/articles")
	}
	articleGCTT := logic.DefaultArticle.FindArticleGCTT(ctx, article)

	data := map[string]interface{}{
		"article":      article,
		"article_gctt": articleGCTT,
	}

	err = self.fillCommentAndUser(ctx, data, cid, objid, model.TypeArticle)

	if err != nil {
		return ctx.Redirect(http.StatusSeeOther, "/articles/"+strconv.Itoa(objid))
	}

	return render(ctx, "articles/comment.html", data)
}

func (CommentController) fillCommentAndUser(ctx echo.Context, data map[string]interface{}, cid, objid, objtype int) error {
	comment, comments := logic.DefaultComment.FindComment(ctx, cid, objid, objtype)

	if comment.Cid == 0 {
		return errors.New("comment not exists!")
	}

	uids := make([]int, 1+len(comments))
	uids[0] = comment.Uid
	for i, comment := range comments {
		uids[i+1] = comment.Uid
	}
	users := logic.DefaultUser.FindUserInfos(ctx, uids)

	data["comment"] = comment
	data["comments"] = comments
	data["users"] = users

	return nil
}
<|endoftext|>


<commit_before>package plugins

import (
	"errors"
	"strings"

	"time"

	"sync"

	"fmt"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/Seklfreak/Robyul2/models"
	"github.com/Sirupsen/logrus"
	"github.com/bwmarrin/discordgo"
	rethink "github.com/gorethink/gorethink"
)

type namesAction func(args []string, in *discordgo.Message, out **discordgo.MessageSend) (next namesAction)

type Names struct{}

var (
	previousNicknames      map[string]map[string]string
	previousNicknamesMutex sync.RWMutex
	previousUsernames      map[string]string
	previousUsernamesMutex sync.RWMutex
)

func (n *Names) Commands() []string {
	return []string{
		"names",
		"nicknames",
	}
}

func (n *Names) Init(session *discordgo.Session) {
	previousNicknamesMutex.Lock()
	previousNicknames = make(map[string]map[string]string, 0)
	previousNicknamesMutex.Unlock()
	previousUsernamesMutex.Lock()
	previousUsernames = make(map[string]string, 0)
	previousUsernamesMutex.Unlock()
	session.AddHandler(n.OnGuildMemberListChunk)
	session.AddHandler(n.OnPresenceUpdate)
	session.AddHandler(n.OnGuildMemberUpdate)
}

func (n *Names) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	defer helpers.Recover()

	session.ChannelTyping(msg.ChannelID)

	var result *discordgo.MessageSend
	args := strings.Fields(content)

	action := n.actionStart
	for action != nil {
		action = action(args, msg, &result)
	}
}

func (n *Names) actionStart(args []string, in *discordgo.Message, out **discordgo.MessageSend) namesAction {
	cache.GetSession().ChannelTyping(in.ChannelID)

	if len(args) < 1 {
		*out = n.newMsg(helpers.GetText("bot.arguments.too-few"))
		return n.actionFinish
	}

	return n.actionNames
}

func (n *Names) actionNames(args []string, in *discordgo.Message, out **discordgo.MessageSend) namesAction {
	user, err := helpers.GetUserFromMention(args[0])
	if err != nil || user == nil || user.ID == "" {
		*out = n.newMsg(helpers.GetText("bot.arguments.invalid"))
		return n.actionFinish
	}
	channel, err := helpers.GetChannel(in.ChannelID)
	helpers.Relax(err)
	member, err := helpers.GetGuildMember(channel.GuildID, user.ID)
	if err != nil || user == nil || user.ID == "" {
		*out = n.newMsg(helpers.GetText("bot.arguments.invalid"))
		return n.actionFinish
	}

	var pastUsernamesText, pastNicknamesText string

	pastUsernames, err := n.GetUsernames(member.User.ID)
	if err != nil && strings.Contains(err.Error(), "no username entries") {
		helpers.Relax(err)
	}

	if len(pastUsernames) <= 0 || pastUsernames[len(pastUsernames)-1] != member.User.Username+"#"+member.User.Discriminator {
		pastUsernames = append(pastUsernames, member.User.Username+"#"+member.User.Discriminator)
	}

	for i, pastUsername := range pastUsernames {
		pastUsernamesText += "`" + pastUsername + "`"
		if i < len(pastUsernames)-1 {
			pastUsernamesText += ", "
		}
		if i == len(pastUsernames) {
			pastUsernamesText += " and "
		}
	}

	if pastUsernamesText == "" {
		pastUsernamesText = "None"
	}

	pastNicknames, err := n.GetNicknames(member.GuildID, member.User.ID)
	if err != nil && strings.Contains(err.Error(), "no nickname entries") {
		helpers.Relax(err)
	}

	if member.Nick != "" {
		if len(pastNicknames) <= 0 || pastNicknames[len(pastNicknames)-1] != member.Nick {
			pastNicknames = append(pastNicknames, member.Nick)
		}
	}

	for i, pastNickname := range pastNicknames {
		pastNicknamesText += "`" + pastNickname + "`"
		if i < len(pastNicknames)-1 {
			pastNicknamesText += ", "
		}
		if i == len(pastNicknames) {
			pastNicknamesText += " and "
		}
	}

	if pastNicknamesText == "" {
		pastNicknamesText = "None"
	}

	resultText := helpers.GetTextF("plugins.names.list-result",
		member.User.Username, member.User.Discriminator, member.User.ID, pastUsernamesText, pastNicknamesText)
	for _, page := range helpers.Pagify(resultText, ",") {
		_, err := cache.GetSession().ChannelMessageSend(in.ChannelID, page)
		helpers.RelaxMessage(err, in.ChannelID, in.ID)
	}

	return nil
}

func (n *Names) OnPresenceUpdate(session *discordgo.Session, presence *discordgo.PresenceUpdate) {
	if presence.GuildID == "" || presence.User == nil || presence.User.ID == "" {
		return
	}

	go func() {
		defer helpers.Recover()
		if presence.Presence.User.Username != "" {
			err := n.UpdateUsername(presence.Presence.User.ID, presence.Presence.User.Username+"#"+presence.Presence.User.Discriminator)
			helpers.Relax(err)
		}
	}()
}

func (n *Names) OnGuildMemberUpdate(session *discordgo.Session, member *discordgo.GuildMemberUpdate) {
	if member.Member == nil {
		return
	}

	go func() {
		defer helpers.Recover()

		if member.Member.Nick != "" {
			err := n.UpdateNickname(member.Member.GuildID, member.Member.User.ID, member.Member.Nick)
			helpers.Relax(err)
		}
	}()
}

func (n *Names) UpdateNickname(guildID string, userID string, newNick string) (err error) {
	previousNicknamesMutex.Lock()
	defer previousNicknamesMutex.Unlock()
	var oldNick string
	if previousNicknames[guildID] == nil {
		previousNicknames[guildID] = make(map[string]string, 0)
	}
	oldNick, _ = previousNicknames[guildID][userID]

	lastSavedNickname, err := n.GetLastNickname(guildID, userID)
	if err != nil && !strings.Contains(err.Error(), "no nickname entry") {
		helpers.RelaxLog(err)
	}

	if oldNick != "" && lastSavedNickname != oldNick {
		err = n.SaveNickname(guildID, userID, oldNick)
		helpers.RelaxLog(err)
		n.logger().WithField("guildID", guildID).WithField("userID", userID).Debug(
			"saved old nickname: ", oldNick) // TODO
		lastSavedNickname = oldNick
	}

	if lastSavedNickname != newNick {
		err = n.SaveNickname(guildID, userID, newNick)
		n.logger().WithField("guildID", guildID).WithField("userID", userID).Debug(
			"saved new nickname: ", newNick) // TODO
		previousNicknames[guildID][userID] = newNick
		return err
	}
	return nil
}

func (n *Names) UpdateUsername(userID string, newUsername string) (err error) {
	previousUsernamesMutex.Lock()
	defer previousUsernamesMutex.Unlock()
	var oldUsername string
	oldUsername, _ = previousUsernames[userID]

	lastSavedUsername, err := n.GetLastUsername(userID)
	if err != nil && !strings.Contains(err.Error(), "no username entry") {
		helpers.RelaxLog(err)
	}
	fmt.Println("lastSavedUsername:", lastSavedUsername, "oldUsername:", oldUsername)

	if oldUsername != "" && lastSavedUsername != oldUsername {
		err = n.SaveUsername(userID, oldUsername)
		helpers.RelaxLog(err)
		n.logger().WithField("userID", userID).Debug(
			"saved old username: ", oldUsername) // TODO
		lastSavedUsername = oldUsername
	}

	if lastSavedUsername != newUsername {
		err = n.SaveUsername(userID, newUsername)
		n.logger().WithField("userID", userID).Debug(
			"saved new username: ", newUsername) // TODO
		previousUsernames[userID] = newUsername
		return err
	}
	return nil
}

func (n *Names) GetLastNickname(guildID string, userID string) (nickname string, err error) {
	var entryBucket models.NamesEntry
	listCursor, err := rethink.Table(models.NamesTable).GetAllByIndex(
		"user_id", userID,
	).Filter(
		rethink.Row.Field("guild_id").Eq(guildID),
	).OrderBy(rethink.Desc("changed_at")).Limit(1).Run(helpers.GetDB())
	if err != nil {
		return "", err
	}
	defer listCursor.Close()
	err = listCursor.One(&entryBucket)

	if err == rethink.ErrEmptyResult {
		return "", errors.New("no nickname entry")
	} else if err != nil {
		return "", err
	}
	return entryBucket.Nickname, nil
}

func (n *Names) SaveNickname(guildID string, userID string, nickname string) (err error) {
	insert := rethink.Table(models.NamesTable).Insert(models.NamesEntry{
		ChangedAt: time.Now(),
		GuildID:   guildID,
		UserID:    userID,
		Nickname:  nickname,
		Username:  "",
	})
	_, err = insert.RunWrite(helpers.GetDB())
	return err
}

func (n *Names) GetLastUsername(userID string) (username string, err error) {
	var entryBucket models.NamesEntry
	listCursor, err := rethink.Table(models.NamesTable).GetAllByIndex(
		"user_id", userID,
	).Filter(
		rethink.Row.Field("guild_id").Eq("global"),
	).OrderBy(rethink.Desc("changed_at")).Limit(1).Run(helpers.GetDB())
	if err != nil {
		return "", err
	}
	defer listCursor.Close()
	err = listCursor.One(&entryBucket)

	if err == rethink.ErrEmptyResult {
		return "", errors.New("no username entry")
	} else if err != nil {
		return "", err
	}
	return entryBucket.Username, nil
}

func (n *Names) GetNicknames(guildID string, userID string) (nicknames []string, err error) {
	var entryBucket []models.NamesEntry
	listCursor, err := rethink.Table(models.NamesTable).GetAllByIndex(
		"user_id", userID,
	).Filter(
		rethink.Row.Field("guild_id").Eq(guildID),
	).OrderBy(rethink.Asc("changed_at")).Run(helpers.GetDB())
	if err != nil {
		return nicknames, err
	}
	defer listCursor.Close()
	err = listCursor.All(&entryBucket)

	if err == rethink.ErrEmptyResult {
		return nicknames, errors.New("no nickname entries")
	} else if err != nil {
		return nicknames, err
	}
	for _, entry := range entryBucket {
		nicknames = append(nicknames, entry.Nickname)
	}
	return nicknames, nil
}

func (n *Names) GetUsernames(userID string) (usernames []string, err error) {
	var entryBucket []models.NamesEntry
	listCursor, err := rethink.Table(models.NamesTable).GetAllByIndex(
		"user_id", userID,
	).Filter(
		rethink.Row.Field("guild_id").Eq("global"),
	).OrderBy(rethink.Asc("changed_at")).Run(helpers.GetDB())
	if err != nil {
		return usernames, err
	}
	defer listCursor.Close()
	err = listCursor.All(&entryBucket)

	if err == rethink.ErrEmptyResult {
		return usernames, errors.New("no username entries")
	} else if err != nil {
		return usernames, err
	}
	for _, entry := range entryBucket {
		usernames = append(usernames, entry.Username)
	}
	return usernames, nil
}

func (n *Names) SaveUsername(userID string, username string) (err error) {
	insert := rethink.Table(models.NamesTable).Insert(models.NamesEntry{
		ChangedAt: time.Now(),
		GuildID:   "global",
		UserID:    userID,
		Nickname:  "",
		Username:  username,
	})
	_, err = insert.RunWrite(helpers.GetDB())
	return err
}

func (n *Names) OnGuildMemberListChunk(session *discordgo.Session, members *discordgo.GuildMembersChunk) {
	previousUsernamesMutex.Lock()
	previousNicknamesMutex.Lock()
	defer previousUsernamesMutex.Unlock()
	defer previousNicknamesMutex.Unlock()
	for _, member := range members.Members {
		previousUsernames[member.User.ID] = member.User.Username + "#" + member.User.Discriminator
		if member.Nick != "" {
			if previousNicknames[members.GuildID] == nil {
				previousNicknames[members.GuildID] = make(map[string]string, 0)
			}
			previousNicknames[members.GuildID][member.User.ID] = member.Nick
		}
	}
}

func (n *Names) actionFinish(args []string, in *discordgo.Message, out **discordgo.MessageSend) namesAction {
	_, err := cache.GetSession().ChannelMessageSendComplex(in.ChannelID, *out)
	helpers.Relax(err)

	return nil
}

func (n *Names) newMsg(content string) *discordgo.MessageSend {
	return &discordgo.MessageSend{Content: helpers.GetText(content)}
}

func (n *Names) Relax(err error) {
	if err != nil {
		panic(err)
	}
}

func (n *Names) logger() *logrus.Entry {
	return cache.GetLogger().WithField("module", "names")
}
<commit_msg>[names] reduces logging<commit_after>package plugins

import (
	"errors"
	"strings"

	"time"

	"sync"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/Seklfreak/Robyul2/models"
	"github.com/Sirupsen/logrus"
	"github.com/bwmarrin/discordgo"
	rethink "github.com/gorethink/gorethink"
)

type namesAction func(args []string, in *discordgo.Message, out **discordgo.MessageSend) (next namesAction)

type Names struct{}

var (
	previousNicknames      map[string]map[string]string
	previousNicknamesMutex sync.RWMutex
	previousUsernames      map[string]string
	previousUsernamesMutex sync.RWMutex
)

func (n *Names) Commands() []string {
	return []string{
		"names",
		"nicknames",
	}
}

func (n *Names) Init(session *discordgo.Session) {
	previousNicknamesMutex.Lock()
	previousNicknames = make(map[string]map[string]string, 0)
	previousNicknamesMutex.Unlock()
	previousUsernamesMutex.Lock()
	previousUsernames = make(map[string]string, 0)
	previousUsernamesMutex.Unlock()
	session.AddHandler(n.OnGuildMemberListChunk)
	session.AddHandler(n.OnPresenceUpdate)
	session.AddHandler(n.OnGuildMemberUpdate)
}

func (n *Names) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	defer helpers.Recover()

	session.ChannelTyping(msg.ChannelID)

	var result *discordgo.MessageSend
	args := strings.Fields(content)

	action := n.actionStart
	for action != nil {
		action = action(args, msg, &result)
	}
}

func (n *Names) actionStart(args []string, in *discordgo.Message, out **discordgo.MessageSend) namesAction {
	cache.GetSession().ChannelTyping(in.ChannelID)

	if len(args) < 1 {
		*out = n.newMsg(helpers.GetText("bot.arguments.too-few"))
		return n.actionFinish
	}

	return n.actionNames
}

func (n *Names) actionNames(args []string, in *discordgo.Message, out **discordgo.MessageSend) namesAction {
	user, err := helpers.GetUserFromMention(args[0])
	if err != nil || user == nil || user.ID == "" {
		*out = n.newMsg(helpers.GetText("bot.arguments.invalid"))
		return n.actionFinish
	}
	channel, err := helpers.GetChannel(in.ChannelID)
	helpers.Relax(err)
	member, err := helpers.GetGuildMember(channel.GuildID, user.ID)
	if err != nil || user == nil || user.ID == "" {
		*out = n.newMsg(helpers.GetText("bot.arguments.invalid"))
		return n.actionFinish
	}

	var pastUsernamesText, pastNicknamesText string

	pastUsernames, err := n.GetUsernames(member.User.ID)
	if err != nil && strings.Contains(err.Error(), "no username entries") {
		helpers.Relax(err)
	}

	if len(pastUsernames) <= 0 || pastUsernames[len(pastUsernames)-1] != member.User.Username+"#"+member.User.Discriminator {
		pastUsernames = append(pastUsernames, member.User.Username+"#"+member.User.Discriminator)
	}

	for i, pastUsername := range pastUsernames {
		pastUsernamesText += "`" + pastUsername + "`"
		if i < len(pastUsernames)-1 {
			pastUsernamesText += ", "
		}
		if i == len(pastUsernames) {
			pastUsernamesText += " and "
		}
	}

	if pastUsernamesText == "" {
		pastUsernamesText = "None"
	}

	pastNicknames, err := n.GetNicknames(member.GuildID, member.User.ID)
	if err != nil && strings.Contains(err.Error(), "no nickname entries") {
		helpers.Relax(err)
	}

	if member.Nick != "" {
		if len(pastNicknames) <= 0 || pastNicknames[len(pastNicknames)-1] != member.Nick {
			pastNicknames = append(pastNicknames, member.Nick)
		}
	}

	for i, pastNickname := range pastNicknames {
		pastNicknamesText += "`" + pastNickname + "`"
		if i < len(pastNicknames)-1 {
			pastNicknamesText += ", "
		}
		if i == len(pastNicknames) {
			pastNicknamesText += " and "
		}
	}

	if pastNicknamesText == "" {
		pastNicknamesText = "None"
	}

	resultText := helpers.GetTextF("plugins.names.list-result",
		member.User.Username, member.User.Discriminator, member.User.ID, pastUsernamesText, pastNicknamesText)
	for _, page := range helpers.Pagify(resultText, ",") {
		_, err := cache.GetSession().ChannelMessageSend(in.ChannelID, page)
		helpers.RelaxMessage(err, in.ChannelID, in.ID)
	}

	return nil
}

func (n *Names) OnPresenceUpdate(session *discordgo.Session, presence *discordgo.PresenceUpdate) {
	if presence.GuildID == "" || presence.User == nil || presence.User.ID == "" {
		return
	}

	go func() {
		defer helpers.Recover()
		if presence.Presence.User.Username != "" {
			err := n.UpdateUsername(presence.Presence.User.ID, presence.Presence.User.Username+"#"+presence.Presence.User.Discriminator)
			helpers.Relax(err)
		}
	}()
}

func (n *Names) OnGuildMemberUpdate(session *discordgo.Session, member *discordgo.GuildMemberUpdate) {
	if member.Member == nil {
		return
	}

	go func() {
		defer helpers.Recover()

		if member.Member.Nick != "" {
			err := n.UpdateNickname(member.Member.GuildID, member.Member.User.ID, member.Member.Nick)
			helpers.Relax(err)
		}
	}()
}

func (n *Names) UpdateNickname(guildID string, userID string, newNick string) (err error) {
	previousNicknamesMutex.Lock()
	defer previousNicknamesMutex.Unlock()
	var oldNick string
	if previousNicknames[guildID] == nil {
		previousNicknames[guildID] = make(map[string]string, 0)
	}
	oldNick, _ = previousNicknames[guildID][userID]

	lastSavedNickname, err := n.GetLastNickname(guildID, userID)
	if err != nil && !strings.Contains(err.Error(), "no nickname entry") {
		helpers.RelaxLog(err)
	}

	if oldNick != "" && lastSavedNickname != oldNick {
		err = n.SaveNickname(guildID, userID, oldNick)
		helpers.RelaxLog(err)
		n.logger().WithField("guildID", guildID).WithField("userID", userID).Debug(
			"saved old nickname: ", oldNick) // TODO
		lastSavedNickname = oldNick
	}

	if lastSavedNickname != newNick {
		err = n.SaveNickname(guildID, userID, newNick)
		n.logger().WithField("guildID", guildID).WithField("userID", userID).Debug(
			"saved new nickname: ", newNick) // TODO
		previousNicknames[guildID][userID] = newNick
		return err
	}
	return nil
}

func (n *Names) UpdateUsername(userID string, newUsername string) (err error) {
	previousUsernamesMutex.Lock()
	defer previousUsernamesMutex.Unlock()
	var oldUsername string
	oldUsername, _ = previousUsernames[userID]

	lastSavedUsername, err := n.GetLastUsername(userID)
	if err != nil && !strings.Contains(err.Error(), "no username entry") {
		helpers.RelaxLog(err)
	}

	if oldUsername != "" && lastSavedUsername != oldUsername {
		err = n.SaveUsername(userID, oldUsername)
		helpers.RelaxLog(err)
		n.logger().WithField("userID", userID).Debug(
			"saved old username: ", oldUsername) // TODO
		lastSavedUsername = oldUsername
	}

	if lastSavedUsername != newUsername {
		err = n.SaveUsername(userID, newUsername)
		n.logger().WithField("userID", userID).Debug(
			"saved new username: ", newUsername) // TODO
		previousUsernames[userID] = newUsername
		return err
	}
	return nil
}

func (n *Names) GetLastNickname(guildID string, userID string) (nickname string, err error) {
	var entryBucket models.NamesEntry
	listCursor, err := rethink.Table(models.NamesTable).GetAllByIndex(
		"user_id", userID,
	).Filter(
		rethink.Row.Field("guild_id").Eq(guildID),
	).OrderBy(rethink.Desc("changed_at")).Limit(1).Run(helpers.GetDB())
	if err != nil {
		return "", err
	}
	defer listCursor.Close()
	err = listCursor.One(&entryBucket)

	if err == rethink.ErrEmptyResult {
		return "", errors.New("no nickname entry")
	} else if err != nil {
		return "", err
	}
	return entryBucket.Nickname, nil
}

func (n *Names) SaveNickname(guildID string, userID string, nickname string) (err error) {
	insert := rethink.Table(models.NamesTable).Insert(models.NamesEntry{
		ChangedAt: time.Now(),
		GuildID:   guildID,
		UserID:    userID,
		Nickname:  nickname,
		Username:  "",
	})
	_, err = insert.RunWrite(helpers.GetDB())
	return err
}

func (n *Names) GetLastUsername(userID string) (username string, err error) {
	var entryBucket models.NamesEntry
	listCursor, err := rethink.Table(models.NamesTable).GetAllByIndex(
		"user_id", userID,
	).Filter(
		rethink.Row.Field("guild_id").Eq("global"),
	).OrderBy(rethink.Desc("changed_at")).Limit(1).Run(helpers.GetDB())
	if err != nil {
		return "", err
	}
	defer listCursor.Close()
	err = listCursor.One(&entryBucket)

	if err == rethink.ErrEmptyResult {
		return "", errors.New("no username entry")
	} else if err != nil {
		return "", err
	}
	return entryBucket.Username, nil
}

func (n *Names) GetNicknames(guildID string, userID string) (nicknames []string, err error) {
	var entryBucket []models.NamesEntry
	listCursor, err := rethink.Table(models.NamesTable).GetAllByIndex(
		"user_id", userID,
	).Filter(
		rethink.Row.Field("guild_id").Eq(guildID),
	).OrderBy(rethink.Asc("changed_at")).Run(helpers.GetDB())
	if err != nil {
		return nicknames, err
	}
	defer listCursor.Close()
	err = listCursor.All(&entryBucket)

	if err == rethink.ErrEmptyResult {
		return nicknames, errors.New("no nickname entries")
	} else if err != nil {
		return nicknames, err
	}
	for _, entry := range entryBucket {
		nicknames = append(nicknames, entry.Nickname)
	}
	return nicknames, nil
}

func (n *Names) GetUsernames(userID string) (usernames []string, err error) {
	var entryBucket []models.NamesEntry
	listCursor, err := rethink.Table(models.NamesTable).GetAllByIndex(
		"user_id", userID,
	).Filter(
		rethink.Row.Field("guild_id").Eq("global"),
	).OrderBy(rethink.Asc("changed_at")).Run(helpers.GetDB())
	if err != nil {
		return usernames, err
	}
	defer listCursor.Close()
	err = listCursor.All(&entryBucket)

	if err == rethink.ErrEmptyResult {
		return usernames, errors.New("no username entries")
	} else if err != nil {
		return usernames, err
	}
	for _, entry := range entryBucket {
		usernames = append(usernames, entry.Username)
	}
	return usernames, nil
}

func (n *Names) SaveUsername(userID string, username string) (err error) {
	insert := rethink.Table(models.NamesTable).Insert(models.NamesEntry{
		ChangedAt: time.Now(),
		GuildID:   "global",
		UserID:    userID,
		Nickname:  "",
		Username:  username,
	})
	_, err = insert.RunWrite(helpers.GetDB())
	return err
}

func (n *Names) OnGuildMemberListChunk(session *discordgo.Session, members *discordgo.GuildMembersChunk) {
	previousUsernamesMutex.Lock()
	previousNicknamesMutex.Lock()
	defer previousUsernamesMutex.Unlock()
	defer previousNicknamesMutex.Unlock()
	for _, member := range members.Members {
		previousUsernames[member.User.ID] = member.User.Username + "#" + member.User.Discriminator
		if member.Nick != "" {
			if previousNicknames[members.GuildID] == nil {
				previousNicknames[members.GuildID] = make(map[string]string, 0)
			}
			previousNicknames[members.GuildID][member.User.ID] = member.Nick
		}
	}
}

func (n *Names) actionFinish(args []string, in *discordgo.Message, out **discordgo.MessageSend) namesAction {
	_, err := cache.GetSession().ChannelMessageSendComplex(in.ChannelID, *out)
	helpers.Relax(err)

	return nil
}

func (n *Names) newMsg(content string) *discordgo.MessageSend {
	return &discordgo.MessageSend{Content: helpers.GetText(content)}
}

func (n *Names) Relax(err error) {
	if err != nil {
		panic(err)
	}
}

func (n *Names) logger() *logrus.Entry {
	return cache.GetLogger().WithField("module", "names")
}
<|endoftext|>


<commit_before>package renter

import (
	"errors"
	"os"
	"strings"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
)

var (
	errInsufficientContracts = errors.New("not enough contracts to upload file")

	// Erasure-coded piece size
	pieceSize = modules.SectorSize - crypto.TwofishOverhead

	// defaultDataPieces is the number of data pieces per erasure-coded chunk
	defaultDataPieces = func() int {
		if build.Release == "testing" {
			return 2
		}
		return 1 // RC; orig value: 4
	}()

	// defaultParityPieces is the number of parity pieces per erasure-coded
	// chunk
	defaultParityPieces = func() int {
		if build.Release == "testing" {
			return 8
		}
		return 1 // RC; orig value: 20
	}()
)

// Upload instructs the renter to start tracking a file. The renter will
// automatically upload and repair tracked files using a background loop.
func (r *Renter) Upload(up modules.FileUploadParams) error {
	// Enforce nickname rules.
	if strings.HasPrefix(up.SiaPath, "/") {
		return errors.New("nicknames cannot begin with /")
	}

	if !r.wallet.Unlocked() {
		return errors.New("wallet must be unlocked before uploading")
	}

	// Check for a nickname conflict.
	lockID := r.mu.RLock()
	_, exists := r.files[up.SiaPath]
	r.mu.RUnlock(lockID)
	if exists {
		return ErrPathOverload
	}

	// Fill in any missing upload params with sensible defaults.
	fileInfo, err := os.Stat(up.Source)
	if err != nil {
		return err
	}
	if up.ErasureCode == nil {
		up.ErasureCode, _ = NewRSCode(defaultDataPieces, defaultParityPieces)
	}

	// Check that we have contracts to upload to.
	if len(r.hostContractor.Contracts()) < up.ErasureCode.NumPieces() && build.Release != "testing" {
		return errInsufficientContracts
	}

	// Create file object.
	f := newFile(up.SiaPath, up.ErasureCode, pieceSize, uint64(fileInfo.Size()))
	f.mode = uint32(fileInfo.Mode())

	// Add file to renter.
	lockID = r.mu.Lock()
	r.files[up.SiaPath] = f
	r.tracking[up.SiaPath] = trackedFile{
		RepairPath: up.Source,
	}
	r.saveSync()
	r.mu.Unlock(lockID)

	// Save the .sia file to the renter directory.
	err = r.saveFile(f)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>increase rc parity<commit_after>package renter

import (
	"errors"
	"os"
	"strings"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
)

var (
	errInsufficientContracts = errors.New("not enough contracts to upload file")

	// Erasure-coded piece size
	pieceSize = modules.SectorSize - crypto.TwofishOverhead

	// defaultDataPieces is the number of data pieces per erasure-coded chunk
	defaultDataPieces = func() int {
		if build.Release == "testing" {
			return 2
		}
		return 1 // RC; orig value: 4
	}()

	// defaultParityPieces is the number of parity pieces per erasure-coded
	// chunk
	defaultParityPieces = func() int {
		if build.Release == "testing" {
			return 8
		}
		return 4 // RC; orig value: 20
	}()
)

// Upload instructs the renter to start tracking a file. The renter will
// automatically upload and repair tracked files using a background loop.
func (r *Renter) Upload(up modules.FileUploadParams) error {
	// Enforce nickname rules.
	if strings.HasPrefix(up.SiaPath, "/") {
		return errors.New("nicknames cannot begin with /")
	}

	if !r.wallet.Unlocked() {
		return errors.New("wallet must be unlocked before uploading")
	}

	// Check for a nickname conflict.
	lockID := r.mu.RLock()
	_, exists := r.files[up.SiaPath]
	r.mu.RUnlock(lockID)
	if exists {
		return ErrPathOverload
	}

	// Fill in any missing upload params with sensible defaults.
	fileInfo, err := os.Stat(up.Source)
	if err != nil {
		return err
	}
	if up.ErasureCode == nil {
		up.ErasureCode, _ = NewRSCode(defaultDataPieces, defaultParityPieces)
	}

	// Check that we have contracts to upload to.
	if len(r.hostContractor.Contracts()) < up.ErasureCode.NumPieces() && build.Release != "testing" {
		return errInsufficientContracts
	}

	// Create file object.
	f := newFile(up.SiaPath, up.ErasureCode, pieceSize, uint64(fileInfo.Size()))
	f.mode = uint32(fileInfo.Mode())

	// Add file to renter.
	lockID = r.mu.Lock()
	r.files[up.SiaPath] = f
	r.tracking[up.SiaPath] = trackedFile{
		RepairPath: up.Source,
	}
	r.saveSync()
	r.mu.Unlock(lockID)

	// Save the .sia file to the renter directory.
	err = r.saveFile(f)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


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

// Package transport supports network connections to HTTP and GRPC servers.
// This package is not intended for use by end developers. Use the
// google.golang.org/api/option package to configure API clients.
package transport

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/oauth"

	gtransport "google.golang.org/api/googleapi/transport"
	"google.golang.org/api/internal"
	"google.golang.org/api/option"
)

// NewHTTPClient returns an HTTP client for use communicating with a Google cloud
// service, configured with the given ClientOptions. It also returns the endpoint
// for the service as specified in the options.
func NewHTTPClient(ctx context.Context, opts ...option.ClientOption) (*http.Client, string, error) {
	var o internal.DialSettings
	for _, opt := range opts {
		opt.Apply(&o)
	}
	if o.GRPCConn != nil {
		return nil, "", errors.New("unsupported gRPC connection specified")
	}
	// TODO(cbro): consider injecting the User-Agent even if an explicit HTTP client is provided?
	if o.HTTPClient != nil {
		return o.HTTPClient, o.Endpoint, nil
	}
	if o.APIKey != "" {
		hc := &http.Client{
			Transport: &gtransport.APIKey{
				Key: o.APIKey,
				Transport: userAgentTransport{
					base:      baseTransport(ctx),
					userAgent: o.UserAgent,
				},
			},
		}
		return hc, o.Endpoint, nil
	}
	if o.ServiceAccountJSONFilename != "" {
		ts, err := serviceAcctTokenSource(ctx, o.ServiceAccountJSONFilename, o.Scopes...)
		if err != nil {
			return nil, "", err
		}
		o.TokenSource = ts
	}
	if o.TokenSource == nil {
		var err error
		o.TokenSource, err = google.DefaultTokenSource(ctx, o.Scopes...)
		if err != nil {
			return nil, "", fmt.Errorf("google.DefaultTokenSource: %v", err)
		}
	}
	hc := &http.Client{
		Transport: &oauth2.Transport{
			Source: o.TokenSource,
			Base: userAgentTransport{
				base:      baseTransport(ctx),
				userAgent: o.UserAgent,
			},
		},
	}
	return hc, o.Endpoint, nil
}

type userAgentTransport struct {
	userAgent string
	base      http.RoundTripper
}

func (t userAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	rt := t.base
	if rt == nil {
		return nil, errors.New("transport: no Transport specified")
	}
	if t.userAgent == "" {
		return rt.RoundTrip(req)
	}
	newReq := *req
	newReq.Header = make(http.Header)
	for k, vv := range req.Header {
		newReq.Header[k] = vv
	}
	// TODO(cbro): append to existing User-Agent header?
	newReq.Header["User-Agent"] = []string{t.userAgent}
	return rt.RoundTrip(&newReq)
}

// Set at init time by dial_appengine.go. If nil, we're not on App Engine.
var appengineDialerHook func(context.Context) grpc.DialOption
var appengineUrlfetchHook func(context.Context) http.RoundTripper

// baseTransport returns the base HTTP transport.
// On App Engine, this is urlfetch.Transport, otherwise it's http.DefaultTransport.
func baseTransport(ctx context.Context) http.RoundTripper {
	if appengineUrlfetchHook != nil {
		return appengineUrlfetchHook(ctx)
	}
	return http.DefaultTransport
}

// DialGRPC returns a GRPC connection for use communicating with a Google cloud
// service, configured with the given ClientOptions.
func DialGRPC(ctx context.Context, opts ...option.ClientOption) (*grpc.ClientConn, error) {
	var o internal.DialSettings
	for _, opt := range opts {
		opt.Apply(&o)
	}
	if o.HTTPClient != nil {
		return nil, errors.New("unsupported HTTP client specified")
	}
	if o.GRPCConn != nil {
		return o.GRPCConn, nil
	}
	if o.ServiceAccountJSONFilename != "" {
		ts, err := serviceAcctTokenSource(ctx, o.ServiceAccountJSONFilename, o.Scopes...)
		if err != nil {
			return nil, err
		}
		o.TokenSource = ts
	}
	if o.TokenSource == nil {
		var err error
		o.TokenSource, err = google.DefaultTokenSource(ctx, o.Scopes...)
		if err != nil {
			return nil, fmt.Errorf("google.DefaultTokenSource: %v", err)
		}
	}
	grpcOpts := []grpc.DialOption{
		grpc.WithPerRPCCredentials(oauth.TokenSource{o.TokenSource}),
		grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
	}
	if appengineDialerHook != nil {
		// Use the Socket API on App Engine.
		grpcOpts = append(grpcOpts, appengineDialerHook(ctx))
	}
	grpcOpts = append(grpcOpts, o.GRPCDialOpts...)
	if o.UserAgent != "" {
		grpcOpts = append(grpcOpts, grpc.WithUserAgent(o.UserAgent))
	}
	return grpc.DialContext(ctx, o.Endpoint, grpcOpts...)
}

func serviceAcctTokenSource(ctx context.Context, filename string, scope ...string) (oauth2.TokenSource, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("cannot read service account file: %v", err)
	}
	cfg, err := google.JWTConfigFromJSON(data, scope...)
	if err != nil {
		return nil, fmt.Errorf("google.JWTConfigFromJSON: %v", err)
	}
	return cfg.TokenSource(ctx), nil
}
<commit_msg>transport: add a way to dial gRPC insecurely.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package transport supports network connections to HTTP and GRPC servers.
// This package is not intended for use by end developers. Use the
// google.golang.org/api/option package to configure API clients.
package transport

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/oauth"

	gtransport "google.golang.org/api/googleapi/transport"
	"google.golang.org/api/internal"
	"google.golang.org/api/option"
)

// NewHTTPClient returns an HTTP client for use communicating with a Google cloud
// service, configured with the given ClientOptions. It also returns the endpoint
// for the service as specified in the options.
func NewHTTPClient(ctx context.Context, opts ...option.ClientOption) (*http.Client, string, error) {
	var o internal.DialSettings
	for _, opt := range opts {
		opt.Apply(&o)
	}
	if o.GRPCConn != nil {
		return nil, "", errors.New("unsupported gRPC connection specified")
	}
	// TODO(cbro): consider injecting the User-Agent even if an explicit HTTP client is provided?
	if o.HTTPClient != nil {
		return o.HTTPClient, o.Endpoint, nil
	}
	if o.APIKey != "" {
		hc := &http.Client{
			Transport: &gtransport.APIKey{
				Key: o.APIKey,
				Transport: userAgentTransport{
					base:      baseTransport(ctx),
					userAgent: o.UserAgent,
				},
			},
		}
		return hc, o.Endpoint, nil
	}
	if o.ServiceAccountJSONFilename != "" {
		ts, err := serviceAcctTokenSource(ctx, o.ServiceAccountJSONFilename, o.Scopes...)
		if err != nil {
			return nil, "", err
		}
		o.TokenSource = ts
	}
	if o.TokenSource == nil {
		var err error
		o.TokenSource, err = google.DefaultTokenSource(ctx, o.Scopes...)
		if err != nil {
			return nil, "", fmt.Errorf("google.DefaultTokenSource: %v", err)
		}
	}
	hc := &http.Client{
		Transport: &oauth2.Transport{
			Source: o.TokenSource,
			Base: userAgentTransport{
				base:      baseTransport(ctx),
				userAgent: o.UserAgent,
			},
		},
	}
	return hc, o.Endpoint, nil
}

type userAgentTransport struct {
	userAgent string
	base      http.RoundTripper
}

func (t userAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	rt := t.base
	if rt == nil {
		return nil, errors.New("transport: no Transport specified")
	}
	if t.userAgent == "" {
		return rt.RoundTrip(req)
	}
	newReq := *req
	newReq.Header = make(http.Header)
	for k, vv := range req.Header {
		newReq.Header[k] = vv
	}
	// TODO(cbro): append to existing User-Agent header?
	newReq.Header["User-Agent"] = []string{t.userAgent}
	return rt.RoundTrip(&newReq)
}

// Set at init time by dial_appengine.go. If nil, we're not on App Engine.
var appengineDialerHook func(context.Context) grpc.DialOption
var appengineUrlfetchHook func(context.Context) http.RoundTripper

// baseTransport returns the base HTTP transport.
// On App Engine, this is urlfetch.Transport, otherwise it's http.DefaultTransport.
func baseTransport(ctx context.Context) http.RoundTripper {
	if appengineUrlfetchHook != nil {
		return appengineUrlfetchHook(ctx)
	}
	return http.DefaultTransport
}

// DialGRPC returns a GRPC connection for use communicating with a Google cloud
// service, configured with the given ClientOptions.
func DialGRPC(ctx context.Context, opts ...option.ClientOption) (*grpc.ClientConn, error) {
	var o internal.DialSettings
	for _, opt := range opts {
		opt.Apply(&o)
	}
	if o.HTTPClient != nil {
		return nil, errors.New("unsupported HTTP client specified")
	}
	if o.GRPCConn != nil {
		return o.GRPCConn, nil
	}
	if o.ServiceAccountJSONFilename != "" {
		ts, err := serviceAcctTokenSource(ctx, o.ServiceAccountJSONFilename, o.Scopes...)
		if err != nil {
			return nil, err
		}
		o.TokenSource = ts
	}
	if o.TokenSource == nil {
		var err error
		o.TokenSource, err = google.DefaultTokenSource(ctx, o.Scopes...)
		if err != nil {
			return nil, fmt.Errorf("google.DefaultTokenSource: %v", err)
		}
	}
	grpcOpts := []grpc.DialOption{
		grpc.WithPerRPCCredentials(oauth.TokenSource{o.TokenSource}),
		grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
	}
	if appengineDialerHook != nil {
		// Use the Socket API on App Engine.
		grpcOpts = append(grpcOpts, appengineDialerHook(ctx))
	}
	grpcOpts = append(grpcOpts, o.GRPCDialOpts...)
	if o.UserAgent != "" {
		grpcOpts = append(grpcOpts, grpc.WithUserAgent(o.UserAgent))
	}
	return grpc.DialContext(ctx, o.Endpoint, grpcOpts...)
}

func serviceAcctTokenSource(ctx context.Context, filename string, scope ...string) (oauth2.TokenSource, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("cannot read service account file: %v", err)
	}
	cfg, err := google.JWTConfigFromJSON(data, scope...)
	if err != nil {
		return nil, fmt.Errorf("google.JWTConfigFromJSON: %v", err)
	}
	return cfg.TokenSource(ctx), nil
}

// DialGRPCInsecure returns an insecure GRPC connection for use communicating
// with fake or mock Google cloud service implementations, such as emulators.
// The connection is configured with the given ClientOptions.
func DialGRPCInsecure(ctx context.Context, opts ...option.ClientOption) (*grpc.ClientConn, error) {
	var o internal.DialSettings
	for _, opt := range opts {
		opt.Apply(&o)
	}
	if o.HTTPClient != nil {
		return nil, errors.New("unsupported HTTP client specified")
	}
	if o.GRPCConn != nil {
		return o.GRPCConn, nil
	}
	grpcOpts := []grpc.DialOption{grpc.WithInsecure()}
	grpcOpts = append(grpcOpts, o.GRPCDialOpts...)
	if o.UserAgent != "" {
		grpcOpts = append(grpcOpts, grpc.WithUserAgent(o.UserAgent))
	}
	return grpc.DialContext(ctx, o.Endpoint, grpcOpts...)
}
<|endoftext|>


<commit_before>package momos

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

var ssiURL string

func SSITimeoutHandler(w http.ResponseWriter, r *http.Request) {
	time.Sleep(1000 * time.Millisecond)
	w.Header().Add("Cache-Control", "max-age=10")
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprint(w, `<h1>hello</h1>`)
}

func SSIInvalidStausCodeHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Header().Add("Cache-Control", "max-age=10")
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprint(w, `<h1>hello</h1>`)
}

func SSIHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Cache-Control", "max-age=10")
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprint(w, `<h1>hello</h1>`)
}

func APIHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Cache-Control", "max-age=10")
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprint(w, `
		<ssi name="basket" timeout="200" template="true" src="`+ssiURL+`">Default content!
		<ssi-timeout>
		Please try it again!
		</ssi-timeout>
		<ssi-error>
		Please call the support!
		</ssi-error>
	</ssi>
	`)
}

func TestSSIContent(t *testing.T) {

	ssiServer := httptest.NewServer(http.HandlerFunc(SSIHandler))
	ssiURL = ssiServer.URL
	defer ssiServer.Close()

	tsAPI := httptest.NewServer(http.HandlerFunc(APIHandler))
	defer tsAPI.Close()

	p := New(tsAPI.URL)

	server := httptest.NewServer(p.Handler)
	defer server.Close()

	res, err := http.Get(server.URL)
	if err != nil {
		log.Fatal(err)
	}

	body, _ := ioutil.ReadAll(res.Body)
	bodyString := string(body)

	assert.Equal(t, res.StatusCode, 200, "should return 200")
	assert.Equal(t, "
hello
\n\t", bodyString)
}
<commit_msg>create test suite<commit_after>package momos

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

var s struct {
	server *httptest.Server
	proxy  *httptest.Server
	client http.Client
}

func TestMain(m *testing.M) {
	setup()
	code := m.Run()
	teardown()
	os.Exit(code)
}

func setup() {

	mux := http.NewServeMux()
	s.server = httptest.NewServer(mux)
	s.client = http.Client{}

	mux.HandleFunc("/api", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Cache-Control", "max-age=10")
		w.Header().Set("Content-Type", "text/html")
		fmt.Fprint(w, `
			<ssi name="basket" timeout="200" template="true" src="`+s.server.URL+`/ssi">Default content!
			<ssi-timeout>
			Please try it again!
			</ssi-timeout>
			<ssi-error>
			Please call the support!
			</ssi-error>
		</ssi>
		`)
	}))

	mux.HandleFunc("/api/ssi500", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Cache-Control", "max-age=10")
		w.Header().Set("Content-Type", "text/html")
		fmt.Fprint(w, `
			<ssi name="basket" timeout="200" template="true" src="`+s.server.URL+`/ssi500">Default content!
			<ssi-timeout>
			Please try it again!
			</ssi-timeout>
			<ssi-error>
			Please call the support!
			</ssi-error>
		</ssi>
		`)
	}))

	p := New(s.server.URL)
	s.proxy = httptest.NewServer(p.Handler)

	mux.HandleFunc("/ssi", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Cache-Control", "max-age=10")
		w.Header().Set("Content-Type", "text/html")
		fmt.Fprint(w, `<h1>hello</h1>`)
	}))

	mux.HandleFunc("/ssiTimeout", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(1000 * time.Millisecond)
		w.Header().Add("Cache-Control", "max-age=10")
		w.Header().Set("Content-Type", "text/html")
		fmt.Fprint(w, `<h1>hello</h1>`)
	}))

	mux.HandleFunc("/ssi500", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
		w.Header().Add("Cache-Control", "max-age=10")
		w.Header().Set("Content-Type", "text/html")
		fmt.Fprint(w, `<h1>hello</h1>`)
	}))
}

func teardown() {
	s.server.Close()
	s.proxy.Close()
}

var ssiURL string

func TestSSIContent(t *testing.T) {
	res, err := s.client.Get(s.proxy.URL + "/api")
	if err != nil {
		log.Fatal(err)
	}

	body, _ := ioutil.ReadAll(res.Body)
	bodyString := string(body)

	assert.Equal(t, res.StatusCode, 200, "should return 200")
	assert.Equal(t, "
hello
\n\t\t", bodyString)
}

func TestError(t *testing.T) {
	res, err := s.client.Get(s.proxy.URL + "/api/ssi500")
	if err != nil {
		log.Fatal(err)
	}

	body, _ := ioutil.ReadAll(res.Body)
	bodyString := string(body)

	assert.Equal(t, res.StatusCode, 200, "should return 200")
	assert.Equal(t, "\n\t\t\tPlease call the support!\n\t\t\t\n\t\t", bodyString)
}
<|endoftext|>


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

package setting

import (
	"strings"
	"time"

	"code.gitea.io/gitea/modules/log"
)

// Cache represents cache settings
type Cache struct {
	Enabled  bool
	Adapter  string
	Interval int
	Conn     string
	TTL      time.Duration `ini:"ITEM_TTL"`
}

var (
	// CacheService the global cache
	CacheService = struct {
		Cache

		LastCommit struct {
			Enabled      bool
			TTL          time.Duration `ini:"ITEM_TTL"`
			CommitsCount int64
		} `ini:"cache.last_commit"`
	}{
		Cache: Cache{
			Enabled:  true,
			Adapter:  "memory",
			Interval: 60,
			TTL:      16 * time.Hour,
		},
		LastCommit: struct {
			Enabled      bool
			TTL          time.Duration `ini:"ITEM_TTL"`
			CommitsCount int64
		}{
			Enabled:      true,
			TTL:          8760 * time.Hour,
			CommitsCount: 1000,
		},
	}
)

func newCacheService() {
	sec := Cfg.Section("cache")
	if err := sec.MapTo(&CacheService); err != nil {
		log.Fatal("Failed to map Cache settings: %v", err)
	}

	CacheService.Adapter = sec.Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})
	switch CacheService.Adapter {
	case "memory":
	case "redis", "memcache":
		CacheService.Conn = strings.Trim(sec.Key("HOST").String(), "\" ")
	case "": // disable cache
		CacheService.Enabled = false
	default:
		log.Fatal("Unknown cache adapter: %s", CacheService.Adapter)
	}

	if CacheService.Enabled {
		log.Info("Cache Service Enabled")
	}

	sec = Cfg.Section("cache.last_commit")
	if !CacheService.Enabled {
		CacheService.LastCommit.Enabled = false
	}

	CacheService.LastCommit.CommitsCount = sec.Key("COMMITS_COUNT").MustInt64(1000)

	if CacheService.LastCommit.Enabled {
		log.Info("Last Commit Cache Service Enabled")
	}
}
<commit_msg>Mark Cache with ini tag (#12605)<commit_after>// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package setting

import (
	"strings"
	"time"

	"code.gitea.io/gitea/modules/log"
)

// Cache represents cache settings
type Cache struct {
	Enabled  bool
	Adapter  string
	Interval int
	Conn     string
	TTL      time.Duration `ini:"ITEM_TTL"`
}

var (
	// CacheService the global cache
	CacheService = struct {
		Cache `ini:"cache"`

		LastCommit struct {
			Enabled      bool
			TTL          time.Duration `ini:"ITEM_TTL"`
			CommitsCount int64
		} `ini:"cache.last_commit"`
	}{
		Cache: Cache{
			Enabled:  true,
			Adapter:  "memory",
			Interval: 60,
			TTL:      16 * time.Hour,
		},
		LastCommit: struct {
			Enabled      bool
			TTL          time.Duration `ini:"ITEM_TTL"`
			CommitsCount int64
		}{
			Enabled:      true,
			TTL:          8760 * time.Hour,
			CommitsCount: 1000,
		},
	}
)

func newCacheService() {
	sec := Cfg.Section("cache")
	if err := sec.MapTo(&CacheService); err != nil {
		log.Fatal("Failed to map Cache settings: %v", err)
	}

	CacheService.Adapter = sec.Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})
	switch CacheService.Adapter {
	case "memory":
	case "redis", "memcache":
		CacheService.Conn = strings.Trim(sec.Key("HOST").String(), "\" ")
	case "": // disable cache
		CacheService.Enabled = false
	default:
		log.Fatal("Unknown cache adapter: %s", CacheService.Adapter)
	}

	if CacheService.Enabled {
		log.Info("Cache Service Enabled")
	}

	sec = Cfg.Section("cache.last_commit")
	if !CacheService.Enabled {
		CacheService.LastCommit.Enabled = false
	}

	CacheService.LastCommit.CommitsCount = sec.Key("COMMITS_COUNT").MustInt64(1000)

	if CacheService.LastCommit.Enabled {
		log.Info("Last Commit Cache Service Enabled")
	}
}
<|endoftext|>