<commit_before>package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"

	"github.com/mattn/go-colorable"
	"github.com/tonnerre/golang-pretty"

	"github.com/alecthomas/kingpin"
	"gopkg.in/inconshreveable/log15.v2"

	"github.com/rightscale/rsc/cm15"
	"github.com/rightscale/rsc/log"
	"github.com/rightscale/rsc/rsapi"
)

var (
	app         = kingpin.New("right_st", "A command-line application for managing RightScripts")
	version     = app.Flag("version", "Print version").Short('v').Bool()
	debug       = app.Flag("debug", "Debug mode").Short('d').Bool()
	configFile  = app.Flag("config", "Set the config file path.").Short('c').Default(defaultConfigFile()).String()
	environment = app.Flag("environment", "Set the RightScale login environment.").Short('e').String()

	rightScript = app.Command("rightscript", "RightScript stuff")

	rightScriptList       = rightScript.Command("list", "List RightScripts")
	rightScriptListFilter = rightScriptList.Arg("filter", "Filter by name").Required().String()

	rightScriptUpload      = rightScript.Command("upload", "Upload a RightScript")
	rightScriptUploadPaths = rightScriptUpload.Arg("path", "File or directory containing script files to upload").Required().ExistingFilesOrDirs()
	rightScriptUploadForce = rightScriptUpload.Flag("force", "Force upload of file if metadata is not present").Bool()

	rightScriptDownload           = rightScript.Command("download", "Download a RightScript to a file or files")
	rightScriptDownloadNameOrHref = rightScriptDownload.Arg("name_or_href", "Script Name or Href").Required().String()
	rightScriptDownloadTo         = rightScriptDownload.Arg("path", "Download location").String()

	rightScriptScaffold      = rightScript.Command("scaffold", "Add RightScript YAML metadata comments to a file or files")
	rightScriptScaffoldPaths = rightScriptScaffold.Arg("path", "File or directory to set metadata for").Required().ExistingFilesOrDirs()

	rightScriptValidate      = rightScript.Command("validate", "Validate RightScript YAML metadata comments in a file or files")
	rightScriptValidatePaths = rightScriptValidate.Arg("path", "Path to script file or directory containing script files").Required().ExistingFilesOrDirs()
)

func main() {
	command := kingpin.MustParse(app.Parse(os.Args[1:]))

	err := readConfig(*configFile, *environment)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s: Error reading config file: %s\n", filepath.Base(os.Args[0]), err)
		os.Exit(1)
	}
	client := config.environment.Client15()

	// Handle logging
	handler := log15.StreamHandler(colorable.NewColorableStdout(), log15.TerminalFormat())
	log15.Root().SetHandler(handler)
	if *debug {
		log.Logger.SetHandler(handler)
	}
	app.Writer(os.Stdout)

	switch command {
	case rightScriptList.FullCommand():
		rightscriptLocator := client.RightScriptLocator("/api/right_scripts")
		var apiParams = rsapi.APIParams{"filter": []string{"name==" + *rightScriptListFilter}}
		fmt.Printf("LIST %s:\n", *rightScriptListFilter)
		rightscripts, err := rightscriptLocator.Index(
			apiParams,
		)
		if err != nil {
			fatalError("%#v", err)
		}
		for _, rs := range rightscripts {
			fmt.Printf("/api/right_scripts/%s %s\n", rs.Id, rs.Name)
		}
	case rightScriptUpload.FullCommand():
		// Pass 1, perform validations, gather up results
		scripts := []RightScript{}
		for _, path := range *rightScriptUploadPaths {
			info, err := os.Stat(path)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
				os.Exit(1)
			}
			if info.IsDir() {
				// TODO: recurse?
			} else {
				fmt.Printf("Uploading %s:", path)
				f, err := os.Open(path)
				if err != nil {
					fatalError("Cannot open %s", path)
				}
				defer f.Close()
				metadata, err := ParseRightScriptMetadata(f)

				if err != nil {
					if !*rightScriptUploadForce {
						fatalError("No embedded metadata for %s. Use --force to upload anyways.", path)
					}
				}
				script := RightScript{"", path, metadata}
				scripts = append(scripts, script)
			}
		}

		// Pass 2, upload
		for _, script := range scripts {
			err = script.Push()
			fmt.Println(err)
		}
	case rightScriptDownload.FullCommand():
		rsIdMatch := regexp.MustCompile(`^\d+$`)
		rsHrefMatch := regexp.MustCompile(`^/api/right_scripts/\d+$`)

		var href string

		if rsIdMatch.Match([]byte(*rightScriptDownloadNameOrHref)) {
			href = fmt.Sprintf("/api/right_scripts/%s", *rightScriptDownloadNameOrHref)
		} else if rsHrefMatch.Match([]byte(*rightScriptDownloadNameOrHref)) {
			href = *rightScriptDownloadNameOrHref
		} else {
			rightscriptLocator := client.RightScriptLocator("/api/right_scripts")
			apiParams := rsapi.APIParams{"filter": []string{"name==" + *rightScriptDownloadNameOrHref}}
			rightscripts, err := rightscriptLocator.Index(apiParams)
			if err != nil {
				fatalError("%s", err.Error())
			}
			foundId := ""
			for _, rs := range rightscripts {
				//fmt.Printf("%#v\n", rs)
				// Recheck the name here, filter does a impartial match and we need an exact one
				// TODO, do first pass for head revisions only, second for non-heads?
				if rs.Name == *rightScriptDownloadNameOrHref && rs.Revision == 0 {
					if foundId != "" {
						fatalError("Error, matched multiple RightScripts with the same name. Don't know which one to download. Please delete one or specify an HREF to download such as /api/right_scripts/%d", rs.Id)
					} else {
						foundId = rs.Id
					}
				}
			}
			if foundId == "" {
				fatalError("Found no RightScripts matching %s", *rightScriptDownloadNameOrHref)
			}
			href = fmt.Sprintf("/api/right_scripts/%s", foundId)
		}

		rightscriptLocator := client.RightScriptLocator(href)
		// attachmentsLocator := client.RightScriptLocator(fmt.Sprintf("%s/attachments", href))
		// sourceLocator := client.RightScriptLocator(fmt.Sprintf("%s/source", href))

		rightscript, err1 := rightscriptLocator.Show()
		source, err2 := GetSource(rightscriptLocator)

		// attachments, err2 := attachmentsLocator.Index(rsapi.APIParams{})
		fmt.Printf("Found %#v  -- %v\n", rightscript, err1)
		fmt.Printf("Source %s -- %v\n", source, err2)

		if *rightScriptDownloadTo == "" {
			*rightScriptDownloadTo = rightscript.Name
		}
		fmt.Printf("Attemping to download '%s' to %s", rightscript.Name, *rightScriptDownloadTo)
		err = ioutil.WriteFile(*rightScriptDownloadTo, source, 0755)
		if err != nil {
			fatalError("Could not create file: %s", err.Error())
		}

	case rightScriptScaffold.FullCommand():
		for _, path := range *rightScriptScaffoldPaths {
			info, err := os.Stat(path)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
				os.Exit(1)
			}
			if info.IsDir() {
				// TODO: recurse - see what was done with rightScriptValidate
			} else {
				err = AddRightScriptMetadata(path)
				if err != nil {
					fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
					os.Exit(1)
				}
			}
		}
	case rightScriptValidate.FullCommand():
		for _, path := range *rightScriptValidatePaths {
			info, err := os.Stat(path)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
				os.Exit(1)
			}
			if info.IsDir() {
				files := []string{}
				err = filepath.Walk(path, func(p string, f os.FileInfo, err error) error {
					files = append(files, p)
					return nil
				})

				err_encountered := false
				for _, file := range files {
					err = validateRightScript(file)
					if err != nil {
						err_encountered = true
						fmt.Fprintf(os.Stderr, "%s - %s: %s\n", file, filepath.Base(os.Args[0]), err)
					}
				}
				if err_encountered != false {
					os.Exit(1)
				}

			} else {
				err = validateRightScript(path)
				if err != nil {
					fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
					os.Exit(1)
				}
			}
		}
	}
}

// Crappy workaround. RSC doesn't return the body of the http request which contains
// the script source, so do the same lower level calls it does to get it.
func GetSource(loc *cm15.RightScriptLocator) (respBody []byte, err error) {
	var params rsapi.APIParams
	var p rsapi.APIParams
	APIVersion := "1.5"
	client := config.environment.Client15()

	uri, err := loc.ActionPath("RightScript", "show_source")
	if err != nil {
		return respBody, err
	}
	req, err := client.BuildHTTPRequest(uri.HTTPMethod, uri.Path, APIVersion, params, p)
	if err != nil {
		return respBody, err
	}
	resp, err := client.PerformRequest(req)
	if err != nil {
		return respBody, err
	}
	defer resp.Body.Close()
	respBody, _ = ioutil.ReadAll(resp.Body)

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return respBody, fmt.Errorf("invalid response %s: %s", resp.Status, string(respBody))
	}
	return respBody, nil
}

type RightScript struct {
	Href     string
	Path     string
	Metadata *RightScriptMetadata
}

func (r *RightScript) Push() error {
	client := config.environment.Client15()
	rightscriptLocator := client.RightScriptLocator("/api/right_scripts")
	apiParams := rsapi.APIParams{"filter": []string{"name==" + r.Metadata.Name}}
	rightscripts, err := rightscriptLocator.Index(apiParams)
	if err != nil {
		return err
	}
	foundId := ""
	for _, rs := range rightscripts {
		//fmt.Printf("%#v\n", rs)
		// Recheck the name here, filter does a impartial match and we need an exact one
		if rs.Name == r.Metadata.Name && rs.Revision == 0 {
			if foundId != "" {
				fatalError("Error, matched multiple RightScripts with the same name, please delete one: %d %d", rs.Id, foundId)
			} else {
				foundId = rs.Id
			}
		}
	}

	pathSrc, err := ioutil.ReadFile(r.Path)
	if err != nil {
		return err
	}

	if foundId == "" {
		fmt.Printf("Creating a new RightScript named '%s' from %s\n", r.Metadata.Name, r.Path)
		// New one, perform create call
		params := cm15.RightScriptParam2{
			Name:        r.Metadata.Name,
			Description: r.Metadata.Description,
			Source:      string(pathSrc),
		}
		//rightscriptLocator = client.RightScriptLocator(fmt.Sprintf("/api/right_scripts", foundId))
		locator, err := rightscriptLocator.Create(&params)
		fmt.Println(locator, err)
		return err
	} else {
		// apiParams = rsapi.APIParams{
		// 	"Name":        r.Metadata.Name,
		// 	"Description": r.Metadata.Description,
		// 	"Source":      string(pathSrc),
		// }
		params := cm15.RightScriptParam3{
			Name:        r.Metadata.Name,
			Description: r.Metadata.Description,
			Source:      string(pathSrc),
		}
		rightscriptLocator = client.RightScriptLocator(fmt.Sprintf("/api/right_scripts/%s", foundId))
		err = rightscriptLocator.Update(&params)
		fmt.Println(err)
		return err
		// Found existing, do an update
	}
}

func fatalError(format string, v ...interface{}) {
	msg := fmt.Sprintf(format, v)
	fmt.Println(msg)
	os.Exit(1)
}

func validateRightScript(path string) error {
	script, err := os.Open(path)
	if err != nil {
		return err
	}
	defer script.Close()

	metadata, err := ParseRightScriptMetadata(script)
	if err != nil {
		return err
	}
	if *debug {
		pretty.Println(metadata)
	}
	fmt.Printf("%s - valid metadata\n", path)

	for _, attachment := range metadata.Attachments {
		md5, err := md5Attachment(path, attachment)
		if err != nil {
			return err
		}
		fmt.Println(attachment, md5)
	}

	return nil
}

func md5Attachment(script, attachment string) (string, error) {
	path := filepath.Join(filepath.Dir(script), attachment)
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()
	hash := md5.New()

	_, err = io.Copy(hash, file)
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(hash.Sum(nil)), nil
}
<commit_msg>Clean up download output<commit_after>package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"

	"github.com/mattn/go-colorable"
	"github.com/tonnerre/golang-pretty"

	"github.com/alecthomas/kingpin"
	"gopkg.in/inconshreveable/log15.v2"

	"github.com/rightscale/rsc/cm15"
	"github.com/rightscale/rsc/log"
	"github.com/rightscale/rsc/rsapi"
)

var (
	app         = kingpin.New("right_st", "A command-line application for managing RightScripts")
	version     = app.Flag("version", "Print version").Short('v').Bool()
	debug       = app.Flag("debug", "Debug mode").Short('d').Bool()
	configFile  = app.Flag("config", "Set the config file path.").Short('c').Default(defaultConfigFile()).String()
	environment = app.Flag("environment", "Set the RightScale login environment.").Short('e').String()

	rightScript = app.Command("rightscript", "RightScript stuff")

	rightScriptList       = rightScript.Command("list", "List RightScripts")
	rightScriptListFilter = rightScriptList.Arg("filter", "Filter by name").Required().String()

	rightScriptUpload      = rightScript.Command("upload", "Upload a RightScript")
	rightScriptUploadPaths = rightScriptUpload.Arg("path", "File or directory containing script files to upload").Required().ExistingFilesOrDirs()
	rightScriptUploadForce = rightScriptUpload.Flag("force", "Force upload of file if metadata is not present").Bool()

	rightScriptDownload           = rightScript.Command("download", "Download a RightScript to a file or files")
	rightScriptDownloadNameOrHref = rightScriptDownload.Arg("name_or_href", "Script Name or Href").Required().String()
	rightScriptDownloadTo         = rightScriptDownload.Arg("path", "Download location").String()

	rightScriptScaffold      = rightScript.Command("scaffold", "Add RightScript YAML metadata comments to a file or files")
	rightScriptScaffoldPaths = rightScriptScaffold.Arg("path", "File or directory to set metadata for").Required().ExistingFilesOrDirs()

	rightScriptValidate      = rightScript.Command("validate", "Validate RightScript YAML metadata comments in a file or files")
	rightScriptValidatePaths = rightScriptValidate.Arg("path", "Path to script file or directory containing script files").Required().ExistingFilesOrDirs()
)

func main() {
	command := kingpin.MustParse(app.Parse(os.Args[1:]))

	err := readConfig(*configFile, *environment)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s: Error reading config file: %s\n", filepath.Base(os.Args[0]), err)
		os.Exit(1)
	}
	client := config.environment.Client15()

	// Handle logging
	handler := log15.StreamHandler(colorable.NewColorableStdout(), log15.TerminalFormat())
	log15.Root().SetHandler(handler)
	if *debug {
		log.Logger.SetHandler(handler)
	}
	app.Writer(os.Stdout)

	switch command {
	case rightScriptList.FullCommand():
		rightscriptLocator := client.RightScriptLocator("/api/right_scripts")
		var apiParams = rsapi.APIParams{"filter": []string{"name==" + *rightScriptListFilter}}
		fmt.Printf("LIST %s:\n", *rightScriptListFilter)
		rightscripts, err := rightscriptLocator.Index(
			apiParams,
		)
		if err != nil {
			fatalError("%#v", err)
		}
		for _, rs := range rightscripts {
			fmt.Printf("/api/right_scripts/%s %s\n", rs.Id, rs.Name)
		}
	case rightScriptUpload.FullCommand():
		// Pass 1, perform validations, gather up results
		scripts := []RightScript{}
		for _, path := range *rightScriptUploadPaths {
			info, err := os.Stat(path)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
				os.Exit(1)
			}
			if info.IsDir() {
				// TODO: recurse?
			} else {
				fmt.Printf("Uploading %s:", path)
				f, err := os.Open(path)
				if err != nil {
					fatalError("Cannot open %s", path)
				}
				defer f.Close()
				metadata, err := ParseRightScriptMetadata(f)

				if err != nil {
					if !*rightScriptUploadForce {
						fatalError("No embedded metadata for %s. Use --force to upload anyways.", path)
					}
				}
				script := RightScript{"", path, metadata}
				scripts = append(scripts, script)
			}
		}

		// Pass 2, upload
		for _, script := range scripts {
			err = script.Push()
			fmt.Println(err)
		}
	case rightScriptDownload.FullCommand():
		rsIdMatch := regexp.MustCompile(`^\d+$`)
		rsHrefMatch := regexp.MustCompile(`^/api/right_scripts/\d+$`)

		var href string

		if rsIdMatch.Match([]byte(*rightScriptDownloadNameOrHref)) {
			href = fmt.Sprintf("/api/right_scripts/%s", *rightScriptDownloadNameOrHref)
		} else if rsHrefMatch.Match([]byte(*rightScriptDownloadNameOrHref)) {
			href = *rightScriptDownloadNameOrHref
		} else {
			rightscriptLocator := client.RightScriptLocator("/api/right_scripts")
			apiParams := rsapi.APIParams{"filter": []string{"name==" + *rightScriptDownloadNameOrHref}}
			rightscripts, err := rightscriptLocator.Index(apiParams)
			if err != nil {
				fatalError("%s", err.Error())
			}
			foundId := ""
			for _, rs := range rightscripts {
				//fmt.Printf("%#v\n", rs)
				// Recheck the name here, filter does a impartial match and we need an exact one
				// TODO, do first pass for head revisions only, second for non-heads?
				if rs.Name == *rightScriptDownloadNameOrHref && rs.Revision == 0 {
					if foundId != "" {
						fatalError("Error, matched multiple RightScripts with the same name. Don't know which one to download. Please delete one or specify an HREF to download such as /api/right_scripts/%d", rs.Id)
					} else {
						foundId = rs.Id
					}
				}
			}
			if foundId == "" {
				fatalError("Found no RightScripts matching %s", *rightScriptDownloadNameOrHref)
			}
			href = fmt.Sprintf("/api/right_scripts/%s", foundId)
		}

		rightscriptLocator := client.RightScriptLocator(href)
		// attachmentsLocator := client.RightScriptLocator(fmt.Sprintf("%s/attachments", href))
		// sourceLocator := client.RightScriptLocator(fmt.Sprintf("%s/source", href))

		rightscript, err1 := rightscriptLocator.Show()
		source, err2 := GetSource(rightscriptLocator)

		// attachments, err2 := attachmentsLocator.Index(rsapi.APIParams{})
		fmt.Printf("Found %#v  -- %v\n", rightscript, err1)
		fmt.Printf("Source %s -- %v\n", source, err2)

		if *rightScriptDownloadTo == "" {
			*rightScriptDownloadTo = rightscript.Name
		}
		fmt.Printf("Attempting to download '%s' to %s\n", rightscript.Name, *rightScriptDownloadTo)
		err = ioutil.WriteFile(*rightScriptDownloadTo, source, 0755)
		if err != nil {
			fatalError("Could not create file: %s", err.Error())
		}

	case rightScriptScaffold.FullCommand():
		for _, path := range *rightScriptScaffoldPaths {
			info, err := os.Stat(path)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
				os.Exit(1)
			}
			if info.IsDir() {
				// TODO: recurse - see what was done with rightScriptValidate
			} else {
				err = AddRightScriptMetadata(path)
				if err != nil {
					fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
					os.Exit(1)
				}
			}
		}
	case rightScriptValidate.FullCommand():
		for _, path := range *rightScriptValidatePaths {
			info, err := os.Stat(path)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
				os.Exit(1)
			}
			if info.IsDir() {
				files := []string{}
				err = filepath.Walk(path, func(p string, f os.FileInfo, err error) error {
					files = append(files, p)
					return nil
				})

				err_encountered := false
				for _, file := range files {
					err = validateRightScript(file)
					if err != nil {
						err_encountered = true
						fmt.Fprintf(os.Stderr, "%s - %s: %s\n", file, filepath.Base(os.Args[0]), err)
					}
				}
				if err_encountered != false {
					os.Exit(1)
				}

			} else {
				err = validateRightScript(path)
				if err != nil {
					fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
					os.Exit(1)
				}
			}
		}
	}
}

// Crappy workaround. RSC doesn't return the body of the http request which contains
// the script source, so do the same lower level calls it does to get it.
func GetSource(loc *cm15.RightScriptLocator) (respBody []byte, err error) {
	var params rsapi.APIParams
	var p rsapi.APIParams
	APIVersion := "1.5"
	client := config.environment.Client15()

	uri, err := loc.ActionPath("RightScript", "show_source")
	if err != nil {
		return respBody, err
	}
	req, err := client.BuildHTTPRequest(uri.HTTPMethod, uri.Path, APIVersion, params, p)
	if err != nil {
		return respBody, err
	}
	resp, err := client.PerformRequest(req)
	if err != nil {
		return respBody, err
	}
	defer resp.Body.Close()
	respBody, _ = ioutil.ReadAll(resp.Body)

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return respBody, fmt.Errorf("invalid response %s: %s", resp.Status, string(respBody))
	}
	return respBody, nil
}

type RightScript struct {
	Href     string
	Path     string
	Metadata *RightScriptMetadata
}

func (r *RightScript) Push() error {
	client := config.environment.Client15()
	rightscriptLocator := client.RightScriptLocator("/api/right_scripts")
	apiParams := rsapi.APIParams{"filter": []string{"name==" + r.Metadata.Name}}
	rightscripts, err := rightscriptLocator.Index(apiParams)
	if err != nil {
		return err
	}
	foundId := ""
	for _, rs := range rightscripts {
		//fmt.Printf("%#v\n", rs)
		// Recheck the name here, filter does a impartial match and we need an exact one
		if rs.Name == r.Metadata.Name && rs.Revision == 0 {
			if foundId != "" {
				fatalError("Error, matched multiple RightScripts with the same name, please delete one: %d %d", rs.Id, foundId)
			} else {
				foundId = rs.Id
			}
		}
	}

	pathSrc, err := ioutil.ReadFile(r.Path)
	if err != nil {
		return err
	}

	if foundId == "" {
		fmt.Printf("Creating a new RightScript named '%s' from %s\n", r.Metadata.Name, r.Path)
		// New one, perform create call
		params := cm15.RightScriptParam2{
			Name:        r.Metadata.Name,
			Description: r.Metadata.Description,
			Source:      string(pathSrc),
		}
		//rightscriptLocator = client.RightScriptLocator(fmt.Sprintf("/api/right_scripts", foundId))
		locator, err := rightscriptLocator.Create(&params)
		fmt.Println(locator, err)
		return err
	} else {
		// apiParams = rsapi.APIParams{
		// 	"Name":        r.Metadata.Name,
		// 	"Description": r.Metadata.Description,
		// 	"Source":      string(pathSrc),
		// }
		params := cm15.RightScriptParam3{
			Name:        r.Metadata.Name,
			Description: r.Metadata.Description,
			Source:      string(pathSrc),
		}
		rightscriptLocator = client.RightScriptLocator(fmt.Sprintf("/api/right_scripts/%s", foundId))
		err = rightscriptLocator.Update(&params)
		fmt.Println(err)
		return err
		// Found existing, do an update
	}
}

func fatalError(format string, v ...interface{}) {
	msg := fmt.Sprintf(format, v)
	fmt.Println(msg)
	os.Exit(1)
}

func validateRightScript(path string) error {
	script, err := os.Open(path)
	if err != nil {
		return err
	}
	defer script.Close()

	metadata, err := ParseRightScriptMetadata(script)
	if err != nil {
		return err
	}
	if *debug {
		pretty.Println(metadata)
	}
	fmt.Printf("%s - valid metadata\n", path)

	for _, attachment := range metadata.Attachments {
		md5, err := md5Attachment(path, attachment)
		if err != nil {
			return err
		}
		fmt.Println(attachment, md5)
	}

	return nil
}

func md5Attachment(script, attachment string) (string, error) {
	path := filepath.Join(filepath.Dir(script), attachment)
	file, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer file.Close()
	hash := md5.New()

	_, err = io.Copy(hash, file)
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(hash.Sum(nil)), nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"regexp"
	"strconv"
	"time"
)

var debug *bool

const pattConn string = `([a-z]{3,}):\/\/(.+)(:[0-9]+)?`

func logDebug(msg interface{}) {
	if *debug {
		log.Print(msg)
	}
}

// Connection data
type Connection struct {
	Type   string
	Scheme string
	Port   int
	Host   string
}

func buildConn(host string, port int, fullConn string) *Connection {
	if host != "" {
		return &Connection{Type: "tcp", Host: host, Port: port}
	}

	if fullConn == "" {
		return nil
	}

	res := regexp.MustCompile(pattConn).FindAllStringSubmatch(fullConn, -1)[0]
	if len(res) != 4 {
		return nil
	}

	port, err := strconv.Atoi(res[3])
	if err != nil {
		port = 80
	}

	conn := &Connection{Type: res[1], Host: res[2], Port: port}
	if conn.Type != "tcp" {
		conn.Scheme = conn.Type
		conn.Type = "tcp"
	}

	return conn
}

func pingTCP(conn *Connection, timeoutSeconds int) error {
	timeout := time.Duration(timeoutSeconds) * time.Second
	start := time.Now()
	address := fmt.Sprintf("%s:%d", conn.Host, conn.Port)
	logDebug("Dial adress: " + address)

	for {
		_, err := net.Dial(conn.Type, address)
		logDebug("ping TCP")

		if err == nil {
			logDebug("Up")
			return nil
		}

		logDebug("Down")
		logDebug(err)
		if time.Since(start) > timeout {
			return err
		}

		time.Sleep(500 * time.Millisecond)
	}
}

func pingHTTP(conn *Connection, timeoutSeconds int) error {
	timeout := time.Duration(timeoutSeconds) * time.Second
	start := time.Now()
	address := fmt.Sprintf("%s://%s:%d", conn.Scheme, conn.Host, conn.Port)
	logDebug("HTTP adress: " + address)

	for {
		resp, err := http.Get(address)
		logDebug("ping HTTP " + resp.Status)

		if err == nil && resp.StatusCode < http.StatusInternalServerError {
			return nil
		}

		if time.Since(start) > timeout {
			return errors.New(resp.Status)
		}

		time.Sleep(500 * time.Millisecond)
	}
}

func main() {
	fullConn := flag.String("full-connection", "", "full connection")
	host := flag.String("host", "", "host to connect")
	port := flag.Int("port", 80, "port to connect")
	timeout := flag.Int("timeout", 10, "time to wait until port become available")
	debug = flag.Bool("debug", false, "enable debug")

	flag.Parse()

	conn := buildConn(*host, *port, *fullConn)
	if conn == nil {
		log.Fatal("Invalid connection")
	}

	logDebug("Waiting")
	if err := pingTCP(conn, *timeout); err != nil {
		log.Fatal(err)
	}

	if conn.Scheme != "http" && conn.Scheme != "https" {
		return
	}

	if err := pingHTTP(conn, *timeout); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>shows the timeout in the debug log<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"regexp"
	"strconv"
	"time"
)

var debug *bool

const pattConn string = `([a-z]{3,}):\/\/(.+)(:[0-9]+)?`

func logDebug(msg interface{}) {
	if *debug {
		log.Print(msg)
	}
}

// Connection data
type Connection struct {
	Type   string
	Scheme string
	Port   int
	Host   string
}

func buildConn(host string, port int, fullConn string) *Connection {
	if host != "" {
		return &Connection{Type: "tcp", Host: host, Port: port}
	}

	if fullConn == "" {
		return nil
	}

	res := regexp.MustCompile(pattConn).FindAllStringSubmatch(fullConn, -1)[0]
	if len(res) != 4 {
		return nil
	}

	port, err := strconv.Atoi(res[3])
	if err != nil {
		port = 80
	}

	conn := &Connection{Type: res[1], Host: res[2], Port: port}
	if conn.Type != "tcp" {
		conn.Scheme = conn.Type
		conn.Type = "tcp"
	}

	return conn
}

func pingTCP(conn *Connection, timeoutSeconds int) error {
	timeout := time.Duration(timeoutSeconds) * time.Second
	start := time.Now()
	address := fmt.Sprintf("%s:%d", conn.Host, conn.Port)
	logDebug("Dial adress: " + address)

	for {
		_, err := net.Dial(conn.Type, address)
		logDebug("ping TCP")

		if err == nil {
			logDebug("Up")
			return nil
		}

		logDebug("Down")
		logDebug(err)
		if time.Since(start) > timeout {
			return err
		}

		time.Sleep(500 * time.Millisecond)
	}
}

func pingHTTP(conn *Connection, timeoutSeconds int) error {
	timeout := time.Duration(timeoutSeconds) * time.Second
	start := time.Now()
	address := fmt.Sprintf("%s://%s:%d", conn.Scheme, conn.Host, conn.Port)
	logDebug("HTTP adress: " + address)

	for {
		resp, err := http.Get(address)
		logDebug("ping HTTP " + resp.Status)

		if err == nil && resp.StatusCode < http.StatusInternalServerError {
			return nil
		}

		if time.Since(start) > timeout {
			return errors.New(resp.Status)
		}

		time.Sleep(500 * time.Millisecond)
	}
}

func main() {
	fullConn := flag.String("full-connection", "", "full connection")
	host := flag.String("host", "", "host to connect")
	port := flag.Int("port", 80, "port to connect")
	timeout := flag.Int("timeout", 10, "time to wait until port become available")
	debug = flag.Bool("debug", false, "enable debug")

	flag.Parse()

	conn := buildConn(*host, *port, *fullConn)
	if conn == nil {
		log.Fatal("Invalid connection")
	}

	logDebug("Waiting "+ strconv.Itoa(*timeout) + " seconds")
	if err := pingTCP(conn, *timeout); err != nil {
		log.Fatal(err)
	}

	if conn.Scheme != "http" && conn.Scheme != "https" {
		return
	}

	if err := pingHTTP(conn, *timeout); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"

	"github.com/PuerkitoBio/ghost/handlers"
	"github.com/PuerkitoBio/ghost/templates"
	_ "github.com/PuerkitoBio/ghost/templates/amber"
	_ "github.com/PuerkitoBio/ghost/templates/gotpl"
	"github.com/howeyc/fsnotify"
)

const (
	defPublicDir     = "./public/"
	defTemplateDir   = "./templates/"
	defPort          = 9000
	tmplRefreshDelay = 2 * time.Second
	faviconCache     = 1 * time.Minute
)

var (
	// Flags values
	publicDir   string
	templateDir string
	port        int

	faviconPath   = path.Join(publicDir, "favicon.ico")
	staticHandler = http.StripPrefix("/public/", http.FileServer(http.Dir(publicDir)))

	// Protect the templates
	tmplLock sync.RWMutex
)

func init() {
	flag.StringVar(&publicDir, "public-dir", defPublicDir, "set the public directory for static assets")
	flag.StringVar(&publicDir, "p", defPublicDir, "shorthand for -public-dir")
	flag.StringVar(&templateDir, "template-dir", defTemplateDir, "set the templates directory")
	flag.StringVar(&templateDir, "t", defTemplateDir, "shorthand for -template-dir")
	flag.IntVar(&port, "port", defPort, "set the port number")
	flag.IntVar(&port, "P", defPort, "shorthand for -port")
}

func serveTemplate(w http.ResponseWriter, r *http.Request) {
	// If a template matches the path, minus the starting /
	tmplLock.RLock()
	err := templates.Render(r.URL.Path[1:], w, nil)
	tmplLock.RUnlock()
	if err != nil {
		w.Header().Del("Content-Type")
		staticHandler.ServeHTTP(w, r)
	}
}

func refreshTemplates() {
	tmplLock.Lock()
	defer tmplLock.Unlock()
	if err := templates.CompileDir(templateDir); err != nil {
		// Do not stop if templates directory does not exist or has an invalid template
		log.Println(err)
	}
}

func watchDir(watcher *fsnotify.Watcher) {
	var delay <-chan time.Time
	for {
		select {
		case <-watcher.Event:
			if delay == nil {
				delay = time.After(tmplRefreshDelay)
			}
		case err, ok := <-watcher.Error:
			log.Printf("watch error: %s\n", err)
			if !ok {
				return
			}
		case <-delay:
			refreshTemplates()
			delay = nil
		}
	}
}

func watchRecursive(w *fsnotify.Watcher) {
	filepath.Walk(templateDir, func(path string, fi os.FileInfo, err error) error {
		if fi != nil && fi.IsDir() {
			if err := w.Watch(path); err != nil {
				log.Fatal(err)
			}
		}
		return nil
	})
}

func main() {
	flag.Parse()

	log.SetFlags(0)

	// Initial compilation of the templates, then activate the watcher
	refreshTemplates()
	w, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	go watchDir(w)
	watchRecursive(w)
	defer w.Close()

	h := handlers.FaviconHandler(
		handlers.PanicHandler(
			handlers.LogHandler(
				handlers.GZIPHandlerFunc(
					serveTemplate,
					nil),
				handlers.NewLogOptions(nil, handlers.Lshort)),
			nil),
		faviconPath,
		faviconCache)

	// Assign the combined handler to the server.
	http.Handle("/", h)

	// Start it up.
	log.Printf("server listening on port %d", port)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
		panic(err)
	}
}
<commit_msg>log instead of panic on server listen error<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"

	"github.com/PuerkitoBio/ghost/handlers"
	"github.com/PuerkitoBio/ghost/templates"
	_ "github.com/PuerkitoBio/ghost/templates/amber"
	_ "github.com/PuerkitoBio/ghost/templates/gotpl"
	"github.com/howeyc/fsnotify"
)

const (
	defPublicDir     = "./public/"
	defTemplateDir   = "./templates/"
	defPort          = 9000
	tmplRefreshDelay = 2 * time.Second
	faviconCache     = 1 * time.Minute
)

var (
	// Flags values
	publicDir   string
	templateDir string
	port        int

	faviconPath   = path.Join(publicDir, "favicon.ico")
	staticHandler = http.StripPrefix("/public/", http.FileServer(http.Dir(publicDir)))

	// Protect the templates
	tmplLock sync.RWMutex
)

func init() {
	flag.StringVar(&publicDir, "public-dir", defPublicDir, "set the public directory for static assets")
	flag.StringVar(&publicDir, "p", defPublicDir, "shorthand for -public-dir")
	flag.StringVar(&templateDir, "template-dir", defTemplateDir, "set the templates directory")
	flag.StringVar(&templateDir, "t", defTemplateDir, "shorthand for -template-dir")
	flag.IntVar(&port, "port", defPort, "set the port number")
	flag.IntVar(&port, "P", defPort, "shorthand for -port")
}

func serveTemplate(w http.ResponseWriter, r *http.Request) {
	// If a template matches the path, minus the starting /
	tmplLock.RLock()
	err := templates.Render(r.URL.Path[1:], w, nil)
	tmplLock.RUnlock()
	if err != nil {
		w.Header().Del("Content-Type")
		staticHandler.ServeHTTP(w, r)
	}
}

func refreshTemplates() {
	tmplLock.Lock()
	defer tmplLock.Unlock()
	if err := templates.CompileDir(templateDir); err != nil {
		// Do not stop if templates directory does not exist or has an invalid template
		log.Println(err)
	}
}

func watchDir(watcher *fsnotify.Watcher) {
	var delay <-chan time.Time
	for {
		select {
		case <-watcher.Event:
			if delay == nil {
				delay = time.After(tmplRefreshDelay)
			}
		case err, ok := <-watcher.Error:
			log.Printf("watch error: %s\n", err)
			if !ok {
				return
			}
		case <-delay:
			refreshTemplates()
			delay = nil
		}
	}
}

func watchRecursive(w *fsnotify.Watcher) {
	filepath.Walk(templateDir, func(path string, fi os.FileInfo, err error) error {
		if fi != nil && fi.IsDir() {
			if err := w.Watch(path); err != nil {
				log.Fatal(err)
			}
		}
		return nil
	})
}

// TODO : Show a default index.html page with all available links?
func main() {
	flag.Parse()

	log.SetFlags(0)

	// Initial compilation of the templates, then activate the watcher
	refreshTemplates()
	w, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	go watchDir(w)
	watchRecursive(w)
	defer w.Close()

	h := handlers.FaviconHandler(
		handlers.PanicHandler(
			handlers.LogHandler(
				handlers.GZIPHandlerFunc(
					serveTemplate,
					nil),
				handlers.NewLogOptions(nil, handlers.Lshort)),
			nil),
		faviconPath,
		faviconCache)

	// Assign the combined handler to the server.
	http.Handle("/", h)

	// Start it up.
	log.Printf("server listening on port %d", port)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"expvar"
	"flag"
	"log"
	"net"
	"net/http"
	nurl "net/url"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/donovanhide/eventsource"
	"github.com/fiorix/freegeoip"
	"github.com/monnand/goredis"
)

const (
	// ssePath is the PATH for the eventsource handler is going to be mounted on
	ssePath = "/push/"
)

var (
	numRegisterUsers   = expvar.NewInt("num_register_users")
	numUnregisterUsers = expvar.NewInt("num_unregister_users")
	numMessages        = expvar.NewInt("num_messages")
)

// Message is the bit of information that is transfered via eventsource
type Message struct {
	Idx           string
	Channel, HTML string
}

// Id is required to implement the eventsource.Event interface
func (c *Message) Id() string { return c.Idx }

// Event is required to implement the eventsource.Event interface
func (c *Message) Event() string { return c.Channel }

// Data is required to implement the eventsource.Event interface
func (c *Message) Data() string {
	return c.HTML
}

// Connection is use to relate a user token to a channel
type Connection struct {
	token   string
	channel string
}

type Location struct {
	Latitude  float64
	Longitude float64
	timezone  string
}

// Hub maintains the states
type Hub struct {
	Data       map[string][]string // Key is the channel, value is a slice of token
	Users      map[string]string   // Key is the token, value is a channel
	register   chan Connection
	unregister chan string
	messages   chan goredis.Message
	srv        *eventsource.Server
	client     goredis.Client
	ipdb       *freegeoip.DB
}

func (h *Hub) userExists(token string) bool {
	_, ok := h.Users[token]
	return ok
}

func (h *Hub) run() {
	log.Println("[Info] Start the Hub")
	var payload [4]string
	psub := make(chan string, 0)
	go h.client.Subscribe(nil, nil, psub, nil, h.messages)

	// Listening to all channel updates
	psub <- "channel_update:*"

	for {
		select {
		case conn := <-h.register:
			log.Println("[Info] register user: ", conn.token)
			h.Users[conn.token] = conn.channel
			h.Data[conn.channel] = append(h.Data[conn.channel], conn.token)
			numRegisterUsers.Add(1)

		case token := <-h.unregister:
			log.Println("[Info] Unregister user: ", token)
			ch, ok := h.Users[token]
			if ok {
				delete(h.Users, token)
				delete(h.Data, ch)
			}
			numUnregisterUsers.Add(1)

		case msg := <-h.messages:
			err := json.Unmarshal(msg.Message, &payload)
			if err != nil {
				log.Println("[Error] An error occured while Unmarshalling the msg: ", msg)
			}
			message := &Message{
				Idx:     payload[2],
				Channel: payload[0],
				HTML:    payload[1],
			}

			val, ok := h.Data[msg.Channel]
			if ok && len(val) >= 1 {
				//log.Println("[Debug] Publishing message", message)
				h.srv.Publish(val, message)
			}

			log.Println("[Debug] Message received", payload)
			log.Println("[Debug] ipdb", h.ipdb)

			if h.ipdb != nil {
				// lookup the host
				ips, err := net.LookupIP(payload[3])
				log.Println("[Debug] ips:", ips, "err:", err)
				if err == nil && len(ips) > 0 {
					var result map[string]interface{}
					err = h.ipdb.Lookup(ips[0], &result)
					if err != nil {
						log.Println(err)
					} else {
						location := reflect.ValueOf(result["location"])
						lat := location.Interface().(map[string]interface{})["latitude"]
						lon := location.Interface().(map[string]interface{})["longitude"]

						val, _ := json.Marshal([]float64{reflect.ValueOf(lat).Interface().(float64),
							reflect.ValueOf(lon).Interface().(float64)})
						message2 := &Message{
							Idx:     payload[2],
							Channel: "loc",
							HTML:    string(val),
						}

						log.Println("[Debug] Sending ip to glob ", message2)
						h.srv.Publish([]string{"glob"}, message2)
					}
				} else {
					log.Println("[Error] Cannot resolve host :'", payload[3], "'. ", err)
				}
			}

			numMessages.Add(1)
		}
	}
}

// EventSourceHandler implements the Handler interface
func (h *Hub) EventSourceHandler(w http.ResponseWriter, req *http.Request) {
	token := req.URL.Path[len(ssePath):]

	// The reserved name "glob" is accepted as a global channel that just sends stats
	if token == "glob" {
		log.Println("[Info] Connecting to the global channel")
		h.register <- Connection{"_", "glob"} // TODO: Need to come up with random, unique user ids
		defer func(u string) {
			h.unregister <- u
		}("_")
		h.srv.Handler(token)(w, req)

	} else {
		if h.userExists(token) {
			log.Println("[Info] Forbiden, user already connected")
			http.Error(w, "Forbiden", http.StatusForbidden)
		} else {
			log.Println("[Info] Exchange token against the channel list", token)
			val, err := h.client.Getset(token, []byte{})
			if err != nil {
				log.Println("[Error] occured while exchanging the your security token.", token, ":", err)
				http.Error(w, "Error occured while exchanging the your security token", http.StatusUnauthorized)

			} else if chanName := string(val); chanName != "" {
				log.Println("[Info] Connecting", token, "to the channel", chanName)
				h.register <- Connection{token, chanName}
				defer func(u string) {
					h.unregister <- u
				}(token)
				h.srv.Handler(token)(w, req)
			}
			_, err = h.client.Del(token)
			if err != nil {
				log.Println("[Error] An error occured while trying to delete the token from redis", err)
			}
		}
	}
}

// NewHub returns a pointer to a initialized and running Hub
func NewHub() *Hub {
	// Handle the flags. TODO: add flags for redis
	var dbPath string
	flag.StringVar(&dbPath, "ipdb-path", os.Getenv("IPDB_PATH"),
		"The path to the ip->location DB")
	flag.Parse()

	// Initialze the hub
	log.Println("[Info] Starting the eventsource Hub")

	var db *freegeoip.DB
	var err error
	if dbPath != "" {
		log.Println("[Info] Using ip->location DB ", dbPath)
		db, err = freegeoip.Open(dbPath)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		log.Println("[Info] No ip->location DB provided")
	}

	redisURLString := os.Getenv("REDIS_SSEQUEUE_URL")
	if redisURLString == "" {
		// Use db 2 by default for  pub/sub
		redisURLString = "redis://localhost:6379/2"
	}
	log.Println("[Info] Redis configuration used for pub/sub", redisURLString)
	redisURL, err := nurl.Parse(redisURLString)
	if err != nil {
		log.Fatal("Could not read Redis string", err)
	}

	redisDb, err := strconv.Atoi(strings.TrimLeft(redisURL.Path, "/"))
	if err != nil {
		log.Fatal("[Error] Could not read Redis path", err)
	}

	server := eventsource.NewServer()
	server.AllowCORS = true

	h := Hub{
		Data:       make(map[string][]string),
		Users:      make(map[string]string),
		register:   make(chan Connection, 0),
		unregister: make(chan string, 0),
		messages:   make(chan goredis.Message, 0),
		srv:        server,
		client:     goredis.Client{Addr: redisURL.Host, Db: redisDb},
		ipdb:       db,
	}

	go h.run()
	return &h
}

func main() {
	sseString := os.Getenv("SSE_HOST")

	if sseString == "" {
		log.Fatal("SSE_HOST is not set, example: SSE_HOST=localhost:3000")
	}
	h := NewHub()

	// eventsource endpoints
	log.Println("[Info] botbot-eventsource is listening on " + sseString)
	http.HandleFunc(ssePath, h.EventSourceHandler)

	log.Fatalln(http.ListenAndServe(sseString, nil))
}
<commit_msg>removed debug data<commit_after>package main

import (
	"encoding/json"
	"expvar"
	"flag"
	"log"
	"net"
	"net/http"
	nurl "net/url"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/donovanhide/eventsource"
	"github.com/fiorix/freegeoip"
	"github.com/monnand/goredis"
)

const (
	// ssePath is the PATH for the eventsource handler is going to be mounted on
	ssePath = "/push/"
)

var (
	numRegisterUsers   = expvar.NewInt("num_register_users")
	numUnregisterUsers = expvar.NewInt("num_unregister_users")
	numMessages        = expvar.NewInt("num_messages")
)

// Message is the bit of information that is transfered via eventsource
type Message struct {
	Idx           string
	Channel, HTML string
}

// Id is required to implement the eventsource.Event interface
func (c *Message) Id() string { return c.Idx }

// Event is required to implement the eventsource.Event interface
func (c *Message) Event() string { return c.Channel }

// Data is required to implement the eventsource.Event interface
func (c *Message) Data() string {
	return c.HTML
}

// Connection is use to relate a user token to a channel
type Connection struct {
	token   string
	channel string
}

type Location struct {
	Latitude  float64
	Longitude float64
	timezone  string
}

// Hub maintains the states
type Hub struct {
	Data       map[string][]string // Key is the channel, value is a slice of token
	Users      map[string]string   // Key is the token, value is a channel
	register   chan Connection
	unregister chan string
	messages   chan goredis.Message
	srv        *eventsource.Server
	client     goredis.Client
	ipdb       *freegeoip.DB
}

func (h *Hub) userExists(token string) bool {
	_, ok := h.Users[token]
	return ok
}

func (h *Hub) run() {
	log.Println("[Info] Start the Hub")
	var payload [4]string
	psub := make(chan string, 0)
	go h.client.Subscribe(nil, nil, psub, nil, h.messages)

	// Listening to all channel updates
	psub <- "channel_update:*"

	for {
		select {
		case conn := <-h.register:
			log.Println("[Info] register user: ", conn.token)
			h.Users[conn.token] = conn.channel
			h.Data[conn.channel] = append(h.Data[conn.channel], conn.token)
			numRegisterUsers.Add(1)

		case token := <-h.unregister:
			log.Println("[Info] Unregister user: ", token)
			ch, ok := h.Users[token]
			if ok {
				delete(h.Users, token)
				delete(h.Data, ch)
			}
			numUnregisterUsers.Add(1)

		case msg := <-h.messages:
			err := json.Unmarshal(msg.Message, &payload)
			if err != nil {
				log.Println("[Error] An error occured while Unmarshalling the msg: ", msg)
			}
			message := &Message{
				Idx:     payload[2],
				Channel: payload[0],
				HTML:    payload[1],
			}

			val, ok := h.Data[msg.Channel]
			if ok && len(val) >= 1 {
				//log.Println("[Debug] Publishing message", message)
				h.srv.Publish(val, message)
			}

			if h.ipdb != nil {
				// lookup the host
				ips, err := net.LookupIP(payload[3])
				if err == nil && len(ips) > 0 {
					var result map[string]interface{}
					err = h.ipdb.Lookup(ips[0], &result)
					if err != nil {
						log.Println(err)
					} else {
						location := reflect.ValueOf(result["location"])
						lat := location.Interface().(map[string]interface{})["latitude"]
						lon := location.Interface().(map[string]interface{})["longitude"]

						val, _ := json.Marshal([]float64{reflect.ValueOf(lat).Interface().(float64),
							reflect.ValueOf(lon).Interface().(float64)})
						message2 := &Message{
							Idx:     payload[2],
							Channel: "loc",
							HTML:    string(val),
						}

						log.Println("[Debug] Sending ip to glob ", message2)
						h.srv.Publish([]string{"glob"}, message2)
					}
				} else {
					log.Println("[Error] Cannot resolve host :'", payload[3], "'. ", err)
				}
			}

			numMessages.Add(1)
		}
	}
}

// EventSourceHandler implements the Handler interface
func (h *Hub) EventSourceHandler(w http.ResponseWriter, req *http.Request) {
	token := req.URL.Path[len(ssePath):]

	// The reserved name "glob" is accepted as a global channel that just sends stats
	if token == "glob" {
		log.Println("[Info] Connecting to the global channel")
		h.register <- Connection{"_", "glob"} // TODO: Need to come up with random, unique user ids
		defer func(u string) {
			h.unregister <- u
		}("_")
		h.srv.Handler(token)(w, req)

	} else {
		if h.userExists(token) {
			log.Println("[Info] Forbiden, user already connected")
			http.Error(w, "Forbiden", http.StatusForbidden)
		} else {
			log.Println("[Info] Exchange token against the channel list", token)
			val, err := h.client.Getset(token, []byte{})
			if err != nil {
				log.Println("[Error] occured while exchanging the your security token.", token, ":", err)
				http.Error(w, "Error occured while exchanging the your security token", http.StatusUnauthorized)

			} else if chanName := string(val); chanName != "" {
				log.Println("[Info] Connecting", token, "to the channel", chanName)
				h.register <- Connection{token, chanName}
				defer func(u string) {
					h.unregister <- u
				}(token)
				h.srv.Handler(token)(w, req)
			}
			_, err = h.client.Del(token)
			if err != nil {
				log.Println("[Error] An error occured while trying to delete the token from redis", err)
			}
		}
	}
}

// NewHub returns a pointer to a initialized and running Hub
func NewHub() *Hub {
	// Handle the flags. TODO: add flags for redis
	var dbPath string
	flag.StringVar(&dbPath, "ipdb-path", os.Getenv("IPDB_PATH"),
		"The path to the ip->location DB")
	flag.Parse()

	// Initialze the hub
	log.Println("[Info] Starting the eventsource Hub")

	var db *freegeoip.DB
	var err error
	if dbPath != "" {
		log.Println("[Info] Using ip->location DB ", dbPath)
		db, err = freegeoip.Open(dbPath)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		log.Println("[Info] No ip->location DB provided")
	}

	redisURLString := os.Getenv("REDIS_SSEQUEUE_URL")
	if redisURLString == "" {
		// Use db 2 by default for  pub/sub
		redisURLString = "redis://localhost:6379/2"
	}
	log.Println("[Info] Redis configuration used for pub/sub", redisURLString)
	redisURL, err := nurl.Parse(redisURLString)
	if err != nil {
		log.Fatal("Could not read Redis string", err)
	}

	redisDb, err := strconv.Atoi(strings.TrimLeft(redisURL.Path, "/"))
	if err != nil {
		log.Fatal("[Error] Could not read Redis path", err)
	}

	server := eventsource.NewServer()
	server.AllowCORS = true

	h := Hub{
		Data:       make(map[string][]string),
		Users:      make(map[string]string),
		register:   make(chan Connection, 0),
		unregister: make(chan string, 0),
		messages:   make(chan goredis.Message, 0),
		srv:        server,
		client:     goredis.Client{Addr: redisURL.Host, Db: redisDb},
		ipdb:       db,
	}

	go h.run()
	return &h
}

func main() {
	sseString := os.Getenv("SSE_HOST")

	if sseString == "" {
		log.Fatal("SSE_HOST is not set, example: SSE_HOST=localhost:3000")
	}
	h := NewHub()

	// eventsource endpoints
	log.Println("[Info] botbot-eventsource is listening on " + sseString)
	http.HandleFunc(ssePath, h.EventSourceHandler)

	log.Fatalln(http.ListenAndServe(sseString, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/coreos/go-omaha/omaha"
	"github.com/elazarl/goproxy"
	"github.com/fsouza/go-dockerclient"
)

func ParseRequest(req *http.Request) omaha.Request {
	var OmahaRequest omaha.Request

	defer req.Body.Close()
	data, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Fatal(err)
	}
	if data == nil {
		log.Fatalln("No request body data.")
	}

	err = xml.Unmarshal(data, &OmahaRequest)
	if err != nil {
		log.Fatal(err)
	}
	return OmahaRequest
}

func ParseResponse(res *http.Response) omaha.Response {
	var OmahaResponse omaha.Response
	defer res.Body.Close()
	data, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	if data == nil {
		log.Fatalln("No response body data.")
	}

	err = xml.Unmarshal(data, &OmahaResponse)
	if err != nil {
		log.Fatal(err)
	}
	return OmahaResponse
}

func main() {
	var OriginalRequest http.Request
	var AppsWithUpdates []omaha.App
	endpoint := "unix:///var/run/docker.sock"
	client, err := docker.NewClient(endpoint)
	if err != nil {
		log.Fatal(err)
	}

	proxy := goproxy.NewProxyHttpServer()
	proxy.OnRequest(goproxy.UrlIs("public.update.core-os.net/v1/update/")).HandleConnect(goproxy.AlwaysMitm)
	proxy.OnRequest(goproxy.UrlIs("public.update.core-os.net/v1/update/")).DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
		OriginalRequest = req
		OmahaRequest := ParseRequest(req)
		return req, nil
	})
	proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
		OmahaResponse := ParseResponse(resp)

		for i := range OmahaResponse.Apps {
			if OmahaResponse.Apps[i].Status == "ok" {
				AppsWithUpdates = append(AppsWithUpdates, OmahaResponse.Apps[i])
			}
		}
		goproxy.NewResponse(OriginalRequest, goproxy.ContentTypeHtml, http.StatusOK, CorePXEResponse)

		resp.Header.Set("X-COREPXE", "corepxe")
		return resp
	})
	proxy.Verbose = true
	log.Fatal(http.ListenAndServe(":8080", proxy))

}
<commit_msg>check correct update status field<commit_after>package main

import (
	"encoding/xml"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/coreos/go-omaha/omaha"
	"github.com/elazarl/goproxy"
	"github.com/fsouza/go-dockerclient"
)

func ParseRequest(req *http.Request) omaha.Request {
	var OmahaRequest omaha.Request

	defer req.Body.Close()
	data, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Fatal(err)
	}
	if data == nil {
		log.Fatalln("No request body data.")
	}

	err = xml.Unmarshal(data, &OmahaRequest)
	if err != nil {
		log.Fatal(err)
	}
	return OmahaRequest
}

func ParseResponse(res *http.Response) omaha.Response {
	var OmahaResponse omaha.Response
	defer res.Body.Close()
	data, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	if data == nil {
		log.Fatalln("No response body data.")
	}

	err = xml.Unmarshal(data, &OmahaResponse)
	if err != nil {
		log.Fatal(err)
	}
	return OmahaResponse
}

func main() {
	var OriginalRequest http.Request
	var AppsWithUpdates []omaha.App
	endpoint := "unix:///var/run/docker.sock"
	client, err := docker.NewClient(endpoint)
	if err != nil {
		log.Fatal(err)
	}

	proxy := goproxy.NewProxyHttpServer()
	proxy.OnRequest(goproxy.UrlIs("public.update.core-os.net/v1/update/")).HandleConnect(goproxy.AlwaysMitm)
	proxy.OnRequest(goproxy.UrlIs("public.update.core-os.net/v1/update/")).DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
		OriginalRequest = req
		OmahaRequest := ParseRequest(req)
		return req, nil
	})
	proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
		OmahaResponse := ParseResponse(resp)

		for i := range OmahaResponse.Apps {
			if OmahaResponse.Apps[i].UpdateCheck.Status == "ok" {
				AppsWithUpdates = append(AppsWithUpdates, OmahaResponse.Apps[i])
			}
		}
		goproxy.NewResponse(OriginalRequest, goproxy.ContentTypeHtml, http.StatusOK, CorePXEResponse)

		resp.Header.Set("X-COREPXE", "corepxe")
		return resp
	})
	proxy.Verbose = true
	log.Fatal(http.ListenAndServe(":8080", proxy))

}
<|endoftext|>


<commit_before>package fiber

import (
	"context"
	"errors"

	"github.com/airbrake/gobrake/v5"

	"github.com/gofiber/fiber/v2"
)

// New returns a function that satisfies fiber.Handler interface
func New(notifier *gobrake.Notifier) fiber.Handler {
	return func(c *fiber.Ctx) error {
		if notifier == nil {
			return errors.New("airbrake notifier not defined")
		}
		_, metric := gobrake.NewRouteMetric(context.TODO(), c.Route().Method, c.Route().Path)
		err := c.Next()
		metric.StatusCode = c.Response().StatusCode()
		_ = notifier.Routes.Notify(context.TODO(), metric)
		return err
	}
}
<commit_msg>Update after feedback<commit_after>package fiber

import (
	"context"
	"errors"

	"github.com/airbrake/gobrake/v5"

	"github.com/gofiber/fiber/v2"
)

// New returns a function that satisfies fiber.Handler interface
func New(notifier *gobrake.Notifier) fiber.Handler {
	return func(c *fiber.Ctx) error {
		if notifier == nil {
			return errors.New("airbrake notifier not defined")
		}

		// Starts the timer.
		_, metric := gobrake.NewRouteMetric(context.TODO(), c.Route().Method, c.Route().Path)
		err := c.Next()

		// capture the status code and resolved Route
		metric.StatusCode = c.Response().StatusCode()
		metric.Route = c.Route().Path

		// Send to Airbrake
		_ = notifier.Routes.Notify(context.TODO(), metric)
		return err
	}
}
<|endoftext|>


<commit_before>package hdfs

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"hash/crc32"
	"io"
	"io/ioutil"
	"math/rand"
	"os"
	"testing"
	"time"

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

const (
	// many grep -b died to bring us this information...
	testStr    = "Abominable are the tumblers into which he pours his poison."
	testStrOff = 48847

	testStr2            = "http://www.gutenberg.org"
	testStr2Off         = 1256988
	testStr2NegativeOff = -288

	testChecksum = "27c076e4987344253650d3335a5d08ce"
)

func TestFileRead(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	bytes, err := ioutil.ReadAll(file)
	assert.NoError(t, err)
	assert.EqualValues(t, "bar\n", string(bytes))

	info := file.Stat()
	assert.False(t, info.IsDir())
	assert.EqualValues(t, 4, info.Size())
	assert.EqualValues(t, time.Now().Year(), info.ModTime().Year())
	assert.EqualValues(t, time.Now().Month(), info.ModTime().Month())
}

func TestReadEmptyFile(t *testing.T) {
	client := getClient(t)

	touch(t, "/_test/emptyfile")

	file, err := client.Open("/_test/emptyfile")
	require.NoError(t, err)

	bytes, err := ioutil.ReadAll(file)
	assert.NoError(t, err)
	assert.EqualValues(t, "", string(bytes))
}

func TestFileBigRead(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	hash := crc32.NewIEEE()
	n, err := io.Copy(hash, file)
	assert.NoError(t, err)
	assert.EqualValues(t, n, 1257276)
	assert.EqualValues(t, 0x199d1ae6, hash.Sum32())
}

func TestFileBigReadWeirdSizes(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	hash := crc32.NewIEEE()
	copied := 0
	var n int64
	for err == nil {
		n, err = io.CopyN(hash, file, int64(rand.Intn(1000)))
		copied += int(n)
	}

	assert.EqualValues(t, io.EOF, err)
	assert.EqualValues(t, 0x199d1ae6, hash.Sum32())
	assert.EqualValues(t, copied, 1257276)
}

func TestFileBigReadN(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	n, err := io.CopyN(ioutil.Discard, file, 1000000)
	assert.NoError(t, err)
	assert.EqualValues(t, n, 1000000)
}

func TestFileReadNil(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	n, err := file.Read(nil)
	assert.NoError(t, err)
	assert.EqualValues(t, n, 0)
}

func TestFileReadAt(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	buf := make([]byte, len(testStr))
	off := 0
	for off < len(buf) {
		n, err := file.ReadAt(buf[off:], int64(testStrOff+off))
		assert.NoError(t, err)
		assert.True(t, n > 0)
		off += n
	}

	assert.EqualValues(t, string(buf), testStr)

	buf = make([]byte, len(testStr2))
	off = 0
	for off < len(buf) {
		n, err := file.ReadAt(buf[off:], int64(testStr2Off+off))
		assert.NoError(t, err)
		assert.True(t, n > 0)
		off += n
	}

	assert.EqualValues(t, testStr2, string(buf))
}

func TestFileReadAtEOF(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	buf := make([]byte, 10)
	_, err = file.ReadAt(buf, 1)

	assert.Equal(t, err, io.EOF)
}

func TestFileSeek(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	buf := new(bytes.Buffer)

	off, err := file.Seek(testStrOff, 0)
	assert.NoError(t, err)
	assert.EqualValues(t, testStrOff, off)

	n, err := io.CopyN(buf, file, int64(len(testStr)))
	assert.NoError(t, err)
	assert.EqualValues(t, len(testStr), n)
	assert.EqualValues(t, testStr, string(buf.Bytes()))

	// seek backwards and read it again
	off, err = file.Seek(-1*int64(len(testStr)), 1)
	assert.NoError(t, err)
	assert.EqualValues(t, testStrOff, off)

	buf.Reset()
	n, err = io.CopyN(buf, file, int64(len(testStr)))
	assert.NoError(t, err)
	assert.EqualValues(t, len(testStr), n)
	assert.EqualValues(t, testStr, string(buf.Bytes()))

	// now seek forward to another block and read a string
	off, err = file.Seek(testStr2NegativeOff, 2)
	assert.NoError(t, err)
	assert.EqualValues(t, testStr2Off, off)

	buf.Reset()
	n, err = io.CopyN(buf, file, int64(len(testStr2)))
	assert.NoError(t, err)
	assert.EqualValues(t, len(testStr2), n)
	assert.EqualValues(t, testStr2, string(buf.Bytes()))
}

func TestFileReadDir(t *testing.T) {
	client := getClient(t)

	baleet(t, "/_test/fulldir3")
	mkdirp(t, "/_test/fulldir3/dir")
	touch(t, "/_test/fulldir3/1")
	touch(t, "/_test/fulldir3/2")
	touch(t, "/_test/fulldir3/3")

	file, err := client.Open("/_test/fulldir3")
	require.NoError(t, err)

	res, err := file.Readdir(2)
	require.Equal(t, 2, len(res))
	assert.EqualValues(t, "1", res[0].Name())
	assert.EqualValues(t, "2", res[1].Name())

	res, err = file.Readdir(5)
	require.Equal(t, 2, len(res))
	assert.EqualValues(t, "3", res[0].Name())
	assert.EqualValues(t, "dir", res[1].Name())

	res, err = file.Readdir(0)
	require.Equal(t, 4, len(res))
	assert.EqualValues(t, "1", res[0].Name())
	assert.EqualValues(t, "2", res[1].Name())
	assert.EqualValues(t, "3", res[2].Name())
	assert.EqualValues(t, "dir", res[3].Name())
}

func TestFileReadDirnames(t *testing.T) {
	client := getClient(t)

	baleet(t, "/_test/fulldir4")
	mkdirp(t, "/_test/fulldir4/dir")
	touch(t, "/_test/fulldir4/1")
	touch(t, "/_test/fulldir4/2")
	touch(t, "/_test/fulldir4/3")

	file, err := client.Open("/_test/fulldir4")
	require.NoError(t, err)

	res, err := file.Readdirnames(0)
	require.Equal(t, 4, len(res))
	assert.EqualValues(t, []string{"1", "2", "3", "dir"}, res)
}

func TestFileReadDirMany(t *testing.T) {
	client := getClient(t)

	maxReadDir := 1000 // HDFS returns this many entries.
	total := maxReadDir*2 + maxReadDir/2 + 35
	firstBatch := maxReadDir + 71

	mkdirp(t, "/_test/fulldir5")
	for i := 0; i < total; i++ {
		touch(t, fmt.Sprintf("/_test/fulldir5/%04d", i))
	}

	file, err := client.Open("/_test/fulldir5")
	require.NoError(t, err)

	res, err := file.Readdir(firstBatch)
	require.Equal(t, firstBatch, len(res))
	for i := range res {
		assert.EqualValues(t, fmt.Sprintf("%04d", i), res[i].Name())
	}

	res, err = file.Readdir(total)
	assert.Equal(t, total-firstBatch, len(res))
	for i := range res {
		assert.EqualValues(t, fmt.Sprintf("%04d", i+firstBatch), res[i].Name())
	}

	res, err = file.Readdir(0)
	require.Equal(t, total, len(res))
	for i := range res {
		assert.EqualValues(t, fmt.Sprintf("%04d", i), res[i].Name())
	}
}

func TestOpenFileWithoutPermission(t *testing.T) {
	client2 := getClientForUser(t, "gohdfs2")

	mkdirpMask(t, "/_test/accessdenied", 0700)
	touchMask(t, "/_test/accessdenied/foo", 0700)

	file, err := client2.Open("/_test/accessdenied/foo")
	assertPathError(t, err, "open", "/_test/accessdenied/foo", os.ErrPermission)
	assert.Nil(t, file)
}

func TestFileChecksum(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	checksum, err := file.Checksum()
	require.NoError(t, err)

	assert.EqualValues(t, testChecksum, hex.EncodeToString(checksum))
}

func TestFileReadDeadline(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	file.SetDeadline(time.Now().Add(100 * time.Millisecond))
	_, err = file.Read([]byte{0, 0})
	assert.NoError(t, err)

	time.Sleep(100 * time.Millisecond)
	_, err = file.Read([]byte{0, 0})
	assert.NotNil(t, err)
}

func TestFileReadDeadlineBefore(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	file.SetDeadline(time.Now())
	_, err = file.Read([]byte{0, 0})
	assert.NotNil(t, err)
}

func TestFileChecksumDeadline(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	file.SetDeadline(time.Now().Add(100 * time.Millisecond))
	_, err = file.Checksum()
	assert.NoError(t, err)

	time.Sleep(100 * time.Millisecond)
	_, err = file.Checksum()
	assert.NotNil(t, err)
}

func TestFileChecksumDeadlineBefore(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	file.SetDeadline(time.Now())
	_, err = file.Checksum()
	assert.NotNil(t, err)
}
<commit_msg>Add some tests for edgy Read behavior<commit_after>package hdfs

import (
	"bytes"
	"context"
	"encoding/hex"
	"fmt"
	"hash/crc32"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"os"
	"testing"
	"time"

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

const (
	// many grep -b died to bring us this information...
	testStr    = "Abominable are the tumblers into which he pours his poison."
	testStrOff = 48847

	testStr2            = "http://www.gutenberg.org"
	testStr2Off         = 1256988
	testStr2NegativeOff = -288

	testChecksum = "27c076e4987344253650d3335a5d08ce"
)

type randomReadConn struct {
	net.Conn
}

func (r *randomReadConn) Read(b []byte) (int, error) {
	return r.Conn.Read(b[0:rand.Intn(len(b)+1)])
}

func TestFileRead(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	bytes, err := ioutil.ReadAll(file)
	assert.NoError(t, err)
	assert.EqualValues(t, "bar\n", string(bytes))

	info := file.Stat()
	assert.False(t, info.IsDir())
	assert.EqualValues(t, 4, info.Size())
	assert.EqualValues(t, time.Now().Year(), info.ModTime().Year())
	assert.EqualValues(t, time.Now().Month(), info.ModTime().Month())
}

func TestReadEmptyFile(t *testing.T) {
	client := getClient(t)

	touch(t, "/_test/emptyfile")

	file, err := client.Open("/_test/emptyfile")
	require.NoError(t, err)

	bytes, err := ioutil.ReadAll(file)
	assert.NoError(t, err)
	assert.EqualValues(t, "", string(bytes))
}

func TestFileBigRead(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	hash := crc32.NewIEEE()
	n, err := io.Copy(hash, file)
	assert.NoError(t, err)
	assert.EqualValues(t, n, 1257276)
	assert.EqualValues(t, 0x199d1ae6, hash.Sum32())
}

func TestFileBigReadWeirdSizes(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	hash := crc32.NewIEEE()
	copied := 0
	var n int64
	for err == nil {
		n, err = io.CopyN(hash, file, int64(rand.Intn(1000)))
		copied += int(n)
	}

	assert.EqualValues(t, io.EOF, err)
	assert.EqualValues(t, 0x199d1ae6, hash.Sum32())
	assert.EqualValues(t, copied, 1257276)
}

func TestFileBigReadWeirdSizesMisalignment(t *testing.T) {
	client := getClient(t)
	dial := client.options.DatanodeDialFunc
	if dial == nil {
		dial = (&net.Dialer{}).DialContext
	}

	client.options.DatanodeDialFunc = func(ctx context.Context, network, address string) (net.Conn, error) {
		conn, err := dial(ctx, network, address)
		if err != nil {
			return nil, err
		}

		return &randomReadConn{conn}, nil
	}

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	hash := crc32.NewIEEE()
	copied := 0
	var n int64
	for err == nil {
		n, err = io.CopyN(hash, file, int64(rand.Intn(1000)))
		copied += int(n)
	}

	assert.EqualValues(t, io.EOF, err)
	assert.EqualValues(t, 0x199d1ae6, hash.Sum32())
	assert.EqualValues(t, copied, 1257276)
}

func TestFileBigReadN(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	n, err := io.CopyN(ioutil.Discard, file, 1000000)
	assert.NoError(t, err)
	assert.EqualValues(t, n, 1000000)
}

func TestFileReadNil(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	n, err := file.Read(nil)
	assert.NoError(t, err)
	assert.EqualValues(t, n, 0)
}

func TestFileReadAt(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	buf := make([]byte, len(testStr))
	off := 0
	for off < len(buf) {
		n, err := file.ReadAt(buf[off:], int64(testStrOff+off))
		assert.NoError(t, err)
		assert.True(t, n > 0)
		off += n
	}

	assert.EqualValues(t, string(buf), testStr)

	buf = make([]byte, len(testStr2))
	off = 0
	for off < len(buf) {
		n, err := file.ReadAt(buf[off:], int64(testStr2Off+off))
		assert.NoError(t, err)
		assert.True(t, n > 0)
		off += n
	}

	assert.EqualValues(t, testStr2, string(buf))
}

func TestFileReadAtEOF(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	buf := make([]byte, 10)
	_, err = file.ReadAt(buf, 1)

	assert.Equal(t, append([]byte{'a', 'r', '\n'}, make([]byte, 7)...), buf)
	assert.Equal(t, io.EOF, err)
}

func TestFileReadOversizedBuffer(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	buf := make([]byte, 1025)
	n, err := file.Read(buf)

	assert.Equal(t, 4, n)
	assert.Equal(t, append([]byte{'b', 'a', 'r', '\n'}, make([]byte, 1025-4)...), buf)
	if err != io.EOF {
		assert.NoError(t, err)
	}
}

func TestFileSeek(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/mobydick.txt")
	require.NoError(t, err)

	buf := new(bytes.Buffer)

	off, err := file.Seek(testStrOff, 0)
	assert.NoError(t, err)
	assert.EqualValues(t, testStrOff, off)

	n, err := io.CopyN(buf, file, int64(len(testStr)))
	assert.NoError(t, err)
	assert.EqualValues(t, len(testStr), n)
	assert.EqualValues(t, testStr, string(buf.Bytes()))

	// seek backwards and read it again
	off, err = file.Seek(-1*int64(len(testStr)), 1)
	assert.NoError(t, err)
	assert.EqualValues(t, testStrOff, off)

	buf.Reset()
	n, err = io.CopyN(buf, file, int64(len(testStr)))
	assert.NoError(t, err)
	assert.EqualValues(t, len(testStr), n)
	assert.EqualValues(t, testStr, string(buf.Bytes()))

	// now seek forward to another block and read a string
	off, err = file.Seek(testStr2NegativeOff, 2)
	assert.NoError(t, err)
	assert.EqualValues(t, testStr2Off, off)

	buf.Reset()
	n, err = io.CopyN(buf, file, int64(len(testStr2)))
	assert.NoError(t, err)
	assert.EqualValues(t, len(testStr2), n)
	assert.EqualValues(t, testStr2, string(buf.Bytes()))
}

func TestFileReadDir(t *testing.T) {
	client := getClient(t)

	baleet(t, "/_test/fulldir3")
	mkdirp(t, "/_test/fulldir3/dir")
	touch(t, "/_test/fulldir3/1")
	touch(t, "/_test/fulldir3/2")
	touch(t, "/_test/fulldir3/3")

	file, err := client.Open("/_test/fulldir3")
	require.NoError(t, err)

	res, err := file.Readdir(2)
	require.Equal(t, 2, len(res))
	assert.EqualValues(t, "1", res[0].Name())
	assert.EqualValues(t, "2", res[1].Name())

	res, err = file.Readdir(5)
	require.Equal(t, 2, len(res))
	assert.EqualValues(t, "3", res[0].Name())
	assert.EqualValues(t, "dir", res[1].Name())

	res, err = file.Readdir(0)
	require.Equal(t, 4, len(res))
	assert.EqualValues(t, "1", res[0].Name())
	assert.EqualValues(t, "2", res[1].Name())
	assert.EqualValues(t, "3", res[2].Name())
	assert.EqualValues(t, "dir", res[3].Name())
}

func TestFileReadDirnames(t *testing.T) {
	client := getClient(t)

	baleet(t, "/_test/fulldir4")
	mkdirp(t, "/_test/fulldir4/dir")
	touch(t, "/_test/fulldir4/1")
	touch(t, "/_test/fulldir4/2")
	touch(t, "/_test/fulldir4/3")

	file, err := client.Open("/_test/fulldir4")
	require.NoError(t, err)

	res, err := file.Readdirnames(0)
	require.Equal(t, 4, len(res))
	assert.EqualValues(t, []string{"1", "2", "3", "dir"}, res)
}

func TestFileReadDirMany(t *testing.T) {
	client := getClient(t)

	maxReadDir := 1000 // HDFS returns this many entries.
	total := maxReadDir*2 + maxReadDir/2 + 35
	firstBatch := maxReadDir + 71

	mkdirp(t, "/_test/fulldir5")
	for i := 0; i < total; i++ {
		touch(t, fmt.Sprintf("/_test/fulldir5/%04d", i))
	}

	file, err := client.Open("/_test/fulldir5")
	require.NoError(t, err)

	res, err := file.Readdir(firstBatch)
	require.Equal(t, firstBatch, len(res))
	for i := range res {
		assert.EqualValues(t, fmt.Sprintf("%04d", i), res[i].Name())
	}

	res, err = file.Readdir(total)
	assert.Equal(t, total-firstBatch, len(res))
	for i := range res {
		assert.EqualValues(t, fmt.Sprintf("%04d", i+firstBatch), res[i].Name())
	}

	res, err = file.Readdir(0)
	require.Equal(t, total, len(res))
	for i := range res {
		assert.EqualValues(t, fmt.Sprintf("%04d", i), res[i].Name())
	}
}

func TestOpenFileWithoutPermission(t *testing.T) {
	client2 := getClientForUser(t, "gohdfs2")

	mkdirpMask(t, "/_test/accessdenied", 0700)
	touchMask(t, "/_test/accessdenied/foo", 0700)

	file, err := client2.Open("/_test/accessdenied/foo")
	assertPathError(t, err, "open", "/_test/accessdenied/foo", os.ErrPermission)
	assert.Nil(t, file)
}

func TestFileChecksum(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	checksum, err := file.Checksum()
	require.NoError(t, err)

	assert.EqualValues(t, testChecksum, hex.EncodeToString(checksum))
}

func TestFileReadDeadline(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	file.SetDeadline(time.Now().Add(100 * time.Millisecond))
	_, err = file.Read([]byte{0, 0})
	assert.NoError(t, err)

	time.Sleep(100 * time.Millisecond)
	_, err = file.Read([]byte{0, 0})
	assert.NotNil(t, err)
}

func TestFileReadDeadlineBefore(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	file.SetDeadline(time.Now())
	_, err = file.Read([]byte{0, 0})
	assert.NotNil(t, err)
}

func TestFileChecksumDeadline(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	file.SetDeadline(time.Now().Add(100 * time.Millisecond))
	_, err = file.Checksum()
	assert.NoError(t, err)

	time.Sleep(100 * time.Millisecond)
	_, err = file.Checksum()
	assert.NotNil(t, err)
}

func TestFileChecksumDeadlineBefore(t *testing.T) {
	client := getClient(t)

	file, err := client.Open("/_test/foo.txt")
	require.NoError(t, err)

	file.SetDeadline(time.Now())
	_, err = file.Checksum()
	assert.NotNil(t, err)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"log"

	"github.com/charles-d-burton/gphoto2go"
	"github.com/gin-gonic/gin"
)

func main() {
	camera := new(gphoto2go.Camera)
	err := camera.Init()
	if err > 0 {
		log.Println(err)
	}
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})

	r.GET("/shot", func(c *gin.Context) {
		cameraFilePath, err := camera.TriggerCaptureToFile()
		if err == 0 {
			cameraFileReader := camera.FileReader(cameraFilePath.Folder, cameraFilePath.Name)
			buf := new(bytes.Buffer)
			buf.ReadFrom(cameraFileReader)

			c.Data(200, "image/jpeg", buf.Bytes())
		}
	})
	r.Run()
}
<commit_msg>Added interrupt and delete file.<commit_after>package main

import (
	"bytes"
	"log"

	"github.com/charles-d-burton/gphoto2go"
	"github.com/gin-gonic/gin"
)

func main() {
	camera := new(gphoto2go.Camera)
	err := camera.Init()
	if err > 0 {
		log.Println(err)
	}
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})

	r.GET("/shot", func(c *gin.Context) {
		camera.Interrupt()
		cameraFilePath, err := camera.TriggerCaptureToFile()
		if err == 0 {

			cameraFileReader := camera.FileReader(cameraFilePath.Folder, cameraFilePath.Name)
			defer cameraFileReader.Close()
			buf := new(bytes.Buffer)
			buf.ReadFrom(cameraFileReader)
			camera.DeleteFile(cameraFilePath.Folder, cameraFilePath.Name)

			c.Data(200, "image/jpeg", buf.Bytes())

		}
		log.Println(gphoto2go.CameraResultToString(err))
	})
	r.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/briandowns/spinner"
	"github.com/davidnix/ffdraft/command"
	"github.com/davidnix/ffdraft/players"
	"log"
	"strings"
	"time"
)

const usage = `
--------------------------------------------------------------------------------------------------------------------
Commands:
    find, f [player name]: fuzzy finds players matching player name
    pick, p [player name]: fuzzy finds players and asks which player to pick, removing them from the draft pool
    unpick, u [player name]: fuzzy finds players and asks which player to unpick, adding them back to the draft pool
    floor: print the highest floor value for available players for each position
    ceil: print the highest ceiling value for available players for each position
    help, h: print this usage text
    exit: exits this program
*By default, this program always prints the result of the floor command after every command.
--------------------------------------------------------------------------------------------------------------------
`

func main() {
	fmt.Println("Welcome to fantasy football!")
	fmt.Println(usage)
	fmt.Println("Fetching current player data...")

	s := startSpinner()
	//undrafted, err := players.Load()
	undrafted, err := players.LoadFromFile("./test/ff_response_fixture.json")
	s.Stop()
	if err != nil {
		log.Fatal("unable to fetch player data:", err)
	}
	repo := players.NewRepo(undrafted)
	fmt.Println("Loaded", len(repo.UnDrafted), "offensive players")
	command.Floor(repo)

Loop:
	for {
		input := strings.Fields(command.GetInput())
		var cmd string
		var args []string
		if len(input) > 0 {
			cmd = input[0]
			args = input[1:]
		}

		switch cmd {
		case "find", "f":
			command.Find(repo, args)

		case "pick", "p":
			fmt.Println("pick from a list of players")

		case "unpick", "u":
			fmt.Println("unpick from a list of players")

		case "floor", "fl":
			command.Floor(repo)

		case "ceil":
            command.Ceil(repo)

		case "help", "h", "usage":
			fmt.Println(usage)

		case "exit":
			break Loop

		case "":
			break

		default:
			fmt.Println("Unrecognized command \"" + cmd + "\". Type help for usage.")
		}
	}

	fmt.Println("Program exited")
}

func startSpinner() *spinner.Spinner {
	s := spinner.New(spinner.CharSets[7], 100*time.Millisecond)
	s.Start()
	return s
}
<commit_msg>update instructions<commit_after>package main

import (
	"fmt"
	"github.com/briandowns/spinner"
	"github.com/davidnix/ffdraft/command"
	"github.com/davidnix/ffdraft/players"
	"log"
	"strings"
	"time"
)

const usage = `
--------------------------------------------------------------------------------------------------------------------
Commands:
    find, f [player name]: fuzzy finds players matching player name
    pick, p [player id]: removes player from draft pool
    unpick, u [player id]: adds player back to draft pool
    floor: print the highest floor value for available players for each position
    ceil: print the highest ceiling value for available players for each position
    help, h: print this usage text
    exit: exits this program
*By default, this program always prints the result of the floor command after every command.
--------------------------------------------------------------------------------------------------------------------
`

func main() {
	fmt.Println("Welcome to fantasy football!")
	fmt.Println(usage)
	fmt.Println("Fetching current player data...")

	s := startSpinner()
	//undrafted, err := players.Load()
	undrafted, err := players.LoadFromFile("./test/ff_response_fixture.json")
	s.Stop()
	if err != nil {
		log.Fatal("unable to fetch player data:", err)
	}
	repo := players.NewRepo(undrafted)
	fmt.Println("Loaded", len(repo.UnDrafted), "offensive players")
	command.Floor(repo)

Loop:
	for {
		input := strings.Fields(command.GetInput())
		var cmd string
		var args []string
		if len(input) > 0 {
			cmd = input[0]
			args = input[1:]
		}

		switch cmd {
		case "find", "f":
			command.Find(repo, args)

		case "pick", "p":
			fmt.Println("pick from a list of players")

		case "unpick", "u":
			fmt.Println("unpick from a list of players")

		case "floor", "fl":
			command.Floor(repo)

		case "ceil":
            command.Ceil(repo)

		case "help", "h", "usage":
			fmt.Println(usage)

		case "exit":
			break Loop

		case "":
			break

		default:
			fmt.Println("Unrecognized command \"" + cmd + "\". Type help for usage.")
		}
	}

	fmt.Println("Program exited")
}

func startSpinner() *spinner.Spinner {
	s := spinner.New(spinner.CharSets[7], 100*time.Millisecond)
	s.Start()
	return s
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net"
	"net/http"
	"os"
	"sync"
	"text/template"
	"time"

	"github.com/go-recaptcha/recaptcha"
	"github.com/gorilla/handlers"
	"github.com/nlopes/slack"
	"github.com/oxtoacart/bpool"
)

var captcha *recaptcha.Recaptcha
var api *slack.Client
var bufpool *bpool.BufferPool
var indexTemplate = template.Must(template.New("index.tmpl").ParseFiles("templates/index.tmpl"))

var captchaSitekey = flag.String("captchaSitekey", "REPLACEME", "reCaptcha Sitekey")
var captchaSecret = flag.String("captchaSecret", "REPLACEME", "reCaptcha Secret")
var slackToken = flag.String("slackToken", "REPLACEME", "Slack API Token")
var listenAddr = flag.String("listenAddr", ":8887", "Address to listen on")

// Slack statistics
var userCount int
var activeUserCount int
var statsMutex sync.RWMutex // Guards slack statistics variables

func init() {
	flag.Parse()
	// Check if we are missing vital config values
	if *captchaSitekey == "REPLACEME" || *captchaSecret == "REPLACEME" || *slackToken == "REPLACEME" {
		flag.PrintDefaults()
		return
	}
	// Init stuff
	captcha = recaptcha.New(*captchaSecret)
	api = slack.New(*slackToken)
	bufpool = bpool.NewBufferPool(64)
	go pollSlack()
}
func pollSlack() {
	for {
		users, err := api.GetUsers()
		if err != nil {
			log.Println("error polling slack for users:", err)
			continue
		}
		uCount := 0 // users
		aCount := 0 // active users
		for _, u := range users {
			if u.ID != "USLACKBOT" && !u.IsBot && !u.Deleted {
				uCount += 1
				if u.Presence == "active" {
					aCount += 1
				}
			}
		}
		statsMutex.Lock()
		userCount = uCount
		activeUserCount = aCount
		statsMutex.Unlock()
		time.Sleep(10 * time.Minute)
	}
}

// Homepage renders the homepage
func homepage(w http.ResponseWriter, r *http.Request) {
	statsMutex.RLock()
	data := map[string]interface{}{"SiteKey": *captchaSitekey, "UserCount": userCount, "ActiveCount": activeUserCount}
	statsMutex.RUnlock()
	buf := bufpool.Get()
	defer bufpool.Put(buf)
	err := indexTemplate.Execute(buf, data)
	if err != nil {
		log.Println("error rendering template:", err)
		http.Error(w, "error rendering template :-(", http.StatusInternalServerError)
		return
	}
	// Set the header and write the buffer to the http.ResponseWriter
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	buf.WriteTo(w)
}

// ShowPost renders a single post
func handleInvite(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}
	captchaResponse := r.FormValue("g-recaptcha-response")
	remoteIP, _, _ := net.SplitHostPort(r.RemoteAddr)

	valid, err := captcha.Verify(captchaResponse, remoteIP)
	if err != nil {
		http.Error(w, "Error validating recaptcha.. Did you click it?", http.StatusPreconditionFailed)
		return
	}
	if !valid {
		http.Error(w, "Invalid recaptcha", http.StatusInternalServerError)
		return

	}
	fname := r.FormValue("fname")
	lname := r.FormValue("lname")
	email := r.FormValue("email")
	coc := r.FormValue("coc")
	if fname == "" {
		http.Error(w, "Missing first name", http.StatusPreconditionFailed)
		return
	}
	if lname == "" {
		http.Error(w, "Lissing last name", http.StatusPreconditionFailed)
		return
	}
	if email == "" {
		http.Error(w, "Missing email", http.StatusPreconditionFailed)
		return
	}
	if coc != "1" {
		http.Error(w, "You need to accept the code of conduct", http.StatusPreconditionFailed)
		return
	}
	err = api.InviteToTeam("Gophers", fname, lname, email)
	if err != nil {
		log.Println("InviteToTeam error:", err)
		http.Error(w, "Error inviting you :-(", http.StatusInternalServerError)
		return
	}
}

func main() {
	log.SetPrefix("slackinvite:")
	mux := http.NewServeMux()
	mux.HandleFunc("/invite/", handleInvite)
	mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
	mux.HandleFunc("/", homepage)
	log.Println("listening on ", *listenAddr)
	err := http.ListenAndServe(*listenAddr, handlers.CombinedLoggingHandler(os.Stdout, mux))
	if err != nil {
		panic(err)
	}
}
<commit_msg>Correct typo in missing last name<commit_after>package main

import (
	"flag"
	"log"
	"net"
	"net/http"
	"os"
	"sync"
	"text/template"
	"time"

	"github.com/go-recaptcha/recaptcha"
	"github.com/gorilla/handlers"
	"github.com/nlopes/slack"
	"github.com/oxtoacart/bpool"
)

var captcha *recaptcha.Recaptcha
var api *slack.Client
var bufpool *bpool.BufferPool
var indexTemplate = template.Must(template.New("index.tmpl").ParseFiles("templates/index.tmpl"))

var captchaSitekey = flag.String("captchaSitekey", "REPLACEME", "reCaptcha Sitekey")
var captchaSecret = flag.String("captchaSecret", "REPLACEME", "reCaptcha Secret")
var slackToken = flag.String("slackToken", "REPLACEME", "Slack API Token")
var listenAddr = flag.String("listenAddr", ":8887", "Address to listen on")

// Slack statistics
var userCount int
var activeUserCount int
var statsMutex sync.RWMutex // Guards slack statistics variables

func init() {
	flag.Parse()
	// Check if we are missing vital config values
	if *captchaSitekey == "REPLACEME" || *captchaSecret == "REPLACEME" || *slackToken == "REPLACEME" {
		flag.PrintDefaults()
		return
	}
	// Init stuff
	captcha = recaptcha.New(*captchaSecret)
	api = slack.New(*slackToken)
	bufpool = bpool.NewBufferPool(64)
	go pollSlack()
}
func pollSlack() {
	for {
		users, err := api.GetUsers()
		if err != nil {
			log.Println("error polling slack for users:", err)
			continue
		}
		uCount := 0 // users
		aCount := 0 // active users
		for _, u := range users {
			if u.ID != "USLACKBOT" && !u.IsBot && !u.Deleted {
				uCount += 1
				if u.Presence == "active" {
					aCount += 1
				}
			}
		}
		statsMutex.Lock()
		userCount = uCount
		activeUserCount = aCount
		statsMutex.Unlock()
		time.Sleep(10 * time.Minute)
	}
}

// Homepage renders the homepage
func homepage(w http.ResponseWriter, r *http.Request) {
	statsMutex.RLock()
	data := map[string]interface{}{"SiteKey": *captchaSitekey, "UserCount": userCount, "ActiveCount": activeUserCount}
	statsMutex.RUnlock()
	buf := bufpool.Get()
	defer bufpool.Put(buf)
	err := indexTemplate.Execute(buf, data)
	if err != nil {
		log.Println("error rendering template:", err)
		http.Error(w, "error rendering template :-(", http.StatusInternalServerError)
		return
	}
	// Set the header and write the buffer to the http.ResponseWriter
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	buf.WriteTo(w)
}

// ShowPost renders a single post
func handleInvite(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}
	captchaResponse := r.FormValue("g-recaptcha-response")
	remoteIP, _, _ := net.SplitHostPort(r.RemoteAddr)

	valid, err := captcha.Verify(captchaResponse, remoteIP)
	if err != nil {
		http.Error(w, "Error validating recaptcha.. Did you click it?", http.StatusPreconditionFailed)
		return
	}
	if !valid {
		http.Error(w, "Invalid recaptcha", http.StatusInternalServerError)
		return

	}
	fname := r.FormValue("fname")
	lname := r.FormValue("lname")
	email := r.FormValue("email")
	coc := r.FormValue("coc")
	if fname == "" {
		http.Error(w, "Missing first name", http.StatusPreconditionFailed)
		return
	}
	if lname == "" {
		http.Error(w, "Missing last name", http.StatusPreconditionFailed)
		return
	}
	if email == "" {
		http.Error(w, "Missing email", http.StatusPreconditionFailed)
		return
	}
	if coc != "1" {
		http.Error(w, "You need to accept the code of conduct", http.StatusPreconditionFailed)
		return
	}
	err = api.InviteToTeam("Gophers", fname, lname, email)
	if err != nil {
		log.Println("InviteToTeam error:", err)
		http.Error(w, "Error inviting you :-(", http.StatusInternalServerError)
		return
	}
}

func main() {
	log.SetPrefix("slackinvite:")
	mux := http.NewServeMux()
	mux.HandleFunc("/invite/", handleInvite)
	mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
	mux.HandleFunc("/", homepage)
	log.Println("listening on ", *listenAddr)
	err := http.ListenAndServe(*listenAddr, handlers.CombinedLoggingHandler(os.Stdout, mux))
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"net/http"
	"net/http/fcgi"
	"encoding/json"
	"io/ioutil"
	"regexp"
	"strconv"
	"net/url"
	"log"
	"math"
	"flag"
)

type Counts struct {
	Url string `json:"url"`
	Count int64 `json:"count,string"`
}

var gplus_pat = regexp.MustCompile(`window\.__SSR\s=\s\{c:\s([0-9]+)\.0`)

func GetGPlus(pageUrl string) (int64, error) {
	safeUrl := url.QueryEscape(pageUrl)
	reqUrl := fmt.Sprintf("https://plusone.google.com/u/0/_/+1/fastbutton?url=%s&count=true", safeUrl)

	// log.Printf("calling G+ at %s", reqUrl)

	resp, err := http.Get(reqUrl)

	if err != nil {
		return 0, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return 0, err
	}

	var count int64 = 0
	if submatches := gplus_pat.FindSubmatch(body); len(submatches) >= 2 {
		// log.Printf("pattern %s found in G+ response for %s", gplus_pat, reqUrl)

		countAsBytes := submatches[1]
		countAsStr := string(countAsBytes[:])

		// log.Printf("count is %s", countAsStr)

		count, err = strconv.ParseInt(countAsStr, 10, 64)

		if err != nil {
			return 0, nil
		}
	} else {
		log.Printf("warning: pattern %s not found in G+ response for %s", gplus_pat, reqUrl)
	}

	return count, nil
}

func GetJson(urlFormat string, pageUrl string, skipFront int, skipEnd int) (map[string]interface{}, error) {
	safeUrl := url.QueryEscape(pageUrl)
	reqUrl := fmt.Sprintf(urlFormat, safeUrl)

	resp, err := http.Get(reqUrl)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	var ret interface{}

	err = json.Unmarshal(body[skipFront:len(body)-skipEnd], &ret)

	return ret.(map[string]interface{}), err
}

func GetStumbleUpon(pageUrl string) (int64, error) {
	ret, err := GetJson("https://www.stumbleupon.com/services/1.01/badge.getinfo?url=%s", pageUrl, 0, 0)

	if err != nil {
		return 0, err
	}

	if ret["result"] == nil {
		log.Printf("StumbleUpon: bad response: result is empty")
		return 0, errors.New("bad response")
	}

	result := ret["result"].(map[string]interface{})

	if result["views"] == nil {
		return 0, nil
	}

	count := result["views"].(float64)

	return int64(math.Floor(count)), nil

}

func GetPinterest(pageUrl string) (int64, error) {
	ret, err := GetJson("https://api.pinterest.com/v1/urls/count.json?url=%s&callback=a", pageUrl, 2, 1)

	if err != nil {
		return 0, err
	}

	if ret["count"] == nil {
		log.Printf("Pinterest: bad response: count is empty")
		return 0, errors.New("bad response")
	}

	count := ret["count"].(float64)

	return int64(math.Floor(count)), nil

}

func GetReddit(pageUrl string) (int64, error) {
	// ret, err := GetJson("http://www.reddit.com/api/info.json?url=%s", pageUrl, 0, 0)

	// if err != nil {
	// 	return 0, err
	// }

	// if ret["children"] == nil {
	// 	log.Printf("Pinterest: bad response: count is empty")
	// 	return 0, errors.New("bad response")
	// }

	// children := ret["children"].([]interface{})

	// if len(children) == 0 {
	// 	return 0, nil
	// }

	// return int64(math.Floor(count)), nil

	return 0, nil
}

var vk_pat = regexp.MustCompile(`VK\.Share\.count\(([0-9]+), ([0-9]+)\);`)

func GetVK(pageUrl string) (int64, error) {
	safeUrl := url.QueryEscape(pageUrl)
	reqUrl := fmt.Sprintf("https://vk.com/share.php?act=count&index=0&url=%s", safeUrl)

	resp, err := http.Get(reqUrl)

	if err != nil {
		return 0, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return 0, err
	}

	if submatches := vk_pat.FindSubmatch(body); len(submatches) >= 3 {
		// log.Printf("pattern %s found in VK response for %s", gplus_pat, reqUrl)

		countAsBytes := submatches[2]
		countAsStr := string(countAsBytes[:])

		// log.Printf("count is %s", countAsStr)

		return strconv.ParseInt(countAsStr, 10, 64)
	} else {
		log.Printf("warning: pattern %s not found in VK response for %s", vk_pat, reqUrl)
		return 0, errors.New("bad response")
	}

	return 0, errors.New("unreachable")
}

var local = flag.String("local", "", "serve as webserver, example: 0.0.0.0:8000")

func Handler(w http.ResponseWriter, r *http.Request) {
	header := w.Header()
	header.Set("Content-Type", "application/json; charset=utf-8")
	header.Set("Cache-Control", "max-age=300, public")
	header.Set("Pragma", "public")

	params := r.URL.Query()
	source := params.Get("type")
	pageUrl := params.Get("url")

	log.Printf("url %s type %s", pageUrl, source)

	counts := Counts{
		Url: pageUrl,
		Count: 0,
	}

	var count int64 = 0
	var err error = nil

	switch source {
	case "googlePlus":
		count, err = GetGPlus(pageUrl)
	case "stumbleupon":
		count, err = GetStumbleUpon(pageUrl)
	case "pinterest":
		count, err = GetPinterest(pageUrl)
	case "vkontakte":
		count, err = GetVK(pageUrl)
	}

	if err != nil {
		fmt.Fprintf(w, "ERROR %+v", err)
		w.WriteHeader(500)
	} else {
		counts.Count = count
	}

	bytes, err := json.Marshal(counts)

	log.Printf("output: %s", bytes)

	if err != nil {
		fmt.Fprintf(w, "ERROR %+v", err)
		w.WriteHeader(500)
	} else {
		w.Write(bytes)
	}
}

func main() {
	flag.Parse()

	handler := http.HandlerFunc(Handler)
	var err error

	if *local != "" { // Run as a local web server
        err = http.ListenAndServe(*local, handler)
    } else { // Run as FCGI via standard I/O
        err = fcgi.Serve(nil, handler)
    }

	if err != nil {
		fmt.Println(err)
	}
}
<commit_msg>Remove unnecessary logging<commit_after>package main

import (
	"errors"
	"fmt"
	"net/http"
	"net/http/fcgi"
	"encoding/json"
	"io/ioutil"
	"regexp"
	"strconv"
	"net/url"
	"log"
	"math"
	"flag"
)

type Counts struct {
	Url string `json:"url"`
	Count int64 `json:"count,string"`
}

var gplus_pat = regexp.MustCompile(`window\.__SSR\s=\s\{c:\s([0-9]+)\.0`)

func GetGPlus(pageUrl string) (int64, error) {
	safeUrl := url.QueryEscape(pageUrl)
	reqUrl := fmt.Sprintf("https://plusone.google.com/u/0/_/+1/fastbutton?url=%s&count=true", safeUrl)

	// log.Printf("calling G+ at %s", reqUrl)

	resp, err := http.Get(reqUrl)

	if err != nil {
		return 0, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return 0, err
	}

	var count int64 = 0
	if submatches := gplus_pat.FindSubmatch(body); len(submatches) >= 2 {
		// log.Printf("pattern %s found in G+ response for %s", gplus_pat, reqUrl)

		countAsBytes := submatches[1]
		countAsStr := string(countAsBytes[:])

		// log.Printf("count is %s", countAsStr)

		count, err = strconv.ParseInt(countAsStr, 10, 64)

		if err != nil {
			return 0, nil
		}
	} else {
		log.Printf("warning: pattern %s not found in G+ response for %s", gplus_pat, reqUrl)
	}

	return count, nil
}

func GetJson(urlFormat string, pageUrl string, skipFront int, skipEnd int) (map[string]interface{}, error) {
	safeUrl := url.QueryEscape(pageUrl)
	reqUrl := fmt.Sprintf(urlFormat, safeUrl)

	resp, err := http.Get(reqUrl)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	var ret interface{}

	err = json.Unmarshal(body[skipFront:len(body)-skipEnd], &ret)

	return ret.(map[string]interface{}), err
}

func GetStumbleUpon(pageUrl string) (int64, error) {
	ret, err := GetJson("https://www.stumbleupon.com/services/1.01/badge.getinfo?url=%s", pageUrl, 0, 0)

	if err != nil {
		return 0, err
	}

	if ret["result"] == nil {
		log.Printf("StumbleUpon: bad response: result is empty")
		return 0, errors.New("bad response")
	}

	result := ret["result"].(map[string]interface{})

	if result["views"] == nil {
		return 0, nil
	}

	count := result["views"].(float64)

	return int64(math.Floor(count)), nil

}

func GetPinterest(pageUrl string) (int64, error) {
	ret, err := GetJson("https://api.pinterest.com/v1/urls/count.json?url=%s&callback=a", pageUrl, 2, 1)

	if err != nil {
		return 0, err
	}

	if ret["count"] == nil {
		log.Printf("Pinterest: bad response: count is empty")
		return 0, errors.New("bad response")
	}

	count := ret["count"].(float64)

	return int64(math.Floor(count)), nil

}

func GetReddit(pageUrl string) (int64, error) {
	// ret, err := GetJson("http://www.reddit.com/api/info.json?url=%s", pageUrl, 0, 0)

	// if err != nil {
	// 	return 0, err
	// }

	// if ret["children"] == nil {
	// 	log.Printf("Pinterest: bad response: count is empty")
	// 	return 0, errors.New("bad response")
	// }

	// children := ret["children"].([]interface{})

	// if len(children) == 0 {
	// 	return 0, nil
	// }

	// return int64(math.Floor(count)), nil

	return 0, nil
}

var vk_pat = regexp.MustCompile(`VK\.Share\.count\(([0-9]+), ([0-9]+)\);`)

func GetVK(pageUrl string) (int64, error) {
	safeUrl := url.QueryEscape(pageUrl)
	reqUrl := fmt.Sprintf("https://vk.com/share.php?act=count&index=0&url=%s", safeUrl)

	resp, err := http.Get(reqUrl)

	if err != nil {
		return 0, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return 0, err
	}

	if submatches := vk_pat.FindSubmatch(body); len(submatches) >= 3 {
		// log.Printf("pattern %s found in VK response for %s", gplus_pat, reqUrl)

		countAsBytes := submatches[2]
		countAsStr := string(countAsBytes[:])

		// log.Printf("count is %s", countAsStr)

		return strconv.ParseInt(countAsStr, 10, 64)
	} else {
		log.Printf("warning: pattern %s not found in VK response for %s", vk_pat, reqUrl)
		return 0, errors.New("bad response")
	}

	return 0, errors.New("unreachable")
}

var local = flag.String("local", "", "serve as webserver, example: 0.0.0.0:8000")

func Handler(w http.ResponseWriter, r *http.Request) {
	header := w.Header()
	header.Set("Content-Type", "application/json; charset=utf-8")
	header.Set("Cache-Control", "max-age=300, public")
	header.Set("Pragma", "public")

	params := r.URL.Query()
	source := params.Get("type")
	pageUrl := params.Get("url")

	//log.Printf("url %s type %s", pageUrl, source)

	counts := Counts{
		Url: pageUrl,
		Count: 0,
	}

	var count int64 = 0
	var err error = nil

	switch source {
	case "googlePlus":
		count, err = GetGPlus(pageUrl)
	case "stumbleupon":
		count, err = GetStumbleUpon(pageUrl)
	case "pinterest":
		count, err = GetPinterest(pageUrl)
	case "vkontakte":
		count, err = GetVK(pageUrl)
	}

	if err != nil {
		fmt.Fprintf(w, "ERROR %+v", err)
		w.WriteHeader(500)
	} else {
		counts.Count = count
	}

	bytes, err := json.Marshal(counts)

	//log.Printf("output: %s", bytes)

	if err != nil {
		fmt.Fprintf(w, "ERROR %+v", err)
		w.WriteHeader(500)
	} else {
		w.Write(bytes)
	}
}

func main() {
	flag.Parse()

	handler := http.HandlerFunc(Handler)
	var err error

	if *local != "" { // Run as a local web server
        err = http.ListenAndServe(*local, handler)
    } else { // Run as FCGI via standard I/O
        err = fcgi.Serve(nil, handler)
    }

	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>