<commit_before>package eval

import (
	"math"
	"math/rand"

	"github.com/elves/elvish/eval/vals"
)

// Numerical operations.

func init() {
	addBuiltinFns(map[string]interface{}{
		// Constructor
		"float64": toFloat64,

		// Comparison
		"<":  lt,
		"<=": le,
		"==": eqNum,
		"!=": ne,
		">":  gt,
		">=": ge,

		// Arithmetics
		"+": plus,
		"-": minus,
		"*": times,
		"/": slash,
		"^": math.Pow,
		"%": mod,

		// Random
		"rand":    rand.Float64,
		"randint": randint,
	})
}

func toFloat64(f float64) float64 {
	return f
}

func lt(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] < nums[i+1]) {
			return false
		}
	}
	return true
}

func le(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] <= nums[i+1]) {
			return false
		}
	}
	return true
}

func eqNum(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] == nums[i+1]) {
			return false
		}
	}
	return true
}

func ne(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] != nums[i+1]) {
			return false
		}
	}
	return true
}

func gt(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] > nums[i+1]) {
			return false
		}
	}
	return true
}

func ge(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] >= nums[i+1]) {
			return false
		}
	}
	return true
}

func plus(nums ...float64) float64 {
	sum := 0.0
	for _, f := range nums {
		sum += f
	}
	return sum
}

func minus(sum float64, nums ...float64) float64 {
	if len(nums) == 0 {
		// Unary -
		return -sum
	}
	for _, f := range nums {
		sum -= f
	}
	return sum
}

func times(nums ...float64) float64 {
	prod := 1.0
	for _, f := range nums {
		prod *= f
	}
	return prod
}

func slash(fm *Frame, args ...float64) error {
	if len(args) == 0 {
		// cd /
		return fm.Chdir("/")
	}
	// Division
	divide(fm, args[0], args[1:]...)
	return nil
}

func divide(fm *Frame, prod float64, nums ...float64) {
	out := fm.ports[1].Chan
	for _, f := range nums {
		prod /= f
	}
	out <- vals.FromGo(prod)
}

func mod(fm *Frame, a, b int) (int, error) {
	if b == 0 {
		return 0, ErrArgs
	}
	return a % b, nil
}

func randint(low, high int) (int, error) {
	if low >= high {
		return 0, ErrArgs
	}
	return low + rand.Intn(high-low), nil
}
<commit_msg>Removed argument *Frame from func mod<commit_after>package eval

import (
	"math"
	"math/rand"

	"github.com/elves/elvish/eval/vals"
)

// Numerical operations.

func init() {
	addBuiltinFns(map[string]interface{}{
		// Constructor
		"float64": toFloat64,

		// Comparison
		"<":  lt,
		"<=": le,
		"==": eqNum,
		"!=": ne,
		">":  gt,
		">=": ge,

		// Arithmetics
		"+": plus,
		"-": minus,
		"*": times,
		"/": slash,
		"^": math.Pow,
		"%": mod,

		// Random
		"rand":    rand.Float64,
		"randint": randint,
	})
}

func toFloat64(f float64) float64 {
	return f
}

func lt(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] < nums[i+1]) {
			return false
		}
	}
	return true
}

func le(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] <= nums[i+1]) {
			return false
		}
	}
	return true
}

func eqNum(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] == nums[i+1]) {
			return false
		}
	}
	return true
}

func ne(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] != nums[i+1]) {
			return false
		}
	}
	return true
}

func gt(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] > nums[i+1]) {
			return false
		}
	}
	return true
}

func ge(nums ...float64) bool {
	for i := 0; i < len(nums)-1; i++ {
		if !(nums[i] >= nums[i+1]) {
			return false
		}
	}
	return true
}

func plus(nums ...float64) float64 {
	sum := 0.0
	for _, f := range nums {
		sum += f
	}
	return sum
}

func minus(sum float64, nums ...float64) float64 {
	if len(nums) == 0 {
		// Unary -
		return -sum
	}
	for _, f := range nums {
		sum -= f
	}
	return sum
}

func times(nums ...float64) float64 {
	prod := 1.0
	for _, f := range nums {
		prod *= f
	}
	return prod
}

func slash(fm *Frame, args ...float64) error {
	if len(args) == 0 {
		// cd /
		return fm.Chdir("/")
	}
	// Division
	divide(fm, args[0], args[1:]...)
	return nil
}

func divide(fm *Frame, prod float64, nums ...float64) {
	out := fm.ports[1].Chan
	for _, f := range nums {
		prod /= f
	}
	out <- vals.FromGo(prod)
}

func mod(a, b int) (int, error) {
	if b == 0 {
		return 0, ErrArgs
	}
	return a % b, nil
}

func randint(low, high int) (int, error) {
	if low >= high {
		return 0, ErrArgs
	}
	return low + rand.Intn(high-low), nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	log "github.com/cihub/seelog"
	"os"
	"strings"
	"time"
)

var Configuration Settings

func main() {
	initLog()

	fmt.Println("Starting...")
	log.Info("Starting...")

	Configuration = LoadSettings()

	loop := make(chan bool)
	go dns_loop(loop)

	ret := <-loop

	if !ret {
		fmt.Println("Dns loop exited...")
		log.Error("Dns loop exited...")
		close(loop)

		os.Exit(1)
	}
}

func dns_loop(loop chan bool) {

	for {

		defer func() {
			if err := recover(); err != nil {
				log.Error(err)
			}
		}()

		domain_id := get_domain(Configuration.Domain)

		if domain_id == -1 {
			continue
		}

		currentIP, err := get_currentIP(Configuration.IP_Url)

		if err != nil {
			continue
		}

		sub_domain_id, ip := get_subdomain(domain_id, Configuration.Sub_domain)

		fmt.Printf("currentIp is:%s\n", currentIP)
		log.Infof("currentIp is:%s\n", currentIP)

		//Continue to check the IP of sub-domain
		if len(ip) > 0 && !strings.Contains(currentIP, ip) {

			fmt.Println("Start to update record IP...")
			log.Info("Start to update record IP...")
			update_ip(domain_id, sub_domain_id, Configuration.Sub_domain, currentIP)

		} else {
			fmt.Println("Current IP is same as domain IP, no need to update...")
			log.Info("Current IP is same as domain IP, no need to update...")
		}

		//Interval is 5 minutes
		time.Sleep(time.Second * 60 * 5)
	}
}
<commit_msg>enhance error handling<commit_after>package main

import (
	"fmt"
	log "github.com/cihub/seelog"
	"os"
	"strings"
	"time"
)

var Configuration Settings

func main() {
	initLog()

	fmt.Println("Starting...")
	log.Info("Starting...")

	Configuration = LoadSettings()

	loop := make(chan bool)
	go dns_loop(loop)

	ret := <-loop

	if !ret {
		fmt.Println("Dns loop exited...")
		log.Error("Dns loop exited...")
		close(loop)

		os.Exit(1)
	}
}

func dns_loop(loop chan bool) {

	for {

		defer func() {
			if err := recover(); err != nil {
				log.Error(err)
			}
		}()

		domain_id := get_domain(Configuration.Domain)

		if domain_id == -1 {
			continue
		}

		currentIP, err := get_currentIP(Configuration.IP_Url)

		if err != nil {
			continue
		}

		sub_domain_id, ip := get_subdomain(domain_id, Configuration.Sub_domain)

		if sub_domain_id == "" || ip == "" {
			continue
		}

		fmt.Printf("currentIp is:%s\n", currentIP)
		log.Infof("currentIp is:%s\n", currentIP)

		//Continue to check the IP of sub-domain
		if len(ip) > 0 && !strings.Contains(currentIP, ip) {

			fmt.Println("Start to update record IP...")
			log.Info("Start to update record IP...")
			update_ip(domain_id, sub_domain_id, Configuration.Sub_domain, currentIP)

		} else {
			fmt.Println("Current IP is same as domain IP, no need to update...")
			log.Info("Current IP is same as domain IP, no need to update...")
		}

		//Interval is 5 minutes
		time.Sleep(time.Second * 60 * 5)
	}
}
<|endoftext|>


<commit_before>package main

import (
	// stdlib
	"encoding/base64"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"

	// external
	"github.com/VictorLowther/go-git/git"
	auth "github.com/abbot/go-http-auth"
	"github.com/russross/blackfriday"
)

const (
	GOIKIVERSION = "0.2.0"
)

var (
	configFile     string
	displayConfig  bool
	displayVersion bool
	templateFiles  map[string]string
	conf           config
	templates      *template.Template
	validPath      *regexp.Regexp
	validLink      *regexp.Regexp
	tableTag       *regexp.Regexp
	authenticator  *auth.BasicAuth
)

type page struct {
	SiteName    string
	Title       string
	Author      author
	Body        string
	Description string
	Revisions   []pageRevision
}

type searchPage struct {
	SiteName string
	Title    string
	Results  []searchResult
}

type historyPage struct {
	SiteName  string
	Title     string
	Revisions []pageRevision
}

func (p *page) save() error {
	filename := fileName(p.Title)
	datapath := dataPath(conf.DataDir, filename)

	err := os.MkdirAll(filepath.Dir(datapath), 0777)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(datapath, []byte(p.Body), 0600)
	if err != nil {
		return err
	}

	_, err = gitAdd(filename)
	if err != nil {
		return err
	}

	message := p.Description
	if len(message) == 0 {
		message = fmt.Sprintf("Update %s", filename)
	}
	stdout, err := gitCommit(message, p.Author)
	if err != nil {
		return err
	}
	log.Println(stdout)

	return nil
}

func fileName(title string) string {
	return title + "." + conf.FileExtension
}

func dataPath(dir string, file string) string {
	return filepath.Join(dir, file)
}

func loadPage(title string, revision string) (*page, error) {
	filename := fileName(title)
	if len(revision) == 0 {
		revision = "HEAD"
	}
	body, err := gitShow(filename, revision)
	if err != nil {
		return &page{
			Title:    title,
			Body:     body.String(),
			SiteName: conf.Name,
		}, fmt.Errorf("Unable to load page content from %s at %s\n", filename, revision)
	}
	return &page{Title: title, Body: body.String(), SiteName: conf.Name}, nil
}

func processLinks(content []byte, link *regexp.Regexp) []byte {
	return link.ReplaceAllFunc(content, func(match []byte) []byte {
		return link.ReplaceAll(match, []byte("[$1]($1)"))
	})
}

func processTables(content []byte, table *regexp.Regexp) []byte {
	return table.ReplaceAllFunc(content, func(match []byte) []byte {
		return table.ReplaceAll(match, []byte(`<table class="`+conf.TableClass+`">`))
	})
}

/*
TODO: How to combine the three functions? With use of an interface for the page?
*/

func renderTemplate(w http.ResponseWriter, tmpl string, p *page) {
	err := templates.ExecuteTemplate(w, tmpl, p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func renderHistoryTemplate(w http.ResponseWriter, tmpl string, p *historyPage) {
	err := templates.ExecuteTemplate(w, tmpl, p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func renderSearchTemplate(w http.ResponseWriter, tmpl string, p *searchPage) {
	err := templates.ExecuteTemplate(w, tmpl, p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		path := r.URL.Path
		log.Println(path)
		if path == "/" {
			path = "/view/"
		}
		if path[len(path)-1:len(path)] == "/" {
			viewIndex := path + conf.IndexPage
			http.Redirect(w, r, viewIndex, http.StatusFound)
			return
		}
		m := validPath.FindStringSubmatch(path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[2])
	}
}

func makeAuthHandler(fn func(http.ResponseWriter, *auth.AuthenticatedRequest, string)) auth.AuthenticatedHandlerFunc {
	return func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
		path := r.URL.Path
		log.Println(path)
		m := validPath.FindStringSubmatch(path)
		if m == nil {
			http.NotFound(w, &r.Request)
			return
		}
		fn(w, r, m[2])
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
	revision := r.FormValue("revision")
	if revision == "" {
		revision = "HEAD"
	}

	p, err := loadPage(title, revision)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}

	content := []byte(p.Body)
	content = processLinks(content, validLink)
	content = blackfriday.MarkdownCommon(content)
	content = processTables(content, tableTag)
	p.Body = string(content)

	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *auth.AuthenticatedRequest, title string) {
	revision := r.FormValue("revision")
	if revision == "" {
		revision = "HEAD"
	}

	p, err := loadPage(title, revision)
	if err != nil {
		p = &page{Title: title, SiteName: conf.Name}
	}

	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *auth.AuthenticatedRequest, title string) {
	body := r.FormValue("body")
	description := r.FormValue("description")
	user := conf.Auth[r.Username]
	author := author{Name: user.Name, Email: user.Email}
	p := &page{Title: title, Body: body, Description: description, Author: author}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, &r.Request, "/view/"+title, http.StatusFound)
}

func historyHandler(w http.ResponseWriter, r *http.Request, title string) {
	revisions, _ := gitLog(fileName(title))
	p := &historyPage{Title: title, Revisions: revisions, SiteName: conf.Name}
	renderHistoryTemplate(w, "history", p)
}

func searchHandler(w http.ResponseWriter, r *http.Request) {
	search := r.FormValue("search")
	results, err := gitGrep(search)
	if err != nil {
		log.Println("error in search", err)
	}
	p := &searchPage{Title: "Search", Results: results, SiteName: conf.Name}
	renderSearchTemplate(w, "search", p)
}

func bundleHandler(w http.ResponseWriter, r *http.Request) {
	p := strings.TrimLeft(r.URL.Path, "/")
	f, ok := _bundle[p]
	if ok {
		w.Write([]byte(f))
	} else {
		http.NotFound(w, r)
	}
}

func defaultConfig() string {
	return _bundle["goiki.toml"]
}

func loadBundle() {
	for file, content := range _bundle {
		c, err := base64.StdEncoding.DecodeString(content)
		if err != nil {
			log.Printf("Error during loadBundle(): %v", err)
		}
		_bundle[file] = string(c)
	}
}

// Initialize the configuration options, templates and URL and link regexes.
func init() {
	flag.StringVar(&configFile, "c", "", "Specify a configuration file (use default config otherwise)")
	flag.BoolVar(&displayConfig, "d", false, "Display default configuration and exit")
	flag.BoolVar(&displayVersion, "v", false, "Display version and exit")

	loadBundle()

	templateFiles = map[string]string{"header": "_header.html", "footer": "_footer.html", "edit": "edit.html",
		"history": "history.html", "search": "search.html", "view": "view.html"}
	validPath = regexp.MustCompile("^/(edit|save|view|history)/([a-zA-Z0-9/_-]+)$")
	validLink = regexp.MustCompile(`\[([^\]]+)]\(\)`)
	tableTag = regexp.MustCompile(`<table>`)
}

func secret(username, realm string) string {
	return conf.Auth[username].Password
}

func serviceAddress(host string, port int) string {
	return fmt.Sprintf("%s:%d", host, port)
}

func main() {
	flag.Parse()

	// Display version and exit (flag -version)
	if displayVersion {
		fmt.Println("Goiki", GOIKIVERSION)
		return
	}

	// Display the default configuration set and exit (flag -default)
	if displayConfig {
		fmt.Println(defaultConfig())
		return
	}

	// Load the configuration. Use the default embedded configuration unless
	// a config file was specified at the command line.
	var err error
	if len(configFile) == 0 {
		conf, err = loadConfig(defaultConfig())
	} else {
		conf, err = loadConfigFromFile(configFile)
	}

	if err != nil {
		fmt.Printf("FATAL: Unable to load configuration: %v\n", err)
		return
	}

	// Load authentication from the config and run the authenticator.
	conf.loadAuth()
	authenticator = auth.NewBasicAuthenticator(serviceAddress(conf.Host, conf.Port), secret)

	// Load the templates. Use the default embedded templates unless a directory
	// of templates is specified in configuration.
	if len(conf.TemplateDir) == 0 {
		templates = template.Must(template.New("goiki").Parse(""))
		for _, file := range templateFiles {
			data, _ := ioutil.ReadFile(filepath.Join("templates", file))
			template.Must(templates.Parse(string(data)))
		}
	} else {
		templateLocations := make([]string, len(templateFiles))
		i := 0
		for _, file := range templateFiles {
			templateLocations[i] = filepath.Join(conf.TemplateDir, file)
			i += 1
		}
		templates = template.Must(template.ParseFiles(templateLocations...))
	}

	// Load the repository.
	if repo, err = git.Open(conf.DataDir); err != nil {
		log.Fatalf("Unable to open the repo at %s. Please check to make sure it exists and is initialized.\n%v", conf.DataDir, err)
	}

	// Static routes
	// If a static directory is provided in the configuration, use that; otherwise
	// use the embedded static content
	if len(conf.StaticDir) > 0 {
		http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(conf.StaticDir))))
	} else {
		http.Handle("/static/", http.FileServer(FS(false)))
	}

	// Unathenticated routes
	http.HandleFunc("/search/", searchHandler)
	http.HandleFunc("/", makeHandler(viewHandler))
	http.HandleFunc("/view/", makeHandler(viewHandler))
	http.HandleFunc("/history/", makeHandler(historyHandler))

	// Authenticated routes
	http.HandleFunc("/edit/", authenticator.Wrap(makeAuthHandler(editHandler)))
	http.HandleFunc("/save/", authenticator.Wrap(makeAuthHandler(saveHandler)))

	address := serviceAddress(conf.Host, conf.Port)

	log.Printf("Listening on %v\n", address)

	l, err := net.Listen("tcp", address)
	if err != nil {
		log.Fatal(err)
	}

	s := &http.Server{}
	s.Serve(l)
	return
}
<commit_msg>Fix support for themes; messy messy<commit_after>package main

import (
	// stdlib
	"encoding/base64"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"

	// external
	"github.com/VictorLowther/go-git/git"
	auth "github.com/abbot/go-http-auth"
	"github.com/russross/blackfriday"
)

const (
	GOIKIVERSION = "0.2.0"
)

var (
	configFile     string
	displayConfig  bool
	displayVersion bool
	templateFiles  map[string]string
	conf           config
	templates      *template.Template
	validPath      *regexp.Regexp
	validLink      *regexp.Regexp
	tableTag       *regexp.Regexp
	authenticator  *auth.BasicAuth
)

type page struct {
	SiteName    string
	Title       string
	Theme       string
	Author      author
	Body        string
	Description string
	Revisions   []pageRevision
}

type searchPage struct {
	SiteName string
	Title    string
	Theme    string
	Results  []searchResult
}

type historyPage struct {
	SiteName  string
	Title     string
	Theme     string
	Revisions []pageRevision
}

func (p *page) save() error {
	filename := fileName(p.Title)
	datapath := dataPath(conf.DataDir, filename)

	err := os.MkdirAll(filepath.Dir(datapath), 0777)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(datapath, []byte(p.Body), 0600)
	if err != nil {
		return err
	}

	_, err = gitAdd(filename)
	if err != nil {
		return err
	}

	message := p.Description
	if len(message) == 0 {
		message = fmt.Sprintf("Update %s", filename)
	}
	stdout, err := gitCommit(message, p.Author)
	if err != nil {
		return err
	}
	log.Println(stdout)

	return nil
}

func fileName(title string) string {
	return title + "." + conf.FileExtension
}

func dataPath(dir string, file string) string {
	return filepath.Join(dir, file)
}

func loadPage(title string, revision string) (*page, error) {
	filename := fileName(title)
	if len(revision) == 0 {
		revision = "HEAD"
	}
	body, err := gitShow(filename, revision)
	if err != nil {
		return &page{
			Title:    title,
			Theme:    conf.Theme,
			Body:     body.String(),
			SiteName: conf.Name,
		}, fmt.Errorf("Unable to load page content from %s at %s\n", filename, revision)
	}
	return &page{Title: title, Theme: conf.Theme, Body: body.String(), SiteName: conf.Name}, nil
}

func processLinks(content []byte, link *regexp.Regexp) []byte {
	return link.ReplaceAllFunc(content, func(match []byte) []byte {
		return link.ReplaceAll(match, []byte("[$1]($1)"))
	})
}

func processTables(content []byte, table *regexp.Regexp) []byte {
	return table.ReplaceAllFunc(content, func(match []byte) []byte {
		return table.ReplaceAll(match, []byte(`<table class="`+conf.TableClass+`">`))
	})
}

/*
TODO: How to combine the three functions? With use of an interface for the page?
*/

func renderTemplate(w http.ResponseWriter, tmpl string, p *page) {
	err := templates.ExecuteTemplate(w, tmpl, p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func renderHistoryTemplate(w http.ResponseWriter, tmpl string, p *historyPage) {
	err := templates.ExecuteTemplate(w, tmpl, p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func renderSearchTemplate(w http.ResponseWriter, tmpl string, p *searchPage) {
	err := templates.ExecuteTemplate(w, tmpl, p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		path := r.URL.Path
		log.Println(path)
		if path == "/" {
			path = "/view/"
		}
		if path[len(path)-1:len(path)] == "/" {
			viewIndex := path + conf.IndexPage
			http.Redirect(w, r, viewIndex, http.StatusFound)
			return
		}
		m := validPath.FindStringSubmatch(path)
		if m == nil {
			http.NotFound(w, r)
			return
		}
		fn(w, r, m[2])
	}
}

func makeAuthHandler(fn func(http.ResponseWriter, *auth.AuthenticatedRequest, string)) auth.AuthenticatedHandlerFunc {
	return func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
		path := r.URL.Path
		log.Println(path)
		m := validPath.FindStringSubmatch(path)
		if m == nil {
			http.NotFound(w, &r.Request)
			return
		}
		fn(w, r, m[2])
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
	revision := r.FormValue("revision")
	if revision == "" {
		revision = "HEAD"
	}

	p, err := loadPage(title, revision)
	if err != nil {
		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
		return
	}

	content := []byte(p.Body)
	content = processLinks(content, validLink)
	content = blackfriday.MarkdownCommon(content)
	content = processTables(content, tableTag)
	p.Body = string(content)

	renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *auth.AuthenticatedRequest, title string) {
	revision := r.FormValue("revision")
	if revision == "" {
		revision = "HEAD"
	}

	p, err := loadPage(title, revision)
	if err != nil {
		p = &page{Title: title, Theme: conf.Theme, SiteName: conf.Name}
	}

	renderTemplate(w, "edit", p)
}

func saveHandler(w http.ResponseWriter, r *auth.AuthenticatedRequest, title string) {
	body := r.FormValue("body")
	description := r.FormValue("description")
	user := conf.Auth[r.Username]
	author := author{Name: user.Name, Email: user.Email}
	p := &page{Title: title, Theme: conf.Theme, Body: body, Description: description, Author: author}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, &r.Request, "/view/"+title, http.StatusFound)
}

func historyHandler(w http.ResponseWriter, r *http.Request, title string) {
	revisions, _ := gitLog(fileName(title))
	p := &historyPage{Title: title, Theme: conf.Theme, Revisions: revisions, SiteName: conf.Name}
	renderHistoryTemplate(w, "history", p)
}

func searchHandler(w http.ResponseWriter, r *http.Request) {
	search := r.FormValue("search")
	results, err := gitGrep(search)
	if err != nil {
		log.Println("error in search", err)
	}
	p := &searchPage{Title: "Search", Theme: conf.Theme, Results: results, SiteName: conf.Name}
	renderSearchTemplate(w, "search", p)
}

func bundleHandler(w http.ResponseWriter, r *http.Request) {
	p := strings.TrimLeft(r.URL.Path, "/")
	f, ok := _bundle[p]
	if ok {
		w.Write([]byte(f))
	} else {
		http.NotFound(w, r)
	}
}

func defaultConfig() string {
	return _bundle["goiki.toml"]
}

func loadBundle() {
	for file, content := range _bundle {
		c, err := base64.StdEncoding.DecodeString(content)
		if err != nil {
			log.Printf("Error during loadBundle(): %v", err)
		}
		_bundle[file] = string(c)
	}
}

// Initialize the configuration options, templates and URL and link regexes.
func init() {
	flag.StringVar(&configFile, "c", "", "Specify a configuration file (use default config otherwise)")
	flag.BoolVar(&displayConfig, "d", false, "Display default configuration and exit")
	flag.BoolVar(&displayVersion, "v", false, "Display version and exit")

	loadBundle()

	templateFiles = map[string]string{"header": "_header.html", "footer": "_footer.html", "edit": "edit.html",
		"history": "history.html", "search": "search.html", "view": "view.html"}
	validPath = regexp.MustCompile("^/(edit|save|view|history)/([a-zA-Z0-9/_-]+)$")
	validLink = regexp.MustCompile(`\[([^\]]+)]\(\)`)
	tableTag = regexp.MustCompile(`<table>`)
}

func secret(username, realm string) string {
	return conf.Auth[username].Password
}

func serviceAddress(host string, port int) string {
	return fmt.Sprintf("%s:%d", host, port)
}

func main() {
	flag.Parse()

	// Display version and exit (flag -version)
	if displayVersion {
		fmt.Println("Goiki", GOIKIVERSION)
		return
	}

	// Display the default configuration set and exit (flag -default)
	if displayConfig {
		fmt.Println(defaultConfig())
		return
	}

	// Load the configuration. Use the default embedded configuration unless
	// a config file was specified at the command line.
	var err error
	if len(configFile) == 0 {
		conf, err = loadConfig(defaultConfig())
	} else {
		conf, err = loadConfigFromFile(configFile)
	}

	if err != nil {
		fmt.Printf("FATAL: Unable to load configuration: %v\n", err)
		return
	}

	// Load authentication from the config and run the authenticator.
	conf.loadAuth()
	authenticator = auth.NewBasicAuthenticator(serviceAddress(conf.Host, conf.Port), secret)

	// Load the templates. Use the default embedded templates unless a directory
	// of templates is specified in configuration.
	if len(conf.TemplateDir) == 0 {
		templates = template.Must(template.New("goiki").Parse(""))
		for _, file := range templateFiles {
			data, _ := ioutil.ReadFile(filepath.Join("templates", file))
			template.Must(templates.Parse(string(data)))
		}
	} else {
		templateLocations := make([]string, len(templateFiles))
		i := 0
		for _, file := range templateFiles {
			templateLocations[i] = filepath.Join(conf.TemplateDir, file)
			i += 1
		}
		templates = template.Must(template.ParseFiles(templateLocations...))
	}

	// Load the repository.
	if repo, err = git.Open(conf.DataDir); err != nil {
		log.Fatalf("Unable to open the repo at %s. Please check to make sure it exists and is initialized.\n%v", conf.DataDir, err)
	}

	// Static routes
	// If a static directory is provided in the configuration, use that; otherwise
	// use the embedded static content
	if len(conf.StaticDir) > 0 {
		http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(conf.StaticDir))))
	} else {
		http.Handle("/static/", http.FileServer(FS(false)))
	}

	// Unathenticated routes
	http.HandleFunc("/search/", searchHandler)
	http.HandleFunc("/", makeHandler(viewHandler))
	http.HandleFunc("/view/", makeHandler(viewHandler))
	http.HandleFunc("/history/", makeHandler(historyHandler))

	// Authenticated routes
	http.HandleFunc("/edit/", authenticator.Wrap(makeAuthHandler(editHandler)))
	http.HandleFunc("/save/", authenticator.Wrap(makeAuthHandler(saveHandler)))

	address := serviceAddress(conf.Host, conf.Port)

	log.Printf("Listening on %v\n", address)

	l, err := net.Listen("tcp", address)
	if err != nil {
		log.Fatal(err)
	}

	s := &http.Server{}
	s.Serve(l)
	return
}
<|endoftext|>


<commit_before>package golis

import (
	"log"
	"net"
	"sync"
	"time"
)

//系统变量定义
var (
	GolisHandler IoHandler            //事件处理
	GolisPackage Packager             //拆包封包处理接口
	w            WaitGroupWrapper     //等待退出
	runnable     bool                 //服务运行状态
	rwTimeout    time.Duration    = 0 //超时单位秒
)

//定义waitGroup
type WaitGroupWrapper struct {
	sync.WaitGroup
}

//定义session
type Iosession struct {
	SesionId   int32     //session唯一表示
	Connection net.Conn  //连接
	IsAuth     bool      //是否认证成功
	closed     bool      //是否已经关闭
	T          time.Time //最后一次调用时间
}

//session写入数据
func (this *Iosession) Write(message interface{}) {
	//触发消息发送事件
	GolisHandler.MessageSent(this, message)
	this.Connection.Write(GolisPackage.Packet(message))
}

//关闭连接
func (this *Iosession) Close() {
	if !this.closed {
		GolisHandler.SessionClosed(this)
		this.closed = true
	}
	this.Connection.Close()
}

//设置读写超时
func SetTimeout(timeoutSec time.Duration) {
	rwTimeout = timeoutSec
}

//拆包封包接口定义
type Packager interface {
	//读取连接数据
	//packageChan 准备好包后交给该chan
	ReadConnData(buffer *Buffer, packageChan chan<- *[]byte)
	//拆包函数
	Unpacket(data []byte) interface{}
	//封包函数
	Packet(msg interface{}) []byte
}

//事件触发接口定义
type IoHandler interface {
	//session打开
	SessionOpened(session *Iosession)
	//session关闭
	SessionClosed(session *Iosession)
	//收到消息时触发
	MessageReceived(session *Iosession, message interface{})
	//消息发送时触发
	MessageSent(session *Iosession, message interface{})
}

//服务器端运行golis
//netPro：运行协议参数，tcp/udp
//laddr ：程序监听ip和端口，如127.0.0.1:8080
func Run(netPro, laddr string) {
	runnable = true
	log.Println("golis is listen port:", laddr)

	netLis, err := net.Listen(netPro, laddr)
	if err != nil {
		log.Fatalln(err)
	}
	defer netLis.Close()
	log.Println("waiting clients...")
	for runnable {
		conn, err := netLis.Accept()
		if err != nil {
			continue
		}
		go connectHandle(conn)
	}

	w.WaitGroup.Wait()
	log.Println("golis is safe exit")
}

//停止服务
func Stop() {
	runnable = false
}

//客户端程序连接服务器
func Dial(netPro, laddr string) {
	runnable = true
	conn, err := net.Dial(netPro, laddr)
	if err != nil {
		log.Fatalln(err)
	}
	go connectHandle(conn)
}

//处理新连接
func connectHandle(conn net.Conn) {
	w.Add(1)
	//声明一个临时缓冲区，用来存储被截断的数据
	ioBuffer := NewBuffer()

	buffer := make([]byte, 512)

	//声明一个管道用于接收解包的数据
	readerChannel := make(chan *[]byte, 16)
	//创建session
	session := Iosession{Connection: conn}
	//触发sessionCreated事件
	GolisHandler.SessionOpened(&session)

	exitChan := make(chan bool)
	go waitData(&session, readerChannel, exitChan)
	defer func() {
		conn.Close()
		ioBuffer = nil
		buffer = nil
		if err := recover(); err != nil {
			log.Println(err)
		}
		w.Done()
	}()

	for runnable && !session.closed {

		n, err := conn.Read(buffer)
		//设置超时
		//resetTimeout(conn)
		ioBuffer.PutBytes(buffer[:n])
		if err == nil {
			GolisPackage.ReadConnData(ioBuffer, readerChannel)
		} else {
			//session已经关闭
			session.Close()
			exitChan <- true
			return
		}
		if !runnable || session.closed {
			//session关闭
			session.Close()
			exitChan <- true
			return
		}

	}

}

func resetTimeout(conn net.Conn) {
	if rwTimeout == 0 {
		conn.SetDeadline(time.Time{})
	} else {
		conn.SetDeadline(time.Now().Add(time.Duration(rwTimeout) * time.Second))
	}
}

//等待数据包
func waitData(session *Iosession, readerChannel chan *[]byte, exitChan chan bool) {
	defer func() {
		close(exitChan)
		close(readerChannel)
		if err := recover(); err != nil {
			log.Println(err)
		}
	}()
	for runnable && !session.closed {
		select {
		case data := <-readerChannel:
			readFromData(session, data)
		case <-exitChan:
			return
		}
	}
}

//从准备好的数据读取并拆包
func readFromData(session *Iosession, data *[]byte) {
	message := GolisPackage.Unpacket(*data) //拆包
	//收到消息到达时触发事件
	GolisHandler.MessageReceived(session, message)
}
<commit_msg>修改发送消息为chan<commit_after>package golis

import (
	"log"
	"net"
	"sync"
	"time"
)

//系统变量定义
var (
	GolisHandler IoHandler            //事件处理
	GolisPackage Packager             //拆包封包处理接口
	w            WaitGroupWrapper     //等待退出
	runnable     bool                 //服务运行状态
	rwTimeout    time.Duration    = 0 //超时单位秒
)

//定义waitGroup
type WaitGroupWrapper struct {
	sync.WaitGroup
}

//定义session
type Iosession struct {
	SesionId   int32            //session唯一表示
	Connection net.Conn         //连接
	IsAuth     bool             //是否认证成功
	closed     bool             //是否已经关闭
	T          time.Time        //最后一次调用时间
	writeChan  chan interface{} //写入通道
}

//session写入数据
func (this *Iosession) Write(message interface{}) {
	this.writeChan <- message
}

//关闭连接
func (this *Iosession) Close() {
	if !this.closed {
		GolisHandler.SessionClosed(this)
		this.closed = true
	}
	this.Connection.Close()
}

//启动channal发送数据
func (session *Iosession) writeDataToConn() {

	for runnable && !session.closed {
		select {
		case data := <-session.writeChan:
			{
				GolisHandler.MessageSent(session, data)
				session.Connection.Write(GolisPackage.Packet(data))
			}
		}
	}
}

//创建新session
func newIoSession(conn net.Conn) *Iosession {
	session := &Iosession{}
	session.Connection = conn
	session.T = time.Now()
	session.writeChan = make(chan interface{}, 16)
	go session.writeDataToConn()
	return session
}

//设置读写超时
func SetTimeout(timeoutSec time.Duration) {
	rwTimeout = timeoutSec
}

//拆包封包接口定义
type Packager interface {
	//读取连接数据
	//packageChan 准备好包后交给该chan
	ReadConnData(buffer *Buffer, packageChan chan<- *[]byte)
	//拆包函数
	Unpacket(data []byte) interface{}
	//封包函数
	Packet(msg interface{}) []byte
}

//事件触发接口定义
type IoHandler interface {
	//session打开
	SessionOpened(session *Iosession)
	//session关闭
	SessionClosed(session *Iosession)
	//收到消息时触发
	MessageReceived(session *Iosession, message interface{})
	//消息发送时触发
	MessageSent(session *Iosession, message interface{})
}

//服务器端运行golis
//netPro：运行协议参数，tcp/udp
//laddr ：程序监听ip和端口，如127.0.0.1:8080
func Run(netPro, laddr string) {
	runnable = true
	log.Println("golis is listen port:", laddr)

	netLis, err := net.Listen(netPro, laddr)
	if err != nil {
		log.Fatalln(err)
	}
	defer netLis.Close()
	log.Println("waiting clients...")
	for runnable {
		conn, err := netLis.Accept()
		if err != nil {
			continue
		}
		go connectHandle(conn)
	}

	w.WaitGroup.Wait()
	log.Println("golis is safe exit")
}

//停止服务
func Stop() {
	runnable = false
}

//客户端程序连接服务器
func Dial(netPro, laddr string) {
	runnable = true
	conn, err := net.Dial(netPro, laddr)
	if err != nil {
		log.Fatalln(err)
	}
	go connectHandle(conn)
}

//处理新连接
func connectHandle(conn net.Conn) {
	w.Add(1)
	//声明一个临时缓冲区，用来存储被截断的数据
	ioBuffer := NewBuffer()

	buffer := make([]byte, 512)

	//声明一个管道用于接收解包的数据
	readerChannel := make(chan *[]byte, 16)
	//创建session
	session := newIoSession(conn)
	//触发sessionCreated事件
	GolisHandler.SessionOpened(session)

	exitChan := make(chan bool)
	go waitData(session, readerChannel, exitChan)
	defer func() {
		conn.Close()
		ioBuffer = nil
		buffer = nil
		if err := recover(); err != nil {
			log.Println(err)
		}
		w.Done()
	}()

	for runnable && !session.closed {

		n, err := conn.Read(buffer)
		//设置超时
		//resetTimeout(conn)
		ioBuffer.PutBytes(buffer[:n])
		if err == nil {
			GolisPackage.ReadConnData(ioBuffer, readerChannel)
		} else {
			//session已经关闭
			session.Close()
			exitChan <- true
			return
		}
		if !runnable || session.closed {
			//session关闭
			session.Close()
			exitChan <- true
			return
		}

	}

}

//设置超时时间
func resetTimeout(conn net.Conn) {
	if rwTimeout == 0 {
		conn.SetDeadline(time.Time{})
	} else {
		conn.SetDeadline(time.Now().Add(time.Duration(rwTimeout) * time.Second))
	}
}

//等待数据包
func waitData(session *Iosession, readerChannel chan *[]byte, exitChan chan bool) {
	defer func() {
		close(exitChan)
		close(readerChannel)
		if err := recover(); err != nil {
			log.Println(err)
		}
	}()
	for runnable && !session.closed {
		select {
		case data := <-readerChannel:
			readFromData(session, data)
		case <-exitChan:
			return
		}
	}
}

//从准备好的数据读取并拆包
func readFromData(session *Iosession, data *[]byte) {
	message := GolisPackage.Unpacket(*data) //拆包
	//收到消息到达时触发事件
	GolisHandler.MessageReceived(session, message)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"
)

var (
	serverPort     = flag.String("addr", "127.0.0.1:8000", "server address (host:port)")
	backupDirPath  = flag.String("backup", "./backup", "path to backup directory")
	filePath       = flag.String("file", "./programs.json", "path to programs.json")
	sessionTimeout = flag.String("timeout", "5m", "session timeout, i.e 3s, 5m10s, etc..")

	lastAccess         = time.Now()
	lastSessionAddress = "no one"
	protectionTime     = time.Second * 20
)

/**
 * Allow http handlers to return errors directly, instead of:
 * 	log(err)
 * 	response.write(error-message)
 * 	return
 * we just do:
 *	return &appError{err, msg, httpCode}
 */
type appError struct {
	Error   error
	Message string
	Code    int
}

// The actual handler to use above struct
type appHandler func(http.ResponseWriter, *http.Request) *appError

// Http handlers must have a ServeHTTP method, so call the actual handler...
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// ...here!
	if err := fn(w, r); err != nil {
		log.Printf("%s: %v\n", err.Message, err.Error)
		http.Error(w, err.Message, err.Code)
	}
}

// Convert POST data to Program. Convert Program to JSON.
type Program struct {
	Name        string `json:"name"`
	RSS         string `json:"rss"`
	Image       string `json:"image"`
	Description string `json:"description"`
	Category    string `json:"category"`
}

/**
 * Check if a request is allowed or blocked.
 * r.RemoteAddr will probably return different addresses for you when
 * developing, due to localhost requests. But it works as intended when
 * a remote machine requests access.
 */
func accessProtected(r *http.Request) bool {
	// We are the active user
	if r.RemoteAddr == lastSessionAddress {
		return false
	}

	return time.Since(lastAccess) < protectionTime
}
func giveAccess(r *http.Request) {
	lastSessionAddress = r.RemoteAddr
	lastAccess = time.Now()
}

// https://gist.github.com/elazarl/5507969
func cp(d *os.File, src string) error {
	s, err := os.Open(src)
	if err != nil {
		return err
	}
	// no need to check errors on read only file, we already got everything
	// we need from the filesystem, so nothing can go wrong now.
	defer s.Close()
	if _, err := io.Copy(d, s); err != nil {
		d.Close()
		return err
	}
	return d.Close()
}

/**
 * Will put copies in the backup directory.
 * File names are programs.json-randomNumber
 * Match log statements with file creation dates to find specific backups.
 */
func makeBackup() *appError {
	f, err := ioutil.TempFile(*backupDirPath, "programs.json-")
	if err != nil {
		return &appError{err, "Couldn't create backup file", 0}
	}

	if err = cp(f, *filePath); err != nil {
		return &appError{err, "Failed to copy source to backup", 0}
	}

	return nil
}

// Handles GET and POST requests to /programs. Enforces session access.
func handlePrograms(w http.ResponseWriter, r *http.Request) *appError {
	if accessProtected(r) {
		// The client expects the number of seconds left until session timeout.
		dur := fmt.Sprintf("%d", int((protectionTime - time.Since(lastAccess)).Seconds()))
		return &appError{errors.New("Conflicting access"), dur, 400}
	}
	giveAccess(r)

	if r.Method == "GET" {
		data, err := ioutil.ReadFile(*filePath)
		if err != nil {
			return &appError{err, "Coudln't find/read the programs.json file :(", 500}
		}

		w.Write(data)
		return nil
	}

	if r.Method == "POST" {
		if err := r.ParseForm(); err != nil {
			return &appError{err, "Error: Couldn't parse form data, check log and contact the IT monkey", 400}
		}

		// Everything is posted as arrays...
		keys := r.Form["programs[][key]"]
		names := r.Form["programs[][name]"]
		rss := r.Form["programs[][rss]"]
		images := r.Form["programs[][image]"]
		categories := r.Form["programs[][category]"]
		descriptions := r.Form["programs[][description]"]

		// So generate the JSON-like object
		programs := make(map[string]Program)
		for i := 0; i < len(keys); i++ {
			programs[keys[i]] = Program{
				Name:        names[i],
				RSS:         rss[i],
				Image:       images[i],
				Category:    categories[i],
				Description: descriptions[i],
			}
		}

		// And then output nicely formatted JSON
		data, err := json.MarshalIndent(programs, "", "   ")
		if err != nil {
			return &appError{err, "Error: Couldn't convert data to json, check log and contact IT turtle", 500}
		}

		// Try to make a backup for safety purposes, but don't enforce it
		backupErr := makeBackup()
		if backupErr != nil {
			log.Printf("%s: %v\n", backupErr.Message, backupErr.Error)
		}

		// We don't need executable rights, just read-write
		if err = ioutil.WriteFile(*filePath, data, 0644); err != nil {
			return &appError{err, "Couldn't write to programs.json, perhaps some premission error?", 500}
		}

		log.Printf("Wrote %d bytes\n", len(data))
		if backupErr == nil {
			fmt.Fprintf(w, "Success: %d programs saved (~%d kB)", len(programs), len(data)/1000)
		} else {
			fmt.Fprintf(w, "Warning: %d programs saved (~%d kB), but failed to backup.", len(programs), len(data)/1000)
		}
	}

	return nil
}

func main() {
	flag.Parse()

	if _, err := os.Stat(*filePath); os.IsNotExist(err) {
		log.Fatalln("Coudln't find json file in " + *filePath)
	}
	log.Println("Program file path is " + *filePath)

	if _, err := os.Stat(*backupDirPath); os.IsNotExist(err) {
		log.Println("Creating backup directory in " + *backupDirPath)
		if err := os.Mkdir(*backupDirPath, 0744); err != nil {
			log.Printf("Couldn't create backup dir. Error: %v", err)
		}
	}
	log.Println("Backup directory is " + *backupDirPath)

	// Do assignment so that we don't shadow the global...
	var err error
	protectionTime, err = time.ParseDuration(*sessionTimeout)
	if err != nil {
		log.Printf("Failed to parse session time, falling back...%v\n", err)
		protectionTime = 5 * time.Minute
	}

	// Better trick than checking for "no one"
	lastAccess = lastAccess.Add(-protectionTime)
	log.Printf("Session timeout is %v\n", protectionTime)

	// Host files from public dir, but mount them on root (/) path instead
	fs := http.FileServer(http.Dir("./public"))
	http.Handle("/", http.StripPrefix("/", fs))

	http.Handle("/programs", appHandler(handlePrograms))

	log.Printf("Running http server on address %s\n", *serverPort)
	http.ListenAndServe(*serverPort, nil)
}
<commit_msg>Updated errors to use net/http status codes<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"
)

var (
	serverPort     = flag.String("addr", "127.0.0.1:8000", "server address (host:port)")
	backupDirPath  = flag.String("backup", "./backup", "path to backup directory")
	filePath       = flag.String("file", "./programs.json", "path to programs.json")
	sessionTimeout = flag.String("timeout", "5m", "session timeout, i.e 3s, 5m10s, etc..")

	lastAccess         = time.Now()
	lastSessionAddress = "no one"
	protectionTime     = time.Second * 20
)

/**
 * Allow http handlers to return errors directly, instead of:
 * 	log(err)
 * 	response.write(error-message)
 * 	return
 * we just do:
 *	return &appError{err, msg, httpCode}
 */
type appError struct {
	Error   error
	Message string
	Code    int
}

// The actual handler to use above struct
type appHandler func(http.ResponseWriter, *http.Request) *appError

// Http handlers must have a ServeHTTP method, so call the actual handler...
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// ...here!
	if err := fn(w, r); err != nil {
		log.Printf("%s: %v\n", err.Message, err.Error)
		http.Error(w, err.Message, err.Code)
	}
}

// Convert POST data to Program. Convert Program to JSON.
type Program struct {
	Name        string `json:"name"`
	RSS         string `json:"rss"`
	Image       string `json:"image"`
	Description string `json:"description"`
	Category    string `json:"category"`
}

/**
 * Check if a request is allowed or blocked.
 * r.RemoteAddr will probably return different addresses for you when
 * developing, due to localhost requests. But it works as intended when
 * a remote machine requests access.
 */
func accessProtected(r *http.Request) bool {
	// We are the active user
	if r.RemoteAddr == lastSessionAddress {
		return false
	}

	return time.Since(lastAccess) < protectionTime
}
func giveAccess(r *http.Request) {
	lastSessionAddress = r.RemoteAddr
	lastAccess = time.Now()
}

// https://gist.github.com/elazarl/5507969
func cp(d *os.File, src string) error {
	s, err := os.Open(src)
	if err != nil {
		return err
	}
	// no need to check errors on read only file, we already got everything
	// we need from the filesystem, so nothing can go wrong now.
	defer s.Close()
	if _, err := io.Copy(d, s); err != nil {
		d.Close()
		return err
	}
	return d.Close()
}

/**
 * Will put copies in the backup directory.
 * File names are programs.json-randomNumber
 * Match log statements with file creation dates to find specific backups.
 */
func makeBackup() *appError {
	f, err := ioutil.TempFile(*backupDirPath, "programs.json-")
	if err != nil {
		return &appError{err, "Couldn't create backup file", 0}
	}

	if err = cp(f, *filePath); err != nil {
		return &appError{err, "Failed to copy source to backup", 0}
	}

	return nil
}

// Handles GET and POST requests to /programs. Enforces session access.
func handlePrograms(w http.ResponseWriter, r *http.Request) *appError {
	if accessProtected(r) {
		// The client expects the number of seconds left until session timeout.
		dur := fmt.Sprintf("%d", int((protectionTime - time.Since(lastAccess)).Seconds()))
		return &appError{errors.New("Conflicting access"), dur, http.StatusConflict}
	}
	giveAccess(r)

	if r.Method == "GET" {
		data, err := ioutil.ReadFile(*filePath)
		if err != nil {
			return &appError{
				err,
				"Error: Coudln't find/read the programs.json file :(",
				http.StatusInternalServerError}
		}

		w.Write(data)
		return nil
	}

	if r.Method == "POST" {
		if err := r.ParseForm(); err != nil {
			return &appError{err,
				"Error: Couldn't parse form data, contact the IT monkey",
				http.StatusBadRequest}
		}

		// Everything is posted as arrays...
		keys := r.Form["programs[][key]"]
		names := r.Form["programs[][name]"]
		rss := r.Form["programs[][rss]"]
		images := r.Form["programs[][image]"]
		categories := r.Form["programs[][category]"]
		descriptions := r.Form["programs[][description]"]

		// So generate the JSON-like object
		programs := make(map[string]Program)
		for i := 0; i < len(keys); i++ {
			programs[keys[i]] = Program{
				Name:        names[i],
				RSS:         rss[i],
				Image:       images[i],
				Category:    categories[i],
				Description: descriptions[i],
			}
		}

		// And then output nicely formatted JSON
		data, err := json.MarshalIndent(programs, "", "   ")
		if err != nil {
			return &appError{err,
				"Error: Couldn't convert data to json, contact IT turtle",
				http.StatusInternalServerError}
		}

		// Try to make a backup for safety purposes, but don't enforce it
		backupErr := makeBackup()
		if backupErr != nil {
			log.Printf("%s: %v\n", backupErr.Message, backupErr.Error)
		}

		// We don't need executable rights, just read-write
		if err = ioutil.WriteFile(*filePath, data, 0644); err != nil {
			return &appError{err,
				"Error: Couldn't write to programs.json, contact IT shibe",
				http.StatusInternalServerError}
		}

		log.Printf("Wrote %d bytes\n", len(data))
		if backupErr == nil {
			fmt.Fprintf(w, "Success: %d programs saved (~%d kB)", len(programs), len(data)/1000)
		} else {
			fmt.Fprintf(w, "Warning: %d programs saved (~%d kB), but failed to backup.", len(programs), len(data)/1000)
		}
	}

	return nil
}

func main() {
	flag.Parse()

	if _, err := os.Stat(*filePath); os.IsNotExist(err) {
		log.Fatalln("Coudln't find json file in " + *filePath)
	}
	log.Println("Program file path is " + *filePath)

	if _, err := os.Stat(*backupDirPath); os.IsNotExist(err) {
		log.Println("Creating backup directory in " + *backupDirPath)
		if err := os.Mkdir(*backupDirPath, 0744); err != nil {
			log.Printf("Couldn't create backup dir. Error: %v", err)
		}
	}
	log.Println("Backup directory is " + *backupDirPath)

	// Do assignment so that we don't shadow the global...
	var err error
	protectionTime, err = time.ParseDuration(*sessionTimeout)
	if err != nil {
		log.Printf("Failed to parse session time, falling back...%v\n", err)
		protectionTime = 5 * time.Minute
	}

	// Better trick than checking for "no one"
	lastAccess = lastAccess.Add(-protectionTime)
	log.Printf("Session timeout is %v\n", protectionTime)

	// Host files from public dir, but mount them on root (/) path instead
	fs := http.FileServer(http.Dir("./public"))
	http.Handle("/", http.StripPrefix("/", fs))

	http.Handle("/programs", appHandler(handlePrograms))

	log.Printf("Running http server on address %s\n", *serverPort)
	http.ListenAndServe(*serverPort, nil)
}
<|endoftext|>


<commit_before>package gorai

import (
	"github.com/facebookgo/grace/gracehttp"
	"github.com/go51/container551"
	"github.com/go51/cookie551"
	"github.com/go51/log551"
	"github.com/go51/memcache551"
	"github.com/go51/model551"
	"github.com/go51/mysql551"
	"github.com/go51/response551"
	"github.com/go51/router551"
	"github.com/go51/secure551"
	"net/http"
	"time"
)

type gorai struct {
	config       *Config
	logger       *log551.Log551
	router       *router551.Router
	modelManager *model551.Model
}

var goraiInstance *gorai = nil

func Load(appConfig ...interface{}) *gorai {
	if goraiInstance != nil {
		return goraiInstance
	}

	goraiInstance = &gorai{}

	goraiInstance.initialize(appConfig[0])

	return goraiInstance
}

func (g *gorai) initialize(appConfig interface{}) {
	// load config
	g.config = loadConfig(appConfig)

	// Logger
	g.logger = log551.New(&g.config.Framework.SystemLog)
	g.logger.Open()
	defer g.logger.Close()

	g.logger.Information("--[ initialize gorai - START ]--")
	g.logger.Information("Success! [Log551]")

	// Router
	g.router = router551.Load()
	g.logger.Information("Success! [Router551]")

	// Router
	g.modelManager = model551.Load()
	g.logger.Information("Success! [Model551]")

	g.logger.Information("--[ initialize gorai - END   ]--")
}

func (g *gorai) Run() {
	server := &http.Server{
		Addr:         g.config.Framework.WebServer.Host + ":" + g.config.Framework.WebServer.Port,
		Handler:      webHandler(),
		ReadTimeout:  g.config.Framework.WebServer.ReadTimeout * time.Second,
		WriteTimeout: g.config.Framework.WebServer.WriteTimeout * time.Second,
	}
	gracehttp.Serve(server)

	g.logger.Close()
}

func webHandler() http.Handler {
	mux := http.NewServeMux()

	mux.HandleFunc("/static/", staticResource)
	mux.HandleFunc("/", rootFunc)

	return mux
}

func staticResource(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Cache-Control-Max-Age", "10")
	http.ServeFile(w, r, r.URL.Path[1:])

}

func rootFunc(w http.ResponseWriter, r *http.Request) {

	g := Load()

	l := log551.New(&g.config.Framework.SystemLog)
	l.Open()
	defer l.Close()

	mysql := mysql551.New(&g.config.Framework.Database)

	cookie := cookie551.New(w, r)

	sid := g.sid(cookie)
	sidShort := sid[:10]
	l.Debugf("%s SID: %s", sidShort, sid)

	session := memcache551.New(&g.config.Framework.Session.Server, sid)

	route := g.router.FindRouteByPathMatch(r.Method, r.URL.Path)

	var data interface{} = nil
	if route != nil {
		l.Debugf("%s --[ Routing ]--", sidShort)
		l.Debugf("%s Path: %s", sidShort, r.URL.Path)
		l.Debugf("%s Neme: %s", sidShort, route.Name())
		c := container551.New()
		c.SetSID(sid)
		c.SetResponseWriter(w)
		c.SetRequest(r)
		c.SetLogger(l)
		c.SetLogger(l)
		c.SetCookie(cookie)
		c.SetDb(mysql)
		c.SetSession(session)

		action := route.Action()
		data = action(c)
		response551.Response(w, r, data, route.PackageName(), route.Name())
	} else {
		l.Debugf("%s --[ Routing ]--", sidShort)
		l.Debugf("%s Path: %s", sidShort, r.URL.Path)
		l.Debugf("%s Neme: Route not found.", sidShort)
		data = response551.Error(404, "Page, Action not found.")
		response551.Response(w, r, data, "", "")
	}

}

func (g *gorai) Config() *Config {
	return g.config
}

func (g *gorai) Logger() *log551.Log551 {
	return g.logger
}

func (g *gorai) Router() *router551.Router {
	return g.router
}

func (g *gorai) sid(cookie *cookie551.Cookie) string {
	sid, err := cookie.Get(g.config.Framework.Session.CookieKeyName)
	if err == nil {
		return sid
	}

	sid = secure551.Hash()

	cookie.Set(g.config.Framework.Session.CookieKeyName, sid, 60*60*24*365)

	return sid

}

func (g *gorai) ModelManager() *model551.Model {
	return g.modelManager
}
<commit_msg>refs #26 リクエスト時に modelManager を container551 へ保持させる<commit_after>package gorai

import (
	"github.com/facebookgo/grace/gracehttp"
	"github.com/go51/container551"
	"github.com/go51/cookie551"
	"github.com/go51/log551"
	"github.com/go51/memcache551"
	"github.com/go51/model551"
	"github.com/go51/mysql551"
	"github.com/go51/response551"
	"github.com/go51/router551"
	"github.com/go51/secure551"
	"net/http"
	"time"
)

type gorai struct {
	config       *Config
	logger       *log551.Log551
	router       *router551.Router
	modelManager *model551.Model
}

var goraiInstance *gorai = nil

func Load(appConfig ...interface{}) *gorai {
	if goraiInstance != nil {
		return goraiInstance
	}

	goraiInstance = &gorai{}

	goraiInstance.initialize(appConfig[0])

	return goraiInstance
}

func (g *gorai) initialize(appConfig interface{}) {
	// load config
	g.config = loadConfig(appConfig)

	// Logger
	g.logger = log551.New(&g.config.Framework.SystemLog)
	g.logger.Open()
	defer g.logger.Close()

	g.logger.Information("--[ initialize gorai - START ]--")
	g.logger.Information("Success! [Log551]")

	// Router
	g.router = router551.Load()
	g.logger.Information("Success! [Router551]")

	// Router
	g.modelManager = model551.Load()
	g.logger.Information("Success! [Model551]")

	g.logger.Information("--[ initialize gorai - END   ]--")
}

func (g *gorai) Run() {
	server := &http.Server{
		Addr:         g.config.Framework.WebServer.Host + ":" + g.config.Framework.WebServer.Port,
		Handler:      webHandler(),
		ReadTimeout:  g.config.Framework.WebServer.ReadTimeout * time.Second,
		WriteTimeout: g.config.Framework.WebServer.WriteTimeout * time.Second,
	}
	gracehttp.Serve(server)

	g.logger.Close()
}

func webHandler() http.Handler {
	mux := http.NewServeMux()

	mux.HandleFunc("/static/", staticResource)
	mux.HandleFunc("/", rootFunc)

	return mux
}

func staticResource(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Cache-Control-Max-Age", "10")
	http.ServeFile(w, r, r.URL.Path[1:])

}

func rootFunc(w http.ResponseWriter, r *http.Request) {

	g := Load()

	l := log551.New(&g.config.Framework.SystemLog)
	l.Open()
	defer l.Close()

	mysql := mysql551.New(&g.config.Framework.Database)

	cookie := cookie551.New(w, r)

	sid := g.sid(cookie)
	sidShort := sid[:10]
	l.Debugf("%s SID: %s", sidShort, sid)

	session := memcache551.New(&g.config.Framework.Session.Server, sid)

	route := g.router.FindRouteByPathMatch(r.Method, r.URL.Path)

	var data interface{} = nil
	if route != nil {
		l.Debugf("%s --[ Routing ]--", sidShort)
		l.Debugf("%s Path: %s", sidShort, r.URL.Path)
		l.Debugf("%s Neme: %s", sidShort, route.Name())
		c := container551.New()
		c.SetSID(sid)
		c.SetResponseWriter(w)
		c.SetRequest(r)
		c.SetLogger(l)
		c.SetLogger(l)
		c.SetCookie(cookie)
		c.SetDb(mysql)
		c.SetSession(session)
		c.SetModel(g.modelManager)

		action := route.Action()
		data = action(c)
		response551.Response(w, r, data, route.PackageName(), route.Name())
	} else {
		l.Debugf("%s --[ Routing ]--", sidShort)
		l.Debugf("%s Path: %s", sidShort, r.URL.Path)
		l.Debugf("%s Neme: Route not found.", sidShort)
		data = response551.Error(404, "Page, Action not found.")
		response551.Response(w, r, data, "", "")
	}

}

func (g *gorai) Config() *Config {
	return g.config
}

func (g *gorai) Logger() *log551.Log551 {
	return g.logger
}

func (g *gorai) Router() *router551.Router {
	return g.router
}

func (g *gorai) sid(cookie *cookie551.Cookie) string {
	sid, err := cookie.Get(g.config.Framework.Session.CookieKeyName)
	if err == nil {
		return sid
	}

	sid = secure551.Hash()

	cookie.Set(g.config.Framework.Session.CookieKeyName, sid, 60*60*24*365)

	return sid

}
<|endoftext|>


<commit_before>package main

import (
    "io"
    "io/ioutil"
    "os"
    "fmt"
    "flag"
    "path/filepath"
    "strings"

    "bitbucket.org/lmika/goseq/seqdiagram"
)

// Name of the output file
var flagOut = flag.String("o", "", "Output file")

// The style to use
var flagStyle = flag.String("s", "default", "The style to use")

// Generate an embedded SVG file
var flagEmbedded = flag.Bool("e", false, "Generate an embedded SVG file")

// Die with error
func die(msg string) {
    fmt.Fprintf(os.Stderr, "goseq: %s\n", msg)
    os.Exit(1)
}

// Construct and build image options based on the current configuration
func buildImageOptions() *seqdiagram.ImageOptions {
    // Work out the style
    style := seqdiagram.DefaultStyle
    if altStyle, hasStyle := seqdiagram.StyleNames[*flagStyle] ; hasStyle {
        style = altStyle
    }

    return &seqdiagram.ImageOptions {
        Style: style,
        Embedded: *flagEmbedded,
    }
}

// Processes a md file
func processMdFile(inFilename string, outFilename string, renderer Renderer) error {
    srcFile, err := openSourceFile(inFilename)
    if err != nil {
        return err
    }
    defer srcFile.Close()

    targetFile := ioutil.Discard

    mf := &MarkdownFilter{srcFile, targetFile, func(codeblock string, output io.Writer) error {
        fmt.Fprint(output, codeblock)
        err := processSeqDiagram(strings.NewReader(codeblock), inFilename, "/dev/null", nil)
        if err != nil {
            fmt.Fprintf(os.Stderr, "goseq: %s:embedded block - %s\n", inFilename, err.Error())
        }
        return nil
    }}
    return mf.Scan()
}

// Processes a seq file
func processSeqFile(inFilename string, outFilename string, renderer Renderer) error {
    srcFile, err := openSourceFile(inFilename)
    if err != nil {
        return err
    }
    defer srcFile.Close()

    return processSeqDiagram(srcFile, inFilename, outFilename, renderer)
}

// Processes a sequence diagram
func processSeqDiagram(infile io.Reader, inFilename string, outFilename string, renderer Renderer) error {
    diagram, err := seqdiagram.ParseDiagram(infile, inFilename)
    if err != nil {
        return err
    }

    // Image options
    imageOptions := buildImageOptions()

    // If there's a process instruction, use it as the target of the diagram
    // TODO: be a little smarter with the process instructions
    for _, pr := range diagram.ProcessingInstructions {
        if pr.Prefix == "goseq" {
            outFilename = pr.Value
        }
    }

    if renderer == nil {
        renderer, err = chooseRendererBaseOnOutfile(outFilename)
        if err != nil {
            return err
        }
    }

    err = renderer(diagram, imageOptions, outFilename)
    if err != nil {
        return err
    }

    return nil
}

// Processes a file.  This switches based on the file extension
func processFile(inFilename string, outFilename string, renderer Renderer) error {
    ext := filepath.Ext(inFilename)
    if ext == ".md" {
        return processMdFile(inFilename, outFilename, renderer)
    } else {
        return processSeqFile(inFilename, outFilename, renderer)
    }
}

func main() {
    var err error

    renderer := SvgRenderer
    outFile := ""

    flag.Parse()

    // Select a suitable renderer (based on the suffix of the output file, if there is one)
    if *flagOut != "" {
        renderer, err = chooseRendererBaseOnOutfile(*flagOut)
        if err != nil {
            die(err.Error())
        }
        outFile = *flagOut
    }

    // Process each file (or stdin)
    if flag.NArg() == 0 {
        err := processFile("-", outFile, renderer)
        if err != nil {
            die("stdin - " + err.Error())
        }
    } else {
        for _, inFile := range flag.Args() {
            err := processFile(inFile, outFile, renderer)
            if err != nil {
                die(inFile + " - " + err.Error())
            }
        }
    }
}<commit_msg>Started work on a watch service<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"bitbucket.org/lmika/goseq/seqdiagram"
	"github.com/howeyc/fsnotify"
)

// Name of the output file
var flagOut = flag.String("o", "", "Output file")

// The style to use
var flagStyle = flag.String("s", "default", "The style to use")

// Generate an embedded SVG file
var flagEmbedded = flag.Bool("e", false, "Generate an embedded SVG file")

// Setup a watcher to regenerate the file when changed
var flagWatch = flag.Bool("w", false, "Watch for changes")

// Die with error
func die(msg string) {
	fmt.Fprintf(os.Stderr, "goseq: %s\n", msg)
	os.Exit(1)
}

// Construct and build image options based on the current configuration
func buildImageOptions() *seqdiagram.ImageOptions {
	// Work out the style
	style := seqdiagram.DefaultStyle
	if altStyle, hasStyle := seqdiagram.StyleNames[*flagStyle]; hasStyle {
		style = altStyle
	}

	return &seqdiagram.ImageOptions{
		Style:    style,
		Embedded: *flagEmbedded,
	}
}

// Processes a md file
func processMdFile(inFilename string, outFilename string, renderer Renderer) error {
	srcFile, err := openSourceFile(inFilename)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	targetFile := ioutil.Discard

	mf := &MarkdownFilter{srcFile, targetFile, func(codeblock string, output io.Writer) error {
		fmt.Fprint(output, codeblock)
		err := processSeqDiagram(strings.NewReader(codeblock), inFilename, "/dev/null", nil)
		if err != nil {
			fmt.Fprintf(os.Stderr, "goseq: %s:embedded block - %s\n", inFilename, err.Error())
		}
		return nil
	}}
	return mf.Scan()
}

// Processes a seq file
func processSeqFile(inFilename string, outFilename string, renderer Renderer) error {
	srcFile, err := openSourceFile(inFilename)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	return processSeqDiagram(srcFile, inFilename, outFilename, renderer)
}

// Processes a sequence diagram
func processSeqDiagram(infile io.Reader, inFilename string, outFilename string, renderer Renderer) error {
	diagram, err := seqdiagram.ParseDiagram(infile, inFilename)
	if err != nil {
		return err
	}

	// Image options
	imageOptions := buildImageOptions()

	// If there's a process instruction, use it as the target of the diagram
	// TODO: be a little smarter with the process instructions
	for _, pr := range diagram.ProcessingInstructions {
		if pr.Prefix == "goseq" {
			outFilename = pr.Value
		}
	}

	if renderer == nil {
		renderer, err = chooseRendererBaseOnOutfile(outFilename)
		if err != nil {
			return err
		}
	}

	err = renderer(diagram, imageOptions, outFilename)
	if err != nil {
		return err
	}

	return nil
}

// Processes a file.  This switches based on the file extension
func processFile(inFilename string, outFilename string, renderer Renderer) error {
	ext := filepath.Ext(inFilename)
	if ext == ".md" {
		return processMdFile(inFilename, outFilename, renderer)
	} else {
		return processSeqFile(inFilename, outFilename, renderer)
	}
}

// Setup a watch process which will regenerate the files
func watchAndProcess(inFiles []string, outFile string, renderer Renderer) error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	for _, inFile := range inFiles {
		watcher.Watch(inFile)
	}

	for {
		select {
		case event := <-watcher.Event:
			if event.IsModify() {
				log.Println("Generating ", event.Name, "->", outFile)
				processFile(event.Name, outFile, renderer)
			}
		}
	}
}

func main() {
	var err error

	renderer := SvgRenderer
	outFile := ""

	flag.Parse()

	// Select a suitable renderer (based on the suffix of the output file, if there is one)
	if *flagOut != "" {
		renderer, err = chooseRendererBaseOnOutfile(*flagOut)
		if err != nil {
			die(err.Error())
		}
		outFile = *flagOut
	}

	// Process each file (or stdin)
	if flag.NArg() == 0 {
		err := processFile("-", outFile, renderer)
		if err != nil {
			die("stdin - " + err.Error())
		}
	} else {
		if *flagWatch {
			watchAndProcess(flag.Args(), outFile, renderer)
		} else {
			for _, inFile := range flag.Args() {
				err := processFile(inFile, outFile, renderer)
				if err != nil {
					die(inFile + " - " + err.Error())
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"time"

	"github.com/hashicorp/vault/api"
)

const (
	// DefaultVaultGrace is the default grace period before which to read a new
	// secret from Vault. If a lease is due to expire in 15 seconds, Consul
	// Template will read a new secret at that time minus this value.
	DefaultVaultGrace = 15 * time.Second

	// DefaultVaultRenewToken is the default value for if the Vault token should
	// be renewed.
	DefaultVaultRenewToken = true

	// DefaultVaultUnwrapToken is the default value for if the Vault token should
	// be unwrapped.
	DefaultVaultUnwrapToken = false

	// DefaultVaultRetryBase is the default value for the base time to use for
	// exponential backoff.
	DefaultVaultRetryBase = 250 * time.Millisecond

	// DefaultVaultRetryMaxAttempts is the default maximum number of attempts to
	// retry before quitting.
	DefaultVaultRetryMaxAttempts = 5
)

// VaultConfig is the configuration for connecting to a vault server.
type VaultConfig struct {
	// Address is the URI to the Vault server.
	Address *string `mapstructure:"address"`

	// Enabled controls whether the Vault integration is active.
	Enabled *bool `mapstructure:"enabled"`

	// Grace is the amount of time before a lease is about to expire to force a
	// new secret to be read.
	Grace *time.Duration `mapstructure:"grace"`

	// Namespace is the Vault namespace to use for reading/writing secrets. This can
	// also be set via the VAULT_NAMESPACE environment variable.
	Namespace *string `mapstructure:"namespace"`

	// RenewToken renews the Vault token.
	RenewToken *bool `mapstructure:"renew_token"`

	// Retry is the configuration for specifying how to behave on failure.
	Retry *RetryConfig `mapstructure:"retry"`

	// SSL indicates we should use a secure connection while talking to Vault.
	SSL *SSLConfig `mapstructure:"ssl"`

	// Token is the Vault token to communicate with for requests. It may be
	// a wrapped token or a real token. This can also be set via the VAULT_TOKEN
	// environment variable, or via the VaultAgentTokenFile.
	Token *string `mapstructure:"token" json:"-"`

	// VaultAgentTokenFile is the path of file that contains a Vault Agent token.
	// If vault_agent_token_file is specified:
	//   - Consul Template will not try to renew the Vault token.
	//   - Consul Template will periodically stat the file and update the token if it has
	// changed.
	VaultAgentTokenFile *string `mapstructure:"vault_agent_token_file" json:"-"`

	// Transport configures the low-level network connection details.
	Transport *TransportConfig `mapstructure:"transport"`

	// UnwrapToken unwraps the provided Vault token as a wrapped token.
	UnwrapToken *bool `mapstructure:"unwrap_token"`
}

// DefaultVaultConfig returns a configuration that is populated with the
// default values.
func DefaultVaultConfig() *VaultConfig {
	v := &VaultConfig{
		Retry:     DefaultRetryConfig(),
		SSL:       DefaultSSLConfig(),
		Transport: DefaultTransportConfig(),
	}

	// Force SSL when communicating with Vault.
	v.SSL.Enabled = Bool(true)

	return v
}

// Copy returns a deep copy of this configuration.
func (c *VaultConfig) Copy() *VaultConfig {
	if c == nil {
		return nil
	}

	var o VaultConfig
	o.Address = c.Address

	o.Enabled = c.Enabled

	o.Grace = c.Grace

	o.Namespace = c.Namespace

	o.RenewToken = c.RenewToken

	if c.Retry != nil {
		o.Retry = c.Retry.Copy()
	}

	if c.SSL != nil {
		o.SSL = c.SSL.Copy()
	}

	o.Token = c.Token

	o.VaultAgentTokenFile = c.VaultAgentTokenFile

	if c.Transport != nil {
		o.Transport = c.Transport.Copy()
	}

	o.UnwrapToken = c.UnwrapToken

	return &o
}

// Merge combines all values in this configuration with the values in the other
// configuration, with values in the other configuration taking precedence.
// Maps and slices are merged, most other values are overwritten. Complex
// structs define their own merge functionality.
func (c *VaultConfig) Merge(o *VaultConfig) *VaultConfig {
	if c == nil {
		if o == nil {
			return nil
		}
		return o.Copy()
	}

	if o == nil {
		return c.Copy()
	}

	r := c.Copy()

	if o.Address != nil {
		r.Address = o.Address
	}

	if o.Enabled != nil {
		r.Enabled = o.Enabled
	}

	if o.Grace != nil {
		r.Grace = o.Grace
	}

	if o.Namespace != nil {
		r.Namespace = o.Namespace
	}

	if o.RenewToken != nil {
		r.RenewToken = o.RenewToken
	}

	if o.Retry != nil {
		r.Retry = r.Retry.Merge(o.Retry)
	}

	if o.SSL != nil {
		r.SSL = r.SSL.Merge(o.SSL)
	}

	if o.Token != nil {
		r.Token = o.Token
	}

	if o.VaultAgentTokenFile != nil {
		r.VaultAgentTokenFile = o.VaultAgentTokenFile
	}

	if o.Transport != nil {
		r.Transport = r.Transport.Merge(o.Transport)
	}

	if o.UnwrapToken != nil {
		r.UnwrapToken = o.UnwrapToken
	}

	return r
}

// Finalize ensures there no nil pointers.
func (c *VaultConfig) Finalize() {
	if c.Address == nil {
		c.Address = stringFromEnv([]string{
			api.EnvVaultAddress,
		}, "")
	}

	if c.Grace == nil {
		c.Grace = TimeDuration(DefaultVaultGrace)
	}

	if c.Namespace == nil {
		c.Namespace = stringFromEnv([]string{"VAULT_NAMESPACE"}, "")
	}

	if c.RenewToken == nil {
		c.RenewToken = boolFromEnv([]string{
			"VAULT_RENEW_TOKEN",
		}, DefaultVaultRenewToken)
	}

	if c.Retry == nil {
		c.Retry = DefaultRetryConfig()
	}
	c.Retry.Finalize()

	// Vault has custom SSL settings
	if c.SSL == nil {
		c.SSL = DefaultSSLConfig()
	}
	if c.SSL.Enabled == nil {
		c.SSL.Enabled = Bool(true)
	}
	if c.SSL.CaCert == nil {
		c.SSL.CaCert = stringFromEnv([]string{api.EnvVaultCACert}, "")
	}
	if c.SSL.CaPath == nil {
		c.SSL.CaPath = stringFromEnv([]string{api.EnvVaultCAPath}, "")
	}
	if c.SSL.Cert == nil {
		c.SSL.Cert = stringFromEnv([]string{api.EnvVaultClientCert}, "")
	}
	if c.SSL.Key == nil {
		c.SSL.Key = stringFromEnv([]string{api.EnvVaultClientKey}, "")
	}
	if c.SSL.ServerName == nil {
		c.SSL.ServerName = stringFromEnv([]string{api.EnvVaultTLSServerName}, "")
	}
	if c.SSL.Verify == nil {
		c.SSL.Verify = antiboolFromEnv([]string{api.EnvVaultInsecure}, true)
	}
	c.SSL.Finalize()

	// Order of precedence
	// 1. `vault_agent_token_file` configuration value
	// 2. `token` configuration value`
	// 3. `VAULT_TOKEN` environment variable
	if c.Token == nil {
		c.Token = stringFromEnv([]string{
			"VAULT_TOKEN",
		}, "")
	}

	if c.VaultAgentTokenFile == nil {
		if StringVal(c.Token) == "" {
			if homePath != "" {
				c.Token = stringFromFile([]string{
					homePath + "/.vault-token",
				}, "")
			}
		}
	} else {
		c.Token = stringFromFile([]string{*c.VaultAgentTokenFile}, "")
	}

	if c.Transport == nil {
		c.Transport = DefaultTransportConfig()
	}
	c.Transport.Finalize()

	if c.UnwrapToken == nil {
		c.UnwrapToken = boolFromEnv([]string{
			"VAULT_UNWRAP_TOKEN",
		}, DefaultVaultUnwrapToken)
	}

	if c.Enabled == nil {
		c.Enabled = Bool(StringPresent(c.Address))
	}
}

// GoString defines the printable version of this struct.
func (c *VaultConfig) GoString() string {
	if c == nil {
		return "(*VaultConfig)(nil)"
	}

	return fmt.Sprintf("&VaultConfig{"+
		"Address:%s, "+
		"Enabled:%s, "+
		"Grace:%s, "+
		"Namespace:%s,"+
		"RenewToken:%s, "+
		"Retry:%#v, "+
		"SSL:%#v, "+
		"Token:%t, "+
		"VaultAgentTokenFile:%t, "+
		"Transport:%#v, "+
		"UnwrapToken:%s"+
		"}",
		StringGoString(c.Address),
		BoolGoString(c.Enabled),
		TimeDurationGoString(c.Grace),
		StringGoString(c.Namespace),
		BoolGoString(c.RenewToken),
		c.Retry,
		c.SSL,
		StringPresent(c.Token),
		StringPresent(c.VaultAgentTokenFile),
		c.Transport,
		BoolGoString(c.UnwrapToken),
	)
}
<commit_msg>keep renew = false if someone sets the AgentTokenFile path<commit_after>package config

import (
	"fmt"
	"time"

	"github.com/hashicorp/vault/api"
)

const (
	// DefaultVaultGrace is the default grace period before which to read a new
	// secret from Vault. If a lease is due to expire in 15 seconds, Consul
	// Template will read a new secret at that time minus this value.
	DefaultVaultGrace = 15 * time.Second

	// DefaultVaultRenewToken is the default value for if the Vault token should
	// be renewed.
	DefaultVaultRenewToken = true

	// DefaultVaultUnwrapToken is the default value for if the Vault token should
	// be unwrapped.
	DefaultVaultUnwrapToken = false

	// DefaultVaultRetryBase is the default value for the base time to use for
	// exponential backoff.
	DefaultVaultRetryBase = 250 * time.Millisecond

	// DefaultVaultRetryMaxAttempts is the default maximum number of attempts to
	// retry before quitting.
	DefaultVaultRetryMaxAttempts = 5
)

// VaultConfig is the configuration for connecting to a vault server.
type VaultConfig struct {
	// Address is the URI to the Vault server.
	Address *string `mapstructure:"address"`

	// Enabled controls whether the Vault integration is active.
	Enabled *bool `mapstructure:"enabled"`

	// Grace is the amount of time before a lease is about to expire to force a
	// new secret to be read.
	Grace *time.Duration `mapstructure:"grace"`

	// Namespace is the Vault namespace to use for reading/writing secrets. This can
	// also be set via the VAULT_NAMESPACE environment variable.
	Namespace *string `mapstructure:"namespace"`

	// RenewToken renews the Vault token.
	RenewToken *bool `mapstructure:"renew_token"`

	// Retry is the configuration for specifying how to behave on failure.
	Retry *RetryConfig `mapstructure:"retry"`

	// SSL indicates we should use a secure connection while talking to Vault.
	SSL *SSLConfig `mapstructure:"ssl"`

	// Token is the Vault token to communicate with for requests. It may be
	// a wrapped token or a real token. This can also be set via the VAULT_TOKEN
	// environment variable, or via the VaultAgentTokenFile.
	Token *string `mapstructure:"token" json:"-"`

	// VaultAgentTokenFile is the path of file that contains a Vault Agent token.
	// If vault_agent_token_file is specified:
	//   - Consul Template will not try to renew the Vault token.
	//   - Consul Template will periodically stat the file and update the token if it has
	// changed.
	VaultAgentTokenFile *string `mapstructure:"vault_agent_token_file" json:"-"`

	// Transport configures the low-level network connection details.
	Transport *TransportConfig `mapstructure:"transport"`

	// UnwrapToken unwraps the provided Vault token as a wrapped token.
	UnwrapToken *bool `mapstructure:"unwrap_token"`
}

// DefaultVaultConfig returns a configuration that is populated with the
// default values.
func DefaultVaultConfig() *VaultConfig {
	v := &VaultConfig{
		Retry:     DefaultRetryConfig(),
		SSL:       DefaultSSLConfig(),
		Transport: DefaultTransportConfig(),
	}

	// Force SSL when communicating with Vault.
	v.SSL.Enabled = Bool(true)

	return v
}

// Copy returns a deep copy of this configuration.
func (c *VaultConfig) Copy() *VaultConfig {
	if c == nil {
		return nil
	}

	var o VaultConfig
	o.Address = c.Address

	o.Enabled = c.Enabled

	o.Grace = c.Grace

	o.Namespace = c.Namespace

	o.RenewToken = c.RenewToken

	if c.Retry != nil {
		o.Retry = c.Retry.Copy()
	}

	if c.SSL != nil {
		o.SSL = c.SSL.Copy()
	}

	o.Token = c.Token

	o.VaultAgentTokenFile = c.VaultAgentTokenFile

	if c.Transport != nil {
		o.Transport = c.Transport.Copy()
	}

	o.UnwrapToken = c.UnwrapToken

	return &o
}

// Merge combines all values in this configuration with the values in the other
// configuration, with values in the other configuration taking precedence.
// Maps and slices are merged, most other values are overwritten. Complex
// structs define their own merge functionality.
func (c *VaultConfig) Merge(o *VaultConfig) *VaultConfig {
	if c == nil {
		if o == nil {
			return nil
		}
		return o.Copy()
	}

	if o == nil {
		return c.Copy()
	}

	r := c.Copy()

	if o.Address != nil {
		r.Address = o.Address
	}

	if o.Enabled != nil {
		r.Enabled = o.Enabled
	}

	if o.Grace != nil {
		r.Grace = o.Grace
	}

	if o.Namespace != nil {
		r.Namespace = o.Namespace
	}

	if o.RenewToken != nil {
		r.RenewToken = o.RenewToken
	}

	if o.Retry != nil {
		r.Retry = r.Retry.Merge(o.Retry)
	}

	if o.SSL != nil {
		r.SSL = r.SSL.Merge(o.SSL)
	}

	if o.Token != nil {
		r.Token = o.Token
	}

	if o.VaultAgentTokenFile != nil {
		r.VaultAgentTokenFile = o.VaultAgentTokenFile
	}

	if o.Transport != nil {
		r.Transport = r.Transport.Merge(o.Transport)
	}

	if o.UnwrapToken != nil {
		r.UnwrapToken = o.UnwrapToken
	}

	return r
}

// Finalize ensures there no nil pointers.
func (c *VaultConfig) Finalize() {
	if c.Address == nil {
		c.Address = stringFromEnv([]string{
			api.EnvVaultAddress,
		}, "")
	}

	if c.Grace == nil {
		c.Grace = TimeDuration(DefaultVaultGrace)
	}

	if c.Namespace == nil {
		c.Namespace = stringFromEnv([]string{"VAULT_NAMESPACE"}, "")
	}

	if c.RenewToken == nil {
		default_renew := DefaultVaultRenewToken
		if c.VaultAgentTokenFile != nil {
			default_renew = false
		}
		c.RenewToken = boolFromEnv([]string{
			"VAULT_RENEW_TOKEN",
		}, default_renew)
	}

	if c.Retry == nil {
		c.Retry = DefaultRetryConfig()
	}
	c.Retry.Finalize()

	// Vault has custom SSL settings
	if c.SSL == nil {
		c.SSL = DefaultSSLConfig()
	}
	if c.SSL.Enabled == nil {
		c.SSL.Enabled = Bool(true)
	}
	if c.SSL.CaCert == nil {
		c.SSL.CaCert = stringFromEnv([]string{api.EnvVaultCACert}, "")
	}
	if c.SSL.CaPath == nil {
		c.SSL.CaPath = stringFromEnv([]string{api.EnvVaultCAPath}, "")
	}
	if c.SSL.Cert == nil {
		c.SSL.Cert = stringFromEnv([]string{api.EnvVaultClientCert}, "")
	}
	if c.SSL.Key == nil {
		c.SSL.Key = stringFromEnv([]string{api.EnvVaultClientKey}, "")
	}
	if c.SSL.ServerName == nil {
		c.SSL.ServerName = stringFromEnv([]string{api.EnvVaultTLSServerName}, "")
	}
	if c.SSL.Verify == nil {
		c.SSL.Verify = antiboolFromEnv([]string{api.EnvVaultInsecure}, true)
	}
	c.SSL.Finalize()

	// Order of precedence
	// 1. `vault_agent_token_file` configuration value
	// 2. `token` configuration value`
	// 3. `VAULT_TOKEN` environment variable
	if c.Token == nil {
		c.Token = stringFromEnv([]string{
			"VAULT_TOKEN",
		}, "")
	}

	if c.VaultAgentTokenFile == nil {
		if StringVal(c.Token) == "" {
			if homePath != "" {
				c.Token = stringFromFile([]string{
					homePath + "/.vault-token",
				}, "")
			}
		}
	} else {
		c.Token = stringFromFile([]string{*c.VaultAgentTokenFile}, "")
	}

	if c.Transport == nil {
		c.Transport = DefaultTransportConfig()
	}
	c.Transport.Finalize()

	if c.UnwrapToken == nil {
		c.UnwrapToken = boolFromEnv([]string{
			"VAULT_UNWRAP_TOKEN",
		}, DefaultVaultUnwrapToken)
	}

	if c.Enabled == nil {
		c.Enabled = Bool(StringPresent(c.Address))
	}
}

// GoString defines the printable version of this struct.
func (c *VaultConfig) GoString() string {
	if c == nil {
		return "(*VaultConfig)(nil)"
	}

	return fmt.Sprintf("&VaultConfig{"+
		"Address:%s, "+
		"Enabled:%s, "+
		"Grace:%s, "+
		"Namespace:%s,"+
		"RenewToken:%s, "+
		"Retry:%#v, "+
		"SSL:%#v, "+
		"Token:%t, "+
		"VaultAgentTokenFile:%t, "+
		"Transport:%#v, "+
		"UnwrapToken:%s"+
		"}",
		StringGoString(c.Address),
		BoolGoString(c.Enabled),
		TimeDurationGoString(c.Grace),
		StringGoString(c.Namespace),
		BoolGoString(c.RenewToken),
		c.Retry,
		c.SSL,
		StringPresent(c.Token),
		StringPresent(c.VaultAgentTokenFile),
		c.Transport,
		BoolGoString(c.UnwrapToken),
	)
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 Romain LÉTENDART
//
// See LICENSE file.
package main

import (
	"flag"
	"fmt"
	"github.com/thoj/go-ircevent"
	"golang.org/x/net/html"
	"net/http"
	"net/url"
	"os"
	"regexp"
)

func getOptions() (nick, server, channel, channelKey string, success bool) {
	flag.StringVar(&channel, "channel", "", "IRC channel name")
	flag.StringVar(&channelKey, "key", "", "IRC channel key (optional)")
	flag.StringVar(&nick, "nick", "goxxx", "the bot's nickname (optional)")
	flag.StringVar(&server, "server", "chat.freenode.net:6697", "IRC_SERVER[:PORT] (optional)")
	flag.Usage = func() {
		fmt.Println("Usage:", os.Args[0], "-channel CHANNEL [ARGUMENTS]")
		fmt.Println()
		fmt.Println("Arguments description:")
		flag.PrintDefaults()
	}
	flag.Parse()

	if channel == "" {
		flag.Usage()
		success = false
	} else {
		success = true
	}

	return
}

func getTitleFromHTML(document *html.Node) (title string, found bool) {
	if document.Type != html.DocumentNode {
		// Didn't find a document node as first node, exit
		return
	}

	// Try to find the <html> inside the document
	child := document.FirstChild
	for child != nil && !(child.Type == html.ElementNode && child.Data == "html") {
		child = child.NextSibling
	}
	if child == nil {
		// Didn't find <html>, exit
		return
	}

	// Try to find the <head> inside the document
	currentNode := child
	for child = currentNode.FirstChild;
	    child != nil && !(child.Type == html.ElementNode && child.Data == "head");
	    child = child.NextSibling {
	}
	if child == nil {
		// Didn't find <head>, exit
		return
	}

	// Try to find the <title> inside the <head>
	currentNode = child
	for child = currentNode.FirstChild;
	    child != nil && !(child.Type == html.ElementNode && child.Data == "title");
	    child = child.NextSibling {
	}
	if child == nil || child.FirstChild == nil {
		// Didn't find <title> or it is empty, exit
		return
	}

	// Retrieve the content inside the <title>
	title = child.FirstChild.Data
	found = true

	return
}

func findUrls(message string) (urls []*url.URL) {
	const maxUrlsCount int = 10

	// Source of the regular expression:
	// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
	re := regexp.MustCompile("(?:https?://|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’])")
	urlCandidates := re.FindAllString(message, maxUrlsCount)

	for _, candidate := range urlCandidates {
		url, err := url.Parse(candidate)
		if err != nil {
			break
		}
		if url.Scheme == "" {
			url.Scheme = "https"
		}
		urls = append(urls, url)
	}

	return
}

func main() {
	nick, server, channel, channelKey, success := getOptions()

	if !success {
		return
	}

	ircConn := irc.IRC(nick, nick)
	ircConn.UseTLS = true
	ircConn.Connect(server)
	ircConn.Join(channel + " " + channelKey)
	ircConn.AddCallback("PRIVMSG", func(event *irc.Event) {
		allUrls := findUrls(event.Message())
		for _, url := range allUrls {
			fmt.Println("Detected URL:", url.String())
			response, err := http.Get(url.String())
			if err != nil {
				// TODO Do proper logging
				fmt.Println(err)
				return
			}
			doc, err := html.Parse(response.Body)
			response.Body.Close()
			if err != nil {
				// TODO Do proper logging
				fmt.Println(err)
				return
			}
			title, found := getTitleFromHTML(doc)
			if found {
				ircConn.Privmsg(channel, title)
			}
		}
	})

	ircConn.Loop()
}
<commit_msg>Applied "go fmt" to current code<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Romain LÉTENDART
//
// See LICENSE file.
package main

import (
	"flag"
	"fmt"
	"github.com/thoj/go-ircevent"
	"golang.org/x/net/html"
	"net/http"
	"net/url"
	"os"
	"regexp"
)

func getOptions() (nick, server, channel, channelKey string, success bool) {
	flag.StringVar(&channel, "channel", "", "IRC channel name")
	flag.StringVar(&channelKey, "key", "", "IRC channel key (optional)")
	flag.StringVar(&nick, "nick", "goxxx", "the bot's nickname (optional)")
	flag.StringVar(&server, "server", "chat.freenode.net:6697", "IRC_SERVER[:PORT] (optional)")
	flag.Usage = func() {
		fmt.Println("Usage:", os.Args[0], "-channel CHANNEL [ARGUMENTS]")
		fmt.Println()
		fmt.Println("Arguments description:")
		flag.PrintDefaults()
	}
	flag.Parse()

	if channel == "" {
		flag.Usage()
		success = false
	} else {
		success = true
	}

	return
}

func getTitleFromHTML(document *html.Node) (title string, found bool) {
	if document.Type != html.DocumentNode {
		// Didn't find a document node as first node, exit
		return
	}

	// Try to find the <html> inside the document
	child := document.FirstChild
	for child != nil && !(child.Type == html.ElementNode && child.Data == "html") {
		child = child.NextSibling
	}
	if child == nil {
		// Didn't find <html>, exit
		return
	}

	// Try to find the <head> inside the document
	currentNode := child
	for child = currentNode.FirstChild; child != nil && !(child.Type == html.ElementNode && child.Data == "head"); child = child.NextSibling {
	}
	if child == nil {
		// Didn't find <head>, exit
		return
	}

	// Try to find the <title> inside the <head>
	currentNode = child
	for child = currentNode.FirstChild; child != nil && !(child.Type == html.ElementNode && child.Data == "title"); child = child.NextSibling {
	}
	if child == nil || child.FirstChild == nil {
		// Didn't find <title> or it is empty, exit
		return
	}

	// Retrieve the content inside the <title>
	title = child.FirstChild.Data
	found = true

	return
}

func findUrls(message string) (urls []*url.URL) {
	const maxUrlsCount int = 10

	// Source of the regular expression:
	// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
	re := regexp.MustCompile("(?:https?://|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’])")
	urlCandidates := re.FindAllString(message, maxUrlsCount)

	for _, candidate := range urlCandidates {
		url, err := url.Parse(candidate)
		if err != nil {
			break
		}
		if url.Scheme == "" {
			url.Scheme = "https"
		}
		urls = append(urls, url)
	}

	return
}

func main() {
	nick, server, channel, channelKey, success := getOptions()

	if !success {
		return
	}

	ircConn := irc.IRC(nick, nick)
	ircConn.UseTLS = true
	ircConn.Connect(server)
	ircConn.Join(channel + " " + channelKey)
	ircConn.AddCallback("PRIVMSG", func(event *irc.Event) {
		allUrls := findUrls(event.Message())
		for _, url := range allUrls {
			fmt.Println("Detected URL:", url.String())
			response, err := http.Get(url.String())
			if err != nil {
				// TODO Do proper logging
				fmt.Println(err)
				return
			}
			doc, err := html.Parse(response.Body)
			response.Body.Close()
			if err != nil {
				// TODO Do proper logging
				fmt.Println(err)
				return
			}
			title, found := getTitleFromHTML(doc)
			if found {
				ircConn.Privmsg(channel, title)
			}
		}
	})

	ircConn.Loop()
}
<|endoftext|>


<commit_before>package index

import (
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/algolia/algoliasearch-client-go/algolia/opt"
	"github.com/algolia/algoliasearch-client-go/algolia/search"
	"github.com/algolia/algoliasearch-client-go/algolia/wait"
	"github.com/algolia/algoliasearch-client-go/cts"
)

func TestSettings(t *testing.T) {
	t.Parallel()
	_, index, indexName := cts.InitSearchClient1AndIndex(t)

	g := wait.NewGroup()

	{
		res, err := index.SaveObject(map[string]string{"objectID": "one", "attribute": "value"})
		require.NoError(t, err)
		g.Collect(res)
	}

	expected := search.Settings{
		SearchableAttributes:              opt.SearchableAttributes("attribute1", "attribute2", "attribute3", "ordered(attribute4)", "unordered(attribute5)"),
		AttributesForFaceting:             opt.AttributesForFaceting("attribute1", "filterOnly(attribute2)", "searchable(attribute3)"),
		UnretrievableAttributes:           opt.UnretrievableAttributes("attribute1", "attribute2"),
		AttributesToRetrieve:              opt.AttributesToRetrieve("attribute3", "attribute4"),
		Ranking:                           opt.Ranking("asc(attribute1)", "desc(attribute2)", "attribute", "custom", "exact", "filters", "geo", "proximity", "typo", "words"),
		CustomRanking:                     opt.CustomRanking("asc(attribute1)", "desc(attribute2)"),
		Replicas:                          opt.Replicas(indexName+"_replica1", indexName+"_replica2"),
		MaxValuesPerFacet:                 opt.MaxValuesPerFacet(100),
		SortFacetValuesBy:                 opt.SortFacetValuesBy("count"),
		AttributesToHighlight:             opt.AttributesToHighlight("attribute1", "attribute2"),
		AttributesToSnippet:               opt.AttributesToSnippet("attribute1:10", "attribute2:8"),
		HighlightPreTag:                   opt.HighlightPreTag(""),
		HighlightPostTag:                  opt.HighlightPostTag(""),
		SnippetEllipsisText:               opt.SnippetEllipsisText(" and so on."),
		RestrictHighlightAndSnippetArrays: opt.RestrictHighlightAndSnippetArrays(true),
		HitsPerPage:                       opt.HitsPerPage(42),
		PaginationLimitedTo:               opt.PaginationLimitedTo(43),
		MinWordSizefor1Typo:               opt.MinWordSizefor1Typo(2),
		MinWordSizefor2Typos:              opt.MinWordSizefor2Typos(6),
		TypoTolerance:                     opt.TypoTolerance(false),
		AllowTyposOnNumericTokens:         opt.AllowTyposOnNumericTokens(false),
		DisableTypoToleranceOnAttributes:  opt.DisableTypoToleranceOnAttributes("attribute1", "attribute2"),
		DisableTypoToleranceOnWords:       opt.DisableTypoToleranceOnWords("word1", "word2"),
		SeparatorsToIndex:                 opt.SeparatorsToIndex("()[]"),
		IgnorePlurals:                     opt.IgnorePlurals(true),
		RemoveStopWords:                   opt.RemoveStopWords(true),
		CamelCaseAttributes:               opt.CamelCaseAttributes("attribute1", "attribute2"),
		DecompoundedAttributes:            opt.DecompoundedAttributes(map[string][]string{"de": {"attribute1", "attribute2"}, "fi": {"attribute3"}}),
		KeepDiacriticsOnCharacters:        opt.KeepDiacriticsOnCharacters("øé"),
		QueryLanguages:                    opt.QueryLanguages("fr", "en"),
		QueryType:                         opt.QueryType("prefixNone"),
		RemoveWordsIfNoResults:            opt.RemoveWordsIfNoResults("allOptional"),
		AdvancedSyntax:                    opt.AdvancedSyntax(true),
		OptionalWords:                     opt.OptionalWords("word1", "word2"),
		DisablePrefixOnAttributes:         opt.DisablePrefixOnAttributes("attribute1", "attribute2"),
		DisableExactOnAttributes:          opt.DisableExactOnAttributes("attribute1", "attribute2"),
		ExactOnSingleWordQuery:            opt.ExactOnSingleWordQuery("word"),
		AlternativesAsExact:               opt.AlternativesAsExact("ignorePlurals"),
		AdvancedSyntaxFeatures:            opt.AdvancedSyntaxFeatures("exactPhrase"),
		EnableRules:                       opt.EnableRules(false),
		NumericAttributesForFiltering:     opt.NumericAttributesForFiltering("attribute1", "attribute2"),
		AllowCompressionOfIntegerArray:    opt.AllowCompressionOfIntegerArray(true),
		AttributeForDistinct:              opt.AttributeForDistinct("attribute1"),
		Distinct:                          opt.DistinctOf(2),
		ReplaceSynonymsInHighlight:        opt.ReplaceSynonymsInHighlight(false),
		MinProximity:                      opt.MinProximity(7),
		ResponseFields:                    opt.ResponseFields("hits", "hitsPerPage"),
		MaxFacetHits:                      opt.MaxFacetHits(100),
		IndexLanguages:                    opt.IndexLanguages("ja"),
	}

	{
		res, err := index.SetSettings(expected)
		require.NoError(t, err)
		g.Collect(res)
	}

	require.NoError(t, g.Wait())

	{
		settings, err := index.GetSettings()
		require.NoError(t, err)
		require.True(t, settings.Equal(expected))

		res, err := index.SetSettings(settings)
		require.NoError(t, err)
		require.NoError(t, res.Wait())

		settings, err = index.GetSettings()
		require.NoError(t, err)
		require.True(t, settings.Equal(expected))
	}

	expected.TypoTolerance = opt.TypoToleranceMin()
	expected.IgnorePlurals = opt.IgnorePluralsFor("en", "fr")
	expected.RemoveStopWords = opt.RemoveStopWordsFor("en", "fr")
	expected.Distinct = opt.Distinct(true)

	{
		res, err := index.SetSettings(expected)
		require.NoError(t, err)
		require.NoError(t, res.Wait())

		settings, err := index.GetSettings()
		require.NoError(t, err)
		require.True(t, settings.Equal(expected))

		res, err = index.SetSettings(settings)
		require.NoError(t, err)
		require.NoError(t, res.Wait())

		settings, err = index.GetSettings()
		require.NoError(t, err)
		require.True(t, settings.Equal(expected))
	}
}
<commit_msg>test: set Settings.UserData field in Settings integration test<commit_after>package index

import (
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/algolia/algoliasearch-client-go/algolia/opt"
	"github.com/algolia/algoliasearch-client-go/algolia/search"
	"github.com/algolia/algoliasearch-client-go/algolia/wait"
	"github.com/algolia/algoliasearch-client-go/cts"
)

func TestSettings(t *testing.T) {
	t.Parallel()
	_, index, indexName := cts.InitSearchClient1AndIndex(t)

	g := wait.NewGroup()

	{
		res, err := index.SaveObject(map[string]string{"objectID": "one", "attribute": "value"})
		require.NoError(t, err)
		g.Collect(res)
	}

	expected := search.Settings{
		SearchableAttributes:              opt.SearchableAttributes("attribute1", "attribute2", "attribute3", "ordered(attribute4)", "unordered(attribute5)"),
		AttributesForFaceting:             opt.AttributesForFaceting("attribute1", "filterOnly(attribute2)", "searchable(attribute3)"),
		UnretrievableAttributes:           opt.UnretrievableAttributes("attribute1", "attribute2"),
		AttributesToRetrieve:              opt.AttributesToRetrieve("attribute3", "attribute4"),
		Ranking:                           opt.Ranking("asc(attribute1)", "desc(attribute2)", "attribute", "custom", "exact", "filters", "geo", "proximity", "typo", "words"),
		CustomRanking:                     opt.CustomRanking("asc(attribute1)", "desc(attribute2)"),
		Replicas:                          opt.Replicas(indexName+"_replica1", indexName+"_replica2"),
		MaxValuesPerFacet:                 opt.MaxValuesPerFacet(100),
		SortFacetValuesBy:                 opt.SortFacetValuesBy("count"),
		AttributesToHighlight:             opt.AttributesToHighlight("attribute1", "attribute2"),
		AttributesToSnippet:               opt.AttributesToSnippet("attribute1:10", "attribute2:8"),
		HighlightPreTag:                   opt.HighlightPreTag(""),
		HighlightPostTag:                  opt.HighlightPostTag(""),
		SnippetEllipsisText:               opt.SnippetEllipsisText(" and so on."),
		RestrictHighlightAndSnippetArrays: opt.RestrictHighlightAndSnippetArrays(true),
		HitsPerPage:                       opt.HitsPerPage(42),
		PaginationLimitedTo:               opt.PaginationLimitedTo(43),
		MinWordSizefor1Typo:               opt.MinWordSizefor1Typo(2),
		MinWordSizefor2Typos:              opt.MinWordSizefor2Typos(6),
		TypoTolerance:                     opt.TypoTolerance(false),
		AllowTyposOnNumericTokens:         opt.AllowTyposOnNumericTokens(false),
		DisableTypoToleranceOnAttributes:  opt.DisableTypoToleranceOnAttributes("attribute1", "attribute2"),
		DisableTypoToleranceOnWords:       opt.DisableTypoToleranceOnWords("word1", "word2"),
		SeparatorsToIndex:                 opt.SeparatorsToIndex("()[]"),
		IgnorePlurals:                     opt.IgnorePlurals(true),
		RemoveStopWords:                   opt.RemoveStopWords(true),
		CamelCaseAttributes:               opt.CamelCaseAttributes("attribute1", "attribute2"),
		DecompoundedAttributes:            opt.DecompoundedAttributes(map[string][]string{"de": {"attribute1", "attribute2"}, "fi": {"attribute3"}}),
		KeepDiacriticsOnCharacters:        opt.KeepDiacriticsOnCharacters("øé"),
		QueryLanguages:                    opt.QueryLanguages("fr", "en"),
		QueryType:                         opt.QueryType("prefixNone"),
		RemoveWordsIfNoResults:            opt.RemoveWordsIfNoResults("allOptional"),
		AdvancedSyntax:                    opt.AdvancedSyntax(true),
		OptionalWords:                     opt.OptionalWords("word1", "word2"),
		DisablePrefixOnAttributes:         opt.DisablePrefixOnAttributes("attribute1", "attribute2"),
		DisableExactOnAttributes:          opt.DisableExactOnAttributes("attribute1", "attribute2"),
		ExactOnSingleWordQuery:            opt.ExactOnSingleWordQuery("word"),
		AlternativesAsExact:               opt.AlternativesAsExact("ignorePlurals"),
		AdvancedSyntaxFeatures:            opt.AdvancedSyntaxFeatures("exactPhrase"),
		EnableRules:                       opt.EnableRules(false),
		NumericAttributesForFiltering:     opt.NumericAttributesForFiltering("attribute1", "attribute2"),
		AllowCompressionOfIntegerArray:    opt.AllowCompressionOfIntegerArray(true),
		AttributeForDistinct:              opt.AttributeForDistinct("attribute1"),
		Distinct:                          opt.DistinctOf(2),
		ReplaceSynonymsInHighlight:        opt.ReplaceSynonymsInHighlight(false),
		MinProximity:                      opt.MinProximity(7),
		ResponseFields:                    opt.ResponseFields("hits", "hitsPerPage"),
		MaxFacetHits:                      opt.MaxFacetHits(100),
		IndexLanguages:                    opt.IndexLanguages("ja"),
		UserData:                          opt.UserData(map[string]interface{}{"customUserData": 42.0}),
	}

	{
		res, err := index.SetSettings(expected)
		require.NoError(t, err)
		g.Collect(res)
	}

	require.NoError(t, g.Wait())

	{
		settings, err := index.GetSettings()
		require.NoError(t, err)
		require.True(t, settings.Equal(expected))

		res, err := index.SetSettings(settings)
		require.NoError(t, err)
		require.NoError(t, res.Wait())

		settings, err = index.GetSettings()
		require.NoError(t, err)
		require.True(t, settings.Equal(expected))
	}

	expected.TypoTolerance = opt.TypoToleranceMin()
	expected.IgnorePlurals = opt.IgnorePluralsFor("en", "fr")
	expected.RemoveStopWords = opt.RemoveStopWordsFor("en", "fr")
	expected.Distinct = opt.Distinct(true)

	{
		res, err := index.SetSettings(expected)
		require.NoError(t, err)
		require.NoError(t, res.Wait())

		settings, err := index.GetSettings()
		require.NoError(t, err)
		require.True(t, settings.Equal(expected))

		res, err = index.SetSettings(settings)
		require.NoError(t, err)
		require.NoError(t, res.Wait())

		settings, err = index.GetSettings()
		require.NoError(t, err)
		require.True(t, settings.Equal(expected))
	}
}
<|endoftext|>