<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() {
	app.HelpFlag.Short('h')
	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{}
		paths, err := walkPaths(rightScriptUploadPaths)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
			os.Exit(1)
		}

		for _, path := range paths {
			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():
		paths, err := walkPaths(rightScriptScaffoldPaths)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
			os.Exit(1)
		}

		for _, path := range paths {
			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():
		paths, err := walkPaths(rightScriptValidatePaths)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
			os.Exit(1)
		}

		err_encountered := false

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

// Turn a mixed array of directories and files into a linear list of files
func walkPaths(paths *[]string) ([]string, error) {
	files := []string{}
	for _, path := range *paths {
		info, err := os.Stat(path)
		if err != nil {
			return files, err
		}
		if info.IsDir() {
			err = filepath.Walk(path, func(p string, f os.FileInfo, err error) error {
				files = append(files, p)
				_, e := os.Stat(p)
				return e
			})
			if err != nil {
				return files, err
			}
		} else {
			files = append(files, path)
		}
	}
	return files, nil

}

// 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>Improve debug handling and general cleanup<commit_after>package main

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

	"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/httpclient"
	"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() {
	app.HelpFlag.Short('h')
	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
	logLevel := log15.LvlInfo

	if *debug {
		log.Logger.SetHandler(
			log15.LvlFilterHandler(
				log15.LvlDebug,
				log15.StderrHandler))
		httpclient.DumpFormat = httpclient.Debug
		logLevel = log15.LvlDebug
	}
	handler := log15.LvlFilterHandler(logLevel, log15.StreamHandler(colorable.NewColorableStdout(), log15.TerminalFormat()))
	log15.Root().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("Listing %s:\n", *rightScriptListFilter)
		//log15.Info("Listing", "RightScript", *rightScriptListFilter)
		rightscripts, err := rightscriptLocator.Index(apiParams)
		if err != nil {
			fatalError("%s", err.Error())
		}
		for _, rs := range rightscripts {
			rev := "HEAD"
			if rs.Revision != 0 {
				rev = fmt.Sprintf("%d", rs.Revision)
			}
			fmt.Printf("/api/right_scripts/%s %5s %s\n", rs.Id, rev, rs.Name)
		}
	case rightScriptUpload.FullCommand():
		// Pass 1, perform validations, gather up results
		scripts := []RightScript{}
		paths, err := walkPaths(rightScriptUploadPaths)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
			os.Exit(1)
		}

		for _, p := range paths {
			fmt.Printf("Uploading %s\n", p)
			f, err := os.Open(p)
			if err != nil {
				fatalError("Cannot open %s", p)
			}
			defer f.Close()
			metadata, err := ParseRightScriptMetadata(f)

			if metadata.Name == "" {
				if !*rightScriptUploadForce {
					fatalError("No embedded metadata for %s. Use --force to upload anyways.", p)
				}
				scriptname := path.Base(p)
				scriptext := path.Ext(scriptname)
				scriptname = strings.TrimRight(scriptname, scriptext)
				metadata.Name = scriptname
			}

			script := RightScript{"", p, metadata}
			scripts = append(scripts, script)
		}

		// Pass 2, upload
		for _, script := range scripts {
			err = script.Push()
			if err != nil {
				fatalError("%s", err.Error())
			}
		}
	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, err := rightscriptLocator.Show()
		if err != nil {
			fatalError("Could not find rightscript with href %s: %s", href, err.Error())
		}
		source, err := getSource(rightscriptLocator)
		if err != nil {
			fatalError("Could get soruce for rightscript with href %s: %s", href, err.Error())
		}

		// attachments, err2 := attachmentsLocator.Index(rsapi.APIParams{})
		if *rightScriptDownloadTo == "" {
			*rightScriptDownloadTo = rightscript.Name
		}
		fmt.Printf("Downloading '%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():
		paths, err := walkPaths(rightScriptScaffoldPaths)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
			os.Exit(1)
		}

		for _, path := range paths {
			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():
		paths, err := walkPaths(rightScriptValidatePaths)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
			os.Exit(1)
		}

		err_encountered := false

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

// Turn a mixed array of directories and files into a linear list of files
func walkPaths(paths *[]string) ([]string, error) {
	files := []string{}
	for _, path := range *paths {
		info, err := os.Stat(path)
		if err != nil {
			return files, err
		}
		if info.IsDir() {
			err = filepath.Walk(path, func(p string, f os.FileInfo, err error) error {
				files = append(files, p)
				_, e := os.Stat(p)
				return e
			})
			if err != nil {
				return files, err
			}
		} else {
			files = append(files, path)
		}
	}
	return files, nil

}

// 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))
		_, err = rightscriptLocator.Create(&params)
	} else {
		fmt.Printf("Updating existing RightScript named '%s' from %s\n", r.Metadata.Name, r.Path)

		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)
		// Found existing, do an update
	}
	return err
}

func fatalError(format string, v ...interface{}) {
	msg := fmt.Sprintf("ERROR: "+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 (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"

	"gopkg.in/mgo.v2"
)

const (
	metricsAPIPath = "/api/metrics"
	agentsAPIPath  = "/api/agents"
)

var (
	ca *mgo.Collection
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("marksman: ")

	var (
		addr = flag.String("addr", ":8080", "serve HTTP on `address`")
	)

	flag.Parse()

	http.HandleFunc(metricsAPIPath, metricsHandler)
	http.HandleFunc(agentsAPIPath, agentsHandler)
	http.Handle("/", http.FileServer(http.Dir("./public")))

	session, err := mgo.Dial("localhost")
	if err != nil {
		panic(err)
	}
	defer session.Close()

	// Optional. Switch the session to a monotonic behavior.
	session.SetMode(mgo.Monotonic, true)
	ca = session.DB("recon-dev").C("agents")

	log.Println("Server started: http://localhost" + *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

func metricsHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "TODO")
}

func agentsHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		var R []Agent
		err := ca.Find(nil).All(&R)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		enc := json.NewEncoder(w)
		if err := enc.Encode(R); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		return
	case "POST":
		var a Agent
		dec := json.NewDecoder(r.Body)
		if err := dec.Decode(&a); err != nil {
			http.Error(w, "unable to decode json", http.StatusBadRequest)
			return
		}
		if a.UID == "" {
			http.Error(w, "UID can't be empty", http.StatusBadRequest)
			return
		}
		err := ca.Insert(a)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Write([]byte(a.UID))
		return
	default:
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}
}

// Agent represents a recon agent running on
// a machine.
type Agent struct {
	UID string
}
<commit_msg>add content type header to the responses<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"

	"gopkg.in/mgo.v2"
)

const (
	metricsAPIPath = "/api/metrics"
	agentsAPIPath  = "/api/agents"
)

var (
	ca *mgo.Collection
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("marksman: ")

	var (
		addr = flag.String("addr", ":8080", "serve HTTP on `address`")
	)

	flag.Parse()

	http.HandleFunc(metricsAPIPath, metricsHandler)
	http.HandleFunc(agentsAPIPath, agentsHandler)
	http.Handle("/", http.FileServer(http.Dir("./public")))

	session, err := mgo.Dial("localhost")
	if err != nil {
		panic(err)
	}
	defer session.Close()

	// Optional. Switch the session to a monotonic behavior.
	session.SetMode(mgo.Monotonic, true)
	ca = session.DB("recon-dev").C("agents")

	log.Println("Server started: http://localhost" + *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

func metricsHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, "TODO")
}

func agentsHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	switch r.Method {
	case "GET":
		var R []Agent
		err := ca.Find(nil).All(&R)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		enc := json.NewEncoder(w)
		if err := enc.Encode(R); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		return
	case "POST":
		var a Agent
		dec := json.NewDecoder(r.Body)
		if err := dec.Decode(&a); err != nil {
			http.Error(w, "unable to decode json", http.StatusBadRequest)
			return
		}
		if a.UID == "" {
			http.Error(w, "UID can't be empty", http.StatusBadRequest)
			return
		}
		err := ca.Insert(a)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Write([]byte(a.UID))
		return
	default:
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}
}

// Agent represents a recon agent running on
// a machine.
type Agent struct {
	UID string
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"io"
	"log"
	"net/http"

	"github.com/dchest/uniuri"
	"github.com/ewhal/pygments"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
)

const (
	ADDRESS  = "http://localhost:9900"
	LENGTH   = 6
	PORT     = ":9900"
	USERNAME = ""
	PASS     = ""
	NAME     = ""
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

type Response struct {
	ID     string `json:"id"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

func check(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func generateName() string {
	s := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	query, err := db.Query("select id from pastebin")
	for query.Next() {
		var id string
		err := query.Scan(&id)
		if err != nil {

		}
		if id == s {
			generateName()
		}
	}
	db.Close()

	return s

}
func hash(paste string) string {
	hasher := sha1.New()

	hasher.Write([]byte(paste))
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

func save(raw string, lang string) []string {
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	sha := hash(raw)
	query, err := db.Query("select id, hash, data, delkey from pastebin")
	for query.Next() {
		var id, hash, paste, delkey string
		err := query.Scan(&id, &hash, &paste, &delkey)
		check(err)
		if hash == sha {
			url := ADDRESS + "/p/" + id
			return []string{id, hash, url, paste, delkey}
		}
	}
	id := generateName()
	var url string
	if lang == "" {
		url = ADDRESS + "/p/" + id
	} else {
		url = ADDRESS + "/p/" + id + "/" + lang
	}
	delKey := uniuri.NewLen(40)
	paste := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, hash, data, delkey) values(?,?,?,?)")
	check(err)
	_, err = stmt.Exec(id, sha, paste, delKey)
	check(err)
	db.Close()
	return []string{id, sha, url, paste, delKey}
}

func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	check(err)

	stmt, err := db.Prepare("delete from pastebin where delkey=?")
	check(err)

	res, err := stmt.Exec(html.EscapeString(delkey))
	check(err)

	affect, err := res.RowsAffected()
	check(err)

	io.WriteString(w, string(affect))

	db.Close()

}
func saveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		paste := r.FormValue("p")
		lang := r.FormValue("lang")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
		}
		values := save(paste, lang)
		b := &Response{
			ID:     values[0],
			HASH:   values[1],
			URL:    values[2],
			SIZE:   len(values[3]),
			DELKEY: values[4],
		}

		switch output {
		case "json":

			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)
		case "html":
			w.Header().Set("Content-Type", "text/html")
			io.WriteString(w, "
URL: "+b.URL+"
")
			io.WriteString(w, "
Delete Key: "+b.DELKEY+"
")

		default:
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

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

	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s := getPaste(paste)
	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "full, style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,", "utf-8")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, highlight)

}

func getPaste(paste string) string {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	var s string
	err = db.QueryRow("select data from pastebin where id=?", param1).Scan(&s)
	db.Close()
	check(err)

	if err == sql.ErrNoRows {
		return "Error invalid paste"
	} else {
		return html.UnescapeString(s)
	}

}
func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	io.WriteString(w, s)

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/p/{pasteId}", pasteHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", langHandler)
	router.HandleFunc("/save", saveHandler)
	router.HandleFunc("/save/{output}", saveHandler)
	router.HandleFunc("/del/{pasteId}/{delKey}", delHandler)
	router.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>fixup deletion handler more<commit_after>package main

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"io"
	"log"
	"net/http"

	"github.com/dchest/uniuri"
	"github.com/ewhal/pygments"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
)

const (
	ADDRESS  = "http://localhost:9900"
	LENGTH   = 6
	PORT     = ":9900"
	USERNAME = ""
	PASS     = ""
	NAME     = ""
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

type Response struct {
	ID     string `json:"id"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

func check(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func generateName() string {
	s := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	query, err := db.Query("select id from pastebin")
	for query.Next() {
		var id string
		err := query.Scan(&id)
		if err != nil {

		}
		if id == s {
			generateName()
		}
	}
	db.Close()

	return s

}
func hash(paste string) string {
	hasher := sha1.New()

	hasher.Write([]byte(paste))
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

func save(raw string, lang string) []string {
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	sha := hash(raw)
	query, err := db.Query("select id, hash, data, delkey from pastebin")
	for query.Next() {
		var id, hash, paste, delkey string
		err := query.Scan(&id, &hash, &paste, &delkey)
		check(err)
		if hash == sha {
			url := ADDRESS + "/p/" + id
			return []string{id, hash, url, paste, delkey}
		}
	}
	id := generateName()
	var url string
	if lang == "" {
		url = ADDRESS + "/p/" + id
	} else {
		url = ADDRESS + "/p/" + id + "/" + lang
	}
	delKey := uniuri.NewLen(40)
	paste := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, hash, data, delkey) values(?,?,?,?)")
	check(err)
	_, err = stmt.Exec(id, sha, paste, delKey)
	check(err)
	db.Close()
	return []string{id, sha, url, paste, delKey}
}

func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	check(err)

	stmt, err := db.Prepare("delete from pastebin where delkey=?")
	check(err)

	res, err := stmt.Exec(html.EscapeString(delkey))
	check(err)

	affect, err := res.RowsAffected()
	if err == sql.ErrNoRows {
		io.WriteString(w, "Error invalid paste")
	} else {
		io.WriteString(w, paste+" deleted")
	}
	db.Close()

}
func saveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		paste := r.FormValue("p")
		lang := r.FormValue("lang")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
		}
		values := save(paste, lang)
		b := &Response{
			ID:     values[0],
			HASH:   values[1],
			URL:    values[2],
			SIZE:   len(values[3]),
			DELKEY: values[4],
		}

		switch output {
		case "json":

			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)
		case "html":
			w.Header().Set("Content-Type", "text/html")
			io.WriteString(w, "
URL: "+b.URL+"
")
			io.WriteString(w, "
Delete Key: "+b.DELKEY+"
")

		default:
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

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

	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s := getPaste(paste)
	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "full, style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,", "utf-8")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, highlight)

}

func getPaste(paste string) string {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	var s string
	err = db.QueryRow("select data from pastebin where id=?", param1).Scan(&s)
	db.Close()
	check(err)

	if err == sql.ErrNoRows {
		return "Error invalid paste"
	} else {
		return html.UnescapeString(s)
	}

}
func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s := getPaste(paste)
	io.WriteString(w, s)

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/p/{pasteId}", pasteHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", langHandler)
	router.HandleFunc("/save", saveHandler)
	router.HandleFunc("/save/{output}", saveHandler)
	router.HandleFunc("/del/{pasteId}/{delKey}", delHandler)
	router.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package miniprofiler

import (
	"fmt"
	"io"
	"math"
	"sort"
	"strings"
	"time"
)

var (
	mp        *MiniProfiler
	enabled   bool
	condition func() bool
)

type MiniProfilerData struct {
	description string
	steps       map[string]int64
	lastStep    time.Time
	memos       []string
}

type MiniProfiler struct {
	profiles []*MiniProfilerData
}

func init() {
	mp = new(MiniProfiler)
	mp.profiles = make([]*MiniProfilerData, 0)
	enabled = true
	condition = func() bool { return true }
}

func Enable() {
	enabled = true
}

func Disable() {
	enabled = false
}

func SetCondition(c func() bool) {
	condition = c
}

func Begin(description string) *MiniProfilerData {
	if !enabled {
		return nil
	}
	if !condition() {
		return nil
	}
	return &MiniProfilerData{description, make(map[string]int64, 0), time.Now(), make([]string, 0)}
}

func Dump(writer io.Writer) {
	for _, prof := range mp.profiles {
		outputs := []string{"log:MP"}

		for tag, val := range prof.steps {
			outputs = append(outputs, fmt.Sprintf("%s:%d", tag, val))
		}

		outputs = append(outputs, fmt.Sprintf("description:%s", prof.description))

		memoOutput := []string{}
		for _, m := range prof.memos {
			memoOutput = append(memoOutput, m)
		}
		outputs = append(outputs, fmt.Sprintf("memo:%s", strings.Join(memoOutput, ",")))

		fmt.Fprintln(writer, strings.Join(outputs, "\t"))
	}
}

func Flush() {
	mp.profiles = make([]*MiniProfilerData, 0)
}

type Measure struct {
	Description string
	Count       int
	Total       int
	Mean        int
	Min         int
	Max         int
}

type By func(a, b *Measure) bool

func (by By) Sort(measures []*Measure) {
	ms := &measureSorter{
		measures: measures,
		by:       by,
	}
	sort.Sort(ms)
}

type measureSorter struct {
	measures []*Measure
	by       func(a, b *Measure) bool
}

func (s *measureSorter) Len() int {
	return len(s.measures)
}

func (s *measureSorter) Swap(i, j int) {
	s.measures[i], s.measures[j] = s.measures[j], s.measures[i]
}

func (s *measureSorter) Less(i, j int) bool {
	return s.by(s.measures[i], s.measures[j])
}

type Column struct {
	Name    string
	Summary string
	Sort    By
}

var (
	columns = []*Column{
		&Column{Name: "Count", Summary: "Count", Sort: func(a, b *Measure) bool { return a.Count > b.Count }},
		&Column{Name: "Total", Summary: "Total", Sort: func(a, b *Measure) bool { return a.Total > b.Total }},
		&Column{Name: "Mean", Summary: "Mean", Sort: func(a, b *Measure) bool { return a.Mean > b.Mean }},
		&Column{Name: "Min"},
		&Column{Name: "Max", Summary: "Maximum(100 Percentile)", Sort: func(a, b *Measure) bool { return a.Max > b.Max }},
	}
)

func getIntegerDigitWidth(i int) int {
	var w int
	switch {
	case i < 1:
		w = 1
	default:
		w = int(math.Log10(float64(i)) + 1)
	}
	return w
}

func showMeasures(writer io.Writer, measures []*Measure) {
	countWidth := 5
	totalWidth := 5
	meanWidth := 5
	maxWidth := 5

	for _, m := range measures {
		var w int
		w = getIntegerDigitWidth(m.Count)
		if countWidth < w {
			countWidth = w
		}
		w = getIntegerDigitWidth(m.Total)
		if totalWidth < w {
			totalWidth = w
		}
		w = getIntegerDigitWidth(m.Mean)
		if meanWidth < w {
			meanWidth = w
		}
		w = getIntegerDigitWidth(m.Max)
		if maxWidth < w {
			maxWidth = w
		}
	}

	var format string
	for _, column := range columns {
		switch column.Name {
		case "Count":
			fmt.Fprintf(writer, fmt.Sprintf("%%%ds  ", countWidth), column.Name)
			format += fmt.Sprintf("%%%dd  ", countWidth)
		case "Total":
			fmt.Fprintf(writer, fmt.Sprintf("%%%ds  ", totalWidth), column.Name)
			format += fmt.Sprintf("%%%dd  ", totalWidth)
		case "Mean":
			fmt.Fprintf(writer, fmt.Sprintf("%%%ds  ", meanWidth), column.Name)
			format += fmt.Sprintf("%%%dd  ", meanWidth)
		default:
			fmt.Fprintf(writer, fmt.Sprintf("%%%ds  ", maxWidth), column.Name)
			format += fmt.Sprintf("%%%dd  ", maxWidth)
		}
	}
	fmt.Fprintln(writer, "Description")
	format += "%s\n"

	for _, m := range measures {
		fmt.Fprintf(writer, format, m.Count, m.Total, m.Mean, m.Min, m.Max, m.Description)
	}
}

func Analyze(writer io.Writer) {
	var times = make(map[string][]int)
	var totals = make(map[string]int)
	for _, prof := range mp.profiles {
		for tag, val := range prof.steps {
			description := fmt.Sprintf("%s/%s", prof.description, tag)
			times[description] = append(times[description], int(val))
			totals[description] += int(val)
		}
	}
	var measures []*Measure
	for description, times := range times {
		sort.Ints(times)
		count := len(times)
		total := totals[description]
		m := &Measure{
			Description: description,
			Count:       count,
			Total:       total,
			Mean:        total / count,
			Min:         times[0],
			Max:         times[count-1],
		}
		measures = append(measures, m)
	}

	for _, column := range columns {
		if column.Sort != nil {
			fmt.Fprintf(writer, "Sort by %s\n", column.Summary)
			By(column.Sort).Sort(measures)
			showMeasures(writer, measures)
			fmt.Fprintln(writer)
		}
	}
}

func (mpd *MiniProfilerData) Step(tag string) {
	if !enabled {
		return
	}
	if mpd == nil {
		return
	}
	now := time.Now()
	thisstep := now.Sub(mpd.lastStep).Nanoseconds()

	mpd.steps[tag] = thisstep
	mpd.lastStep = now
}

func (mpd *MiniProfilerData) Memo(memo string) {
	if !enabled {
		return
	}
	if mpd == nil {
		return
	}
	memo = memoFilter(memo)
	mpd.memos = append(mpd.memos, memo)
}

/*
remove (,|\t|\r|\n)
*/
func memoFilter(memo string) string {
	memo = strings.Replace(memo, ",", "", -1)
	memo = strings.Replace(memo, "\t", "", -1)
	memo = strings.Replace(memo, "\r", "", -1)
	memo = strings.Replace(memo, "\n", "", -1)
	return memo
}

func (mpd *MiniProfilerData) End() {
	if !enabled {
		return
	}
	if mpd == nil {
		return
	}
	mpd.Step("Last Step to End")
	mp.profiles = append(mp.profiles, mpd)
}
<commit_msg>add Standard Deviation<commit_after>package miniprofiler

import (
	"fmt"
	"io"
	"math"
	"sort"
	"strings"
	"time"
)

var (
	mp        *MiniProfiler
	enabled   bool
	condition func() bool
)

type MiniProfilerData struct {
	description string
	steps       map[string]int64
	lastStep    time.Time
	memos       []string
}

type MiniProfiler struct {
	profiles []*MiniProfilerData
}

func init() {
	mp = new(MiniProfiler)
	mp.profiles = make([]*MiniProfilerData, 0)
	enabled = true
	condition = func() bool { return true }
}

func Enable() {
	enabled = true
}

func Disable() {
	enabled = false
}

func SetCondition(c func() bool) {
	condition = c
}

func Begin(description string) *MiniProfilerData {
	if !enabled {
		return nil
	}
	if !condition() {
		return nil
	}
	return &MiniProfilerData{description, make(map[string]int64, 0), time.Now(), make([]string, 0)}
}

func Dump(writer io.Writer) {
	for _, prof := range mp.profiles {
		outputs := []string{"log:MP"}

		for tag, val := range prof.steps {
			outputs = append(outputs, fmt.Sprintf("%s:%d", tag, val))
		}

		outputs = append(outputs, fmt.Sprintf("description:%s", prof.description))

		memoOutput := []string{}
		for _, m := range prof.memos {
			memoOutput = append(memoOutput, m)
		}
		outputs = append(outputs, fmt.Sprintf("memo:%s", strings.Join(memoOutput, ",")))

		fmt.Fprintln(writer, strings.Join(outputs, "\t"))
	}
}

func Flush() {
	mp.profiles = make([]*MiniProfilerData, 0)
}

type Measure struct {
	Description string
	Count       int
	Total       int
	Mean        int
	Stddev      float64
	Min         int
	Max         int
}

type By func(a, b *Measure) bool

func (by By) Sort(measures []*Measure) {
	ms := &measureSorter{
		measures: measures,
		by:       by,
	}
	sort.Sort(ms)
}

type measureSorter struct {
	measures []*Measure
	by       func(a, b *Measure) bool
}

func (s *measureSorter) Len() int {
	return len(s.measures)
}

func (s *measureSorter) Swap(i, j int) {
	s.measures[i], s.measures[j] = s.measures[j], s.measures[i]
}

func (s *measureSorter) Less(i, j int) bool {
	return s.by(s.measures[i], s.measures[j])
}

type Column struct {
	Name    string
	Summary string
	Sort    By
}

var (
	columns = []*Column{
		&Column{Name: "Count", Summary: "Count", Sort: func(a, b *Measure) bool { return a.Count > b.Count }},
		&Column{Name: "Total", Summary: "Total", Sort: func(a, b *Measure) bool { return a.Total > b.Total }},
		&Column{Name: "Mean", Summary: "Mean", Sort: func(a, b *Measure) bool { return a.Mean > b.Mean }},
		&Column{Name: "Stddev", Summary: "Standard Deviation", Sort: func(a, b *Measure) bool { return a.Stddev > b.Stddev }},
		&Column{Name: "Min"},
		&Column{Name: "Max", Summary: "Maximum(100 Percentile)", Sort: func(a, b *Measure) bool { return a.Max > b.Max }},
	}
)

func getIntegerDigitWidth(i int) int {
	var w int
	switch {
	case i < 1:
		w = 1
	default:
		w = int(math.Log10(float64(i)) + 1)
	}
	return w
}

func showMeasures(writer io.Writer, measures []*Measure) {
	countWidth := 5
	totalWidth := 5
	meanWidth := 4
	stddevWidth := 6
	maxWidth := 3

	for _, m := range measures {
		var w int
		w = getIntegerDigitWidth(m.Count)
		if countWidth < w {
			countWidth = w
		}
		w = getIntegerDigitWidth(m.Total)
		if totalWidth < w {
			totalWidth = w
		}
		w = getIntegerDigitWidth(int(m.Mean))
		if meanWidth < w {
			meanWidth = w
		}
		w = getIntegerDigitWidth(int(m.Stddev)) + 4
		if stddevWidth < w {
			stddevWidth = w
		}
		w = getIntegerDigitWidth(m.Max)
		if maxWidth < w {
			maxWidth = w
		}
	}

	var format string
	for _, column := range columns {
		switch column.Name {
		case "Count":
			fmt.Fprintf(writer, fmt.Sprintf("%%%ds  ", countWidth), column.Name)
			format += fmt.Sprintf("%%%dd  ", countWidth)
		case "Total":
			fmt.Fprintf(writer, fmt.Sprintf("%%%ds  ", totalWidth), column.Name)
			format += fmt.Sprintf("%%%dd  ", totalWidth)
		case "Mean":
			fmt.Fprintf(writer, fmt.Sprintf("%%%ds  ", meanWidth), column.Name)
			format += fmt.Sprintf("%%%dd  ", meanWidth)
		case "Stddev":
			fmt.Fprintf(writer, fmt.Sprintf("%%%ds  ", stddevWidth), column.Name)
			format += fmt.Sprintf("%%%d.3f  ", stddevWidth)
		default:
			fmt.Fprintf(writer, fmt.Sprintf("%%%ds  ", maxWidth), column.Name)
			format += fmt.Sprintf("%%%dd  ", maxWidth)
		}
	}
	fmt.Fprintln(writer, "Description")
	format += "%s\n"

	for _, m := range measures {
		fmt.Fprintf(writer, format, m.Count, m.Total, m.Mean, m.Stddev, m.Min, m.Max, m.Description)
	}
}

func Analyze(writer io.Writer) {
	var times = make(map[string][]int)
	var totals = make(map[string]int)
	for _, prof := range mp.profiles {
		for tag, val := range prof.steps {
			description := fmt.Sprintf("%s/%s", prof.description, tag)
			times[description] = append(times[description], int(val))
			totals[description] += int(val)
		}
	}
	var measures []*Measure
	for description, times := range times {
		sort.Ints(times)
		count := len(times)
		total := totals[description]
		m := &Measure{
			Description: description,
			Count:       count,
			Total:       total,
			Mean:        total / count,
			Min:         times[0],
			Max:         times[count-1],
		}
		var sum int
		for _, t := range times {
			sum += (t - m.Mean) * (t - m.Mean)
		}
		m.Stddev = math.Sqrt(float64(sum) / float64(count))
		measures = append(measures, m)
	}

	for _, column := range columns {
		if column.Sort != nil {
			fmt.Fprintf(writer, "Sort by %s\n", column.Summary)
			By(column.Sort).Sort(measures)
			showMeasures(writer, measures)
			fmt.Fprintln(writer)
		}
	}
}

func (mpd *MiniProfilerData) Step(tag string) {
	if !enabled {
		return
	}
	if mpd == nil {
		return
	}
	now := time.Now()
	thisstep := now.Sub(mpd.lastStep).Nanoseconds()

	mpd.steps[tag] = thisstep
	mpd.lastStep = now
}

func (mpd *MiniProfilerData) Memo(memo string) {
	if !enabled {
		return
	}
	if mpd == nil {
		return
	}
	memo = memoFilter(memo)
	mpd.memos = append(mpd.memos, memo)
}

/*
remove (,|\t|\r|\n)
*/
func memoFilter(memo string) string {
	memo = strings.Replace(memo, ",", "", -1)
	memo = strings.Replace(memo, "\t", "", -1)
	memo = strings.Replace(memo, "\r", "", -1)
	memo = strings.Replace(memo, "\n", "", -1)
	return memo
}

func (mpd *MiniProfilerData) End() {
	if !enabled {
		return
	}
	if mpd == nil {
		return
	}
	mpd.Step("Last Step to End")
	mp.profiles = append(mp.profiles, mpd)
}
<|endoftext|>


<commit_before>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// 基于 Git 的博客系统。
package main

import (
	"flag"
	"fmt"
	"runtime"

	"github.com/caixw/typing/app"
	"github.com/caixw/typing/vars"
	"github.com/issue9/logs"
)

func main() {
	help := flag.Bool("h", false, "显示当前信息")
	version := flag.Bool("v", false, "显示程序的版本信息")
	pprof := flag.Bool("pprof", false, "是否在 /debug/pprof/ 启用调试功能")
	appdir := flag.String("appdir", "./", "指定运行的工作目录")
	init := flag.String("init", "", "初始化一个工作目录")
	flag.Usage = usage
	flag.Parse()

	switch {
	case *help:
		flag.Usage()
		return
	case *version:
		printVersion()
		return
	case len(*init) > 0:
		runInit(*init) // *init 指向的目录不存在时，会尝试创建
		return
	}

	path := vars.NewPath(*appdir)

	if err := logs.InitFromXMLFile(path.LogsConfigFile); err != nil {
		panic(err)
	}

	logs.Critical(app.Run(path, *pprof))
	logs.Flush()
}

func usage() {
	fmt.Printf("%s 是一个基于 Git 的博客系统。\n", vars.Name)
	fmt.Printf("源代码以 MIT 开源许可发布于：%s\n", vars.URL)

	fmt.Println("\n参数：")
	flag.PrintDefaults()
}

func printVersion() {
	fmt.Printf("%s %s build with %s\n", vars.Name, vars.Version(), runtime.Version())

	if len(vars.CommitHash()) > 0 {
		fmt.Printf("Git commit hash:%s\n", vars.CommitHash())
	}
}

func runInit(root string) {
	if err := app.Init(vars.NewPath(root)); err != nil {
		panic(err)
	}

	fmt.Printf("操作成功，你现在可以在 %s 中修改具体的参数配置！\n", root)
}
<commit_msg>将项目介绍文本放到一个常量中<commit_after>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// 基于 Git 的博客系统。
package main

import (
	"flag"
	"fmt"
	"runtime"

	"github.com/caixw/typing/app"
	"github.com/caixw/typing/vars"
	"github.com/issue9/logs"
)

const usage = `%s 是一个基于 Git 的博客系统。
源代码以 MIT 开源许可发布于：%s

常见用法：

typing -pprof -appdir="./"
typing -appdir="./"

参数：

`

func main() {
	help := flag.Bool("h", false, "显示当前信息")
	version := flag.Bool("v", false, "显示程序的版本信息")
	pprof := flag.Bool("pprof", false, "是否在 /debug/pprof/ 启用调试功能")
	appdir := flag.String("appdir", "./", "指定运行的工作目录")
	init := flag.String("init", "", "初始化一个工作目录")
	flag.Usage = func() {
		fmt.Printf(usage, vars.Name, vars.URL)
		flag.PrintDefaults()
	}
	flag.Parse()

	switch {
	case *help:
		flag.Usage()
		return
	case *version:
		printVersion()
		return
	case len(*init) > 0:
		runInit(*init) // *init 指向的目录不存在时，会尝试创建
		return
	}

	path := vars.NewPath(*appdir)

	if err := logs.InitFromXMLFile(path.LogsConfigFile); err != nil {
		panic(err)
	}

	logs.Critical(app.Run(path, *pprof))
	logs.Flush()
}

func printVersion() {
	fmt.Printf("%s %s build with %s\n", vars.Name, vars.Version(), runtime.Version())

	if len(vars.CommitHash()) > 0 {
		fmt.Printf("Git commit hash:%s\n", vars.CommitHash())
	}
}

func runInit(root string) {
	if err := app.Init(vars.NewPath(root)); err != nil {
		panic(err)
	}

	fmt.Printf("操作成功，你现在可以在 %s 中修改具体的参数配置！\n", root)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"log"
	"os"
	"fmt"
	"database/sql"
	_ "github.com/lib/pq"
)

var conn sql.DB

func GetBlog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func GetSpinData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func GetServoData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func PostBlog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func PostSpinData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func PostServoData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func main() {
	conn,_ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
	router := httprouter.New()
	router.GET("/blog", GetBlog)
	router.GET("/spin", GetSpinData)
	router.GET("/servo", GetServoData)
	router.POST("/blog", PostBlog)
	router.POST("/spin", PostSpinData)
	router.POST("/servo", PostServoData)
	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}
	fmt.Printf("Starting server at localhost:%s...", port)
	log.Fatal(http.ListenAndServe(":"+port, router))
}<commit_msg>global database var fixed<commit_after>package main

import (
	"encoding/json"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"log"
	"os"
	"fmt"
	"database/sql"
	_ "github.com/lib/pq"
)

var conn *sql.DB

func GetBlog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func GetSpinData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func GetServoData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func PostBlog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func PostSpinData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func PostServoData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	data,_ := json.Marshal("blah")
	w.Write(data)
}

func main() {
	conn,_ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
	router := httprouter.New()
	router.GET("/blog", GetBlog)
	router.GET("/spin", GetSpinData)
	router.GET("/servo", GetServoData)
	router.POST("/blog", PostBlog)
	router.POST("/spin", PostSpinData)
	router.POST("/servo", PostServoData)
	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}
	fmt.Printf("Starting server at localhost:%s...", port)
	log.Fatal(http.ListenAndServe(":"+port, router))
}<|endoftext|>


<commit_before>// RTLAMR - An rtl-sdr receiver for smart meters operating in the 900MHz ISM band.
// Copyright (C) 2015 Douglas Hall
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/pkg/errors"

	"github.com/bemasher/rtlamr/protocol"
	"github.com/bemasher/rtltcp"

	_ "github.com/bemasher/rtlamr/idm"
	_ "github.com/bemasher/rtlamr/netidm"
	_ "github.com/bemasher/rtlamr/r900"
	_ "github.com/bemasher/rtlamr/r900bcd"
	_ "github.com/bemasher/rtlamr/scm"
	_ "github.com/bemasher/rtlamr/scmplus"
)

var rcvr Receiver

type Receiver struct {
	rtltcp.SDR
	d  protocol.Decoder
	fc protocol.FilterChain

	ctx    context.Context
	cancel context.CancelFunc
	wg     *sync.WaitGroup

	err error
}

func (rcvr *Receiver) NewReceiver() {
	rcvr.ctx, rcvr.cancel = context.WithCancel(context.Background())
	rcvr.wg = &sync.WaitGroup{}

	rcvr.d = protocol.NewDecoder()

	// If the msgtype "all" is given alone, register and use scm, scm+, idm and r900.
	if _, all := msgType["all"]; all && len(msgType) == 1 {
		delete(msgType, "all")
		msgType["scm"] = true
		msgType["scm+"] = true
		msgType["idm"] = true
		msgType["r900"] = true
	}

	// For each given msgType, register it with the decoder.
	for name := range msgType {
		p, err := protocol.NewParser(name, *symbolLength)
		if err != nil {
			log.Fatal(err)
		}

		rcvr.d.RegisterProtocol(p)
	}

	// Allocate the internal buffers of the decoder.
	rcvr.d.Allocate()

	// Connect to rtl_tcp server.
	if rcvr.err = rcvr.Connect(nil); rcvr.err != nil {
		log.Fatalf("%+v", errors.Wrap(rcvr.err, "rcvr.Connect"))
	}

	cfg := rcvr.d.Cfg

	gainFlagSet := false
	flag.Visit(func(f *flag.Flag) {
		switch f.Name {
		case "centerfreq":
			cfg.CenterFreq = uint32(rcvr.Flags.CenterFreq)
		case "samplerate":
			cfg.SampleRate = int(rcvr.Flags.SampleRate)
		case "gainbyindex", "tunergainmode", "tunergain", "agcmode":
			gainFlagSet = true
		case "unique":
			rcvr.fc.Add(NewUniqueFilter())
		case "filterid":
			rcvr.fc.Add(meterID)
		case "filtertype":
			rcvr.fc.Add(meterType)
		}
	})

	rcvr.SetCenterFreq(cfg.CenterFreq)
	rcvr.SetSampleRate(uint32(cfg.SampleRate))

	if !gainFlagSet {
		rcvr.SetGainMode(true)
	}

	rcvr.d.Cfg = cfg
	rcvr.d.Log()

	// Tell the user how many gain settings were reported by rtl_tcp.
	log.Println("GainCount:", rcvr.SDR.Info.GainCount)

	return
}

func (rcvr *Receiver) Close() {
	rcvr.cancel()
	rcvr.wg.Wait()
	rcvr.SDR.Close()
}

func (rcvr *Receiver) Run() {
	rcvr.wg.Add(2)

	sampleBuf := new(bytes.Buffer)

	// Allocate a channel of blocks.
	blockCh := make(chan []byte)

	// Make maps for tracking messages spanning sample blocks.
	prev := map[protocol.Digest]bool{}
	next := map[protocol.Digest]bool{}

	// Read and send sample blocks to the decoder.
	go func() {
		defer close(blockCh)
		defer rcvr.wg.Done()

		// Make two sample blocks, one for reading, and one for the receiver to
		// decode, these are exchanged each time we read a new block.
		blockA := make([]byte, rcvr.d.Cfg.BlockSize2)
		blockB := make([]byte, rcvr.d.Cfg.BlockSize2)

		for {
			select {
			// Exit if we've been told to stop.
			case <-rcvr.ctx.Done():
				return
			default:
				rcvr.err = rcvr.SetDeadline(time.Now().Add(5 * time.Second))
				if rcvr.err != nil {
					rcvr.err = errors.Wrap(rcvr.err, "rcvr.SetDeadline")
					return
				}

				// Read new sample block.
				_, rcvr.err = io.ReadFull(rcvr, blockA)
				if rcvr.err != nil {
					rcvr.err = errors.Wrap(rcvr.err, "io.ReadFull")
					return
				}

				// Send the sample block.
				blockCh <- blockA

				// Exchange blocks for next read.
				blockA, blockB = blockB, blockA
			}
		}
	}()

	go func() {
		defer rcvr.wg.Done()
		for {
			select {
			case <-rcvr.ctx.Done():
				// Consume any in-flight blocks.
				for range blockCh {
				}
				return
			case block, ok := <-blockCh:
				if !ok {
					continue
				}

				// Clear next map for this sample block.
				for key := range next {
					delete(next, key)
				}

				// If dumping samples, discard the oldest block from the buffer if
				// it's full and write the new block to it.
				if *sampleFilename != os.DevNull {
					if sampleBuf.Len() > rcvr.d.Cfg.BufferLength<<1 {
						io.CopyN(ioutil.Discard, sampleBuf, int64(len(block)))
					}
					sampleBuf.Write(block)
				}

				pktFound := false

				// For each message returned
				for msg := range rcvr.d.Decode(block) {
					// If the filterchain rejects the message, skip it.
					if !rcvr.fc.Match(msg) {
						continue
					}

					// Make a new LogMessage
					var logMsg protocol.LogMessage
					logMsg.Time = time.Now()
					logMsg.Offset, _ = sampleFile.Seek(0, os.SEEK_CUR)
					logMsg.Length = sampleBuf.Len()
					logMsg.Type = msg.MsgType()
					logMsg.Message = msg

					// This should be unique enough to identify a message between blocks.
					msgDigest := protocol.NewDigest(msg)

					// Mark the message as seen for the next loop.
					next[msgDigest] = true

					// If the message was seen in the previous loop, skip it.
					if prev[msgDigest] {
						continue
					}

					// Encode the message
					rcvr.err = encoder.Encode(logMsg)
					rcvr.err = errors.Wrap(rcvr.err, "encoder.Encode")

					if rcvr.err != nil {
						return
					}

					pktFound = true
					if *single {
						if len(meterID.UintMap) == 0 {
							break
						} else {
							delete(meterID.UintMap, uint(msg.MeterID()))
						}
					}
				}

				if pktFound {
					if *sampleFilename != os.DevNull {
						_, err := sampleFile.Write(sampleBuf.Bytes())
						if err != nil {
							log.Fatal("Error writing raw samples to file:", err)
						}
					}
					if *single && len(meterID.UintMap) == 0 {
						return
					}
				}

				// Swap next and previous digest maps.
				next, prev = prev, next
			}
		}
	}()
}

func init() {
	log.SetFlags(log.Lshortfile | log.Lmicroseconds)
}

var (
	buildTag   = "dev"     // v#.#.#
	buildDate  = "unknown" // date -u '+%Y-%m-%d'
	commitHash = "unknown" // git rev-parse HEAD
)

func main() {
	rcvr.RegisterFlags()
	RegisterFlags()
	EnvOverride()
	flag.Parse()
	rcvr.HandleFlags()

	if *version {
		fmt.Println("Build Tag: ", buildTag)
		fmt.Println("Build Date:", buildDate)
		fmt.Println("Commit:    ", commitHash)
		os.Exit(0)
	}

	HandleFlags()

	rcvr.NewReceiver()

	defer func() {
		sampleFile.Close()
		rcvr.Close()

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

	start := time.Now()
	rcvr.Run()

	// Setup signal channel for interruption.
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)

	// Setup time limit channel
	timeLimitCh := make(<-chan time.Time, 1)
	if *timeLimit != 0 {
		timeLimitCh = time.After(*timeLimit)
	}

	select {
	case sig := <-sigCh:
		log.Println("Received Signal:", sig)
	case <-timeLimitCh:
		log.Println("Time Limit Reached:", time.Since(start))
	}

	rcvr.Close()
}
<commit_msg>Fixes what 60f79de broke.<commit_after>// RTLAMR - An rtl-sdr receiver for smart meters operating in the 900MHz ISM band.
// Copyright (C) 2015 Douglas Hall
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/pkg/errors"

	"github.com/bemasher/rtlamr/protocol"
	"github.com/bemasher/rtltcp"

	_ "github.com/bemasher/rtlamr/idm"
	_ "github.com/bemasher/rtlamr/netidm"
	_ "github.com/bemasher/rtlamr/r900"
	_ "github.com/bemasher/rtlamr/r900bcd"
	_ "github.com/bemasher/rtlamr/scm"
	_ "github.com/bemasher/rtlamr/scmplus"
)

var rcvr Receiver

type Receiver struct {
	rtltcp.SDR
	d  protocol.Decoder
	fc protocol.FilterChain

	ctx    context.Context
	cancel context.CancelFunc
	wg     *sync.WaitGroup

	err error
}

func (rcvr *Receiver) NewReceiver() {
	rcvr.ctx, rcvr.cancel = context.WithCancel(context.Background())
	rcvr.wg = &sync.WaitGroup{}

	rcvr.d = protocol.NewDecoder()

	// If the msgtype "all" is given alone, register and use scm, scm+, idm and r900.
	if _, all := msgType["all"]; all && len(msgType) == 1 {
		delete(msgType, "all")
		msgType["scm"] = true
		msgType["scm+"] = true
		msgType["idm"] = true
		msgType["r900"] = true
	}

	// For each given msgType, register it with the decoder.
	for name := range msgType {
		p, err := protocol.NewParser(name, *symbolLength)
		if err != nil {
			log.Fatal(err)
		}

		rcvr.d.RegisterProtocol(p)
	}

	// Allocate the internal buffers of the decoder.
	rcvr.d.Allocate()

	// Connect to rtl_tcp server.
	if rcvr.err = rcvr.Connect(nil); rcvr.err != nil {
		log.Fatalf("%+v", errors.Wrap(rcvr.err, "rcvr.Connect"))
	}

	cfg := rcvr.d.Cfg

	gainFlagSet := false
	flag.Visit(func(f *flag.Flag) {
		switch f.Name {
		case "centerfreq":
			cfg.CenterFreq = uint32(rcvr.Flags.CenterFreq)
		case "samplerate":
			cfg.SampleRate = int(rcvr.Flags.SampleRate)
		case "gainbyindex", "tunergainmode", "tunergain", "agcmode":
			gainFlagSet = true
		case "unique":
			rcvr.fc.Add(NewUniqueFilter())
		case "filterid":
			rcvr.fc.Add(meterID)
		case "filtertype":
			rcvr.fc.Add(meterType)
		}
	})

	rcvr.SetCenterFreq(cfg.CenterFreq)
	rcvr.SetSampleRate(uint32(cfg.SampleRate))

	if !gainFlagSet {
		rcvr.SetGainMode(true)
	}

	rcvr.d.Cfg = cfg
	rcvr.d.Log()

	// Tell the user how many gain settings were reported by rtl_tcp.
	log.Println("GainCount:", rcvr.SDR.Info.GainCount)

	return
}

func (rcvr *Receiver) Close() {
	rcvr.cancel()
	rcvr.wg.Wait()
	rcvr.SDR.Close()
}

func (rcvr *Receiver) Run() {
	rcvr.wg.Add(3)

	sampleBuf := new(bytes.Buffer)

	// Allocate a channel of blocks.
	blockCh := make(chan []byte)

	// Make maps for tracking messages spanning sample blocks.
	prev := map[protocol.Digest]bool{}
	next := map[protocol.Digest]bool{}

	go func() {
		defer rcvr.wg.Done()
		<-rcvr.ctx.Done()
		// Consume any in-flight blocks.
		for range blockCh {
		}
	}()

	// Read and send sample blocks to the decoder.
	go func() {
		defer rcvr.cancel()
		defer close(blockCh)
		defer rcvr.wg.Done()

		// Make two sample blocks, one for reading, and one for the receiver to
		// decode, these are exchanged each time we read a new block.
		blockA := make([]byte, rcvr.d.Cfg.BlockSize2)
		blockB := make([]byte, rcvr.d.Cfg.BlockSize2)

		for {
			select {
			// Exit if we've been told to stop.
			case <-rcvr.ctx.Done():
				return
			default:
				rcvr.err = rcvr.SetDeadline(time.Now().Add(5 * time.Second))
				if rcvr.err != nil {
					rcvr.err = errors.Wrap(rcvr.err, "rcvr.SetDeadline")
					return
				}

				// Read new sample block.
				_, rcvr.err = io.ReadFull(rcvr, blockA)
				if rcvr.err != nil {
					rcvr.err = errors.Wrap(rcvr.err, "io.ReadFull")
					return
				}

				// Send the sample block.
				blockCh <- blockA

				// Exchange blocks for next read.
				blockA, blockB = blockB, blockA
			}
		}
	}()

	go func() {
		defer rcvr.cancel()
		defer rcvr.wg.Done()

		for {
			select {
			case <-rcvr.ctx.Done():
				return
			case block, ok := <-blockCh:
				if !ok {
					continue
				}

				// Clear next map for this sample block.
				for key := range next {
					delete(next, key)
				}

				// If dumping samples, discard the oldest block from the buffer if
				// it's full and write the new block to it.
				if *sampleFilename != os.DevNull {
					if sampleBuf.Len() > rcvr.d.Cfg.BufferLength<<1 {
						io.CopyN(ioutil.Discard, sampleBuf, int64(len(block)))
					}
					sampleBuf.Write(block)
				}

				pktFound := false

				// For each message returned
				for msg := range rcvr.d.Decode(block) {
					// If the filterchain rejects the message, skip it.
					if !rcvr.fc.Match(msg) {
						continue
					}

					// Make a new LogMessage
					var logMsg protocol.LogMessage
					logMsg.Time = time.Now()
					logMsg.Offset, _ = sampleFile.Seek(0, os.SEEK_CUR)
					logMsg.Length = sampleBuf.Len()
					logMsg.Type = msg.MsgType()
					logMsg.Message = msg

					// This should be unique enough to identify a message between blocks.
					msgDigest := protocol.NewDigest(msg)

					// Mark the message as seen for the next loop.
					next[msgDigest] = true

					// If the message was seen in the previous loop, skip it.
					if prev[msgDigest] {
						continue
					}

					// Encode the message
					rcvr.err = encoder.Encode(logMsg)
					rcvr.err = errors.Wrap(rcvr.err, "encoder.Encode")

					if rcvr.err != nil {
						return
					}

					pktFound = true
					if *single {
						if len(meterID.UintMap) == 0 {
							break
						} else {
							delete(meterID.UintMap, uint(msg.MeterID()))
						}
					}
				}

				if pktFound {
					if *sampleFilename != os.DevNull {
						_, err := sampleFile.Write(sampleBuf.Bytes())
						if err != nil {
							log.Fatal("Error writing raw samples to file:", err)
						}
					}
					if *single && len(meterID.UintMap) == 0 {
						rcvr.cancel()
						return
					}
				}

				// Swap next and previous digest maps.
				next, prev = prev, next
			}
		}
	}()
}

func init() {
	log.SetFlags(log.Lshortfile | log.Lmicroseconds)
}

var (
	buildTag   = "dev"     // v#.#.#
	buildDate  = "unknown" // date -u '+%Y-%m-%d'
	commitHash = "unknown" // git rev-parse HEAD
)

func main() {
	rcvr.RegisterFlags()
	RegisterFlags()
	EnvOverride()
	flag.Parse()
	rcvr.HandleFlags()

	if *version {
		fmt.Println("Build Tag: ", buildTag)
		fmt.Println("Build Date:", buildDate)
		fmt.Println("Commit:    ", commitHash)
		os.Exit(0)
	}

	HandleFlags()

	rcvr.NewReceiver()

	defer func() {
		sampleFile.Close()
		rcvr.Close()

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

	start := time.Now()
	rcvr.Run()

	// Setup signal channel for interruption.
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)

	// Setup time limit channel
	timeLimitCh := make(<-chan time.Time, 1)
	if *timeLimit != 0 {
		timeLimitCh = time.After(*timeLimit)
	}

	select {
	case sig := <-sigCh:
		log.Println("Received Signal:", sig)
	case <-timeLimitCh:
		log.Println("Time Limit Reached:", time.Since(start))
	case <-rcvr.ctx.Done():
		log.Println("Receiver context cancelled.")
	}

	rcvr.Close()
}
<|endoftext|>


<commit_before>// Package file is a Go library to open files with file locking depending on the system.
//
// Currently file locking on the following systems are supported.
//
/*
  darwin dragonfly freebsd linux netbsd openbsd:
          Advisory Lock

  windows:
          Mandatory Lock

  android nacl plan9 solaris zos:
          Not Supported
*/
package file
<commit_msg>Update comment.<commit_after>// Package file is a Go library to open files with file locking depending on the system.
//
// Currently file locking on the following systems are supported.
//
/*
  darwin dragonfly freebsd linux netbsd openbsd solaris:
          Advisory Lock

  windows:
          Mandatory Lock

  android nacl plan9 zos:
          Not Supported
*/
package file
<|endoftext|>


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

package main

import (
	"log"
	"net/http"
	"os"

	"github.com/line/line-bot-sdk-go/linebot"
)

/*
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 09a61a620751c49e8b67d3244f5280b4b309e1ae
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
var bot *linebot.Client
var eggyoID = "ufa92a3a52f197e19bfddeb5ca0595e93"
var logNof = "open"

type GeoContent struct {
	LatLong string `json:"latLon"`
	Utm     string `json:"utm"`
	Mgrs    string `json:"mgrs"`
}

type ResultGeoLoc struct {
	Results GeoContent `json:"result"`
}

func getGeoLoc(body []byte) (*ResultGeoLoc, error) {
	var s = new(ResultGeoLoc)
	err := json.Unmarshal(body, &s)
	if err != nil {
		fmt.Println("whoops:", err)
	}
	return s, err
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
}*/

func main() {

	bot, err := linebot.New(
		os.Getenv("ChannelSecret"),
		os.Getenv("ChannelToken"),
	)
	if err != nil {
		log.Fatal(err)
	}
	// Setup HTTP Server for receiving requests from LINE platform
	http.HandleFunc("/callback", func(w http.ResponseWriter, req *http.Request) {
		events, err := bot.ParseRequest(req)
		if err != nil {
			if err == linebot.ErrInvalidSignature {
				w.WriteHeader(400)
			} else {
				w.WriteHeader(500)
			}
			return
		}
		for _, event := range events {
			if event.Type == linebot.EventTypeMessage {
				switch message := event.Message.(type) {
				case *linebot.TextMessage:
					if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(message.Text)).Do(); err != nil {
						log.Print(err)
					}

				case *linebot.ImageMessage:
					log.Print(message)

					content, err := bot.GetMessageContent(message.ID).Do()
					if err != nil {
						log.Print(err)
					}
					defer content.Content.Close()
					log.Printf(content.Content)

				}

			}

		}
	})
	// This is just sample code.
	// For actual use, you must support HTTPS by using `ListenAndServeTLS`, a reverse proxy or something else.
	if err := http.ListenAndServe(":"+os.Getenv("PORT"), nil); err != nil {
		log.Fatal(err)
	}
}

/*
<<<<<<< HEAD
<<<<<<< HEAD
=======
}

func main() {
	getAllUser()
	// line bot
	strID := os.Getenv("ChannelID")
	numID, err := strconv.ParseInt(strID, 10, 64)
	if err != nil {
		log.Fatal("Wrong environment setting about ChannelID")
	}

	bot, err = linebot.NewClient(numID, os.Getenv("ChannelSecret"), os.Getenv("MID"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
	//test

}
>>>>>>> 09a61a620751c49e8b67d3244f5280b4b309e1ae
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
func callbackHandler(w http.ResponseWriter, r *http.Request) {

	received, err := bot.ParseRequest(r)
	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}
	for _, result := range received.Results {
		content := result.Content()
		log.Println("-->", content)

		//Log detail receive content
		if content != nil {
			log.Println("RECEIVE Msg:", content.IsMessage, " OP:", content.IsOperation, " type:", content.ContentType, " from:", content.From, "to:", content.To, " ID:", content.ID)
		}
		// user add friend
		if content != nil && content.IsOperation && content.OpType == linebot.OpTypeAddedAsFriend {
			out := fmt.Sprintf("Bot แปลงพิกัด Eggyo\nวิธีใช้\nเพียงแค่กดแชร์ Location ที่ต้องการ ระบบจะทำการแปลง Location เป็นพิกัดระบบต่างๆ และหาความสูงจากระดับน้ำทะเลให้\n\nหรือจะพูดคุยกับ bot ก็ได้\nกด #help เพื่อดูวิธีใช้อื่นๆ \nติดต่อผู้พัฒนา LINE ID : eggyo")
			//result.RawContent.Params[0] is who send your bot friend added operation, otherwise you cannot get in content or operation content.
			_, err = bot.SendText([]string{content.From}, out)
			if logNof == "open" {
				bot.SendText([]string{eggyoID}, "bot has a new friend :"+content.From)
			}

			addNewUser(content.From)

			if err != nil {
				log.Println(err)
			}
		}

		if content != nil && content.IsMessage && content.ContentType == linebot.ContentTypeText {

			text, err := content.TextContent()
			if logNof == "open" {
				bot.SendText([]string{eggyoID}, "bot get msg:"+text.Text+"\nfrom :"+content.From)
			}
			// reply message
			var processedText = messageCheck(text.Text)
			_, err = bot.SendText([]string{content.From}, processedText)

			if err != nil {
				log.Println(err)
			}
		}
		if content != nil && content.ContentType == linebot.ContentTypeLocation {
			_, err = bot.SendText([]string{content.From}, "ระบบกำลังประมวลผล...")

			loc, err := content.LocationContent()

<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
			// add eggyo geo test//
			resp, err := http.Get("http://eggyo-geo-node.herokuapp.com/geo/" + FloatToString(loc.Latitude) + "," + FloatToString(loc.Longitude))
=======
			// add eggyo geo test
			resp, err := http.Get("http://eggyo-geo-node.herokuapp.com/geo/" + FloatToString(loc.Latitude) + "/" + FloatToString(loc.Longitude))
>>>>>>> 09a61a620751c49e8b67d3244f5280b4b309e1ae
=======
			// add eggyo geo test//
			resp, err := http.Get("http://eggyo-geo-node.herokuapp.com/geo/" + FloatToString(loc.Latitude) + "," + FloatToString(loc.Longitude))
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
=======
			// add eggyo geo test//
			resp, err := http.Get("http://eggyo-geo-node.herokuapp.com/geo/" + FloatToString(loc.Latitude) + "," + FloatToString(loc.Longitude))
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
			if err != nil {
				println(err.Error())
				return
			}
			defer resp.Body.Close()
			body, err := ioutil.ReadAll(resp.Body)
			log.Println(string(body))

			var elev = callGoogleElev(loc.Latitude, loc.Longitude)
			geo, err := getGeoLoc([]byte(body))
			_, err = bot.SendText([]string{content.From}, "LatLong :"+geo.Results.LatLong)
			_, err = bot.SendText([]string{content.From}, "Utm :"+geo.Results.Utm+"\n\nMgrs :"+geo.Results.Mgrs+"\n\nAltitude :"+elev)
			if logNof == "open" {
				bot.SendText([]string{eggyoID}, "bot get loc:"+geo.Results.Mgrs+"\nfrom :"+content.From)
			}
			if err != nil {
				log.Println(err)
			}
		}
	}
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
*/
<commit_msg>v.5.1.2<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"log"
	"net/http"
	"os"

	"github.com/line/line-bot-sdk-go/linebot"
)

/*
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 09a61a620751c49e8b67d3244f5280b4b309e1ae
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
var bot *linebot.Client
var eggyoID = "ufa92a3a52f197e19bfddeb5ca0595e93"
var logNof = "open"

type GeoContent struct {
	LatLong string `json:"latLon"`
	Utm     string `json:"utm"`
	Mgrs    string `json:"mgrs"`
}

type ResultGeoLoc struct {
	Results GeoContent `json:"result"`
}

func getGeoLoc(body []byte) (*ResultGeoLoc, error) {
	var s = new(ResultGeoLoc)
	err := json.Unmarshal(body, &s)
	if err != nil {
		fmt.Println("whoops:", err)
	}
	return s, err
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
}*/

func main() {

	bot, err := linebot.New(
		os.Getenv("ChannelSecret"),
		os.Getenv("ChannelToken"),
	)
	if err != nil {
		log.Fatal(err)
	}
	// Setup HTTP Server for receiving requests from LINE platform
	http.HandleFunc("/callback", func(w http.ResponseWriter, req *http.Request) {
		events, err := bot.ParseRequest(req)
		if err != nil {
			if err == linebot.ErrInvalidSignature {
				w.WriteHeader(400)
			} else {
				w.WriteHeader(500)
			}
			return
		}
		for _, event := range events {
			if event.Type == linebot.EventTypeMessage {
				switch message := event.Message.(type) {
				case *linebot.TextMessage:
					if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(message.Text)).Do(); err != nil {
						log.Print(err)
					}

				case *linebot.ImageMessage:
					log.Print(message)

					content, err := bot.GetMessageContent(message.ID).Do()
					if err != nil {
						log.Print(err)
					}
					defer content.Content.Close()
					log.Printf(message.ID)

				}

			}

		}
	})
	// This is just sample code.
	// For actual use, you must support HTTPS by using `ListenAndServeTLS`, a reverse proxy or something else.
	if err := http.ListenAndServe(":"+os.Getenv("PORT"), nil); err != nil {
		log.Fatal(err)
	}
}

/*
<<<<<<< HEAD
<<<<<<< HEAD
=======
}

func main() {
	getAllUser()
	// line bot
	strID := os.Getenv("ChannelID")
	numID, err := strconv.ParseInt(strID, 10, 64)
	if err != nil {
		log.Fatal("Wrong environment setting about ChannelID")
	}

	bot, err = linebot.NewClient(numID, os.Getenv("ChannelSecret"), os.Getenv("MID"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
	//test

}
>>>>>>> 09a61a620751c49e8b67d3244f5280b4b309e1ae
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
func callbackHandler(w http.ResponseWriter, r *http.Request) {

	received, err := bot.ParseRequest(r)
	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}
	for _, result := range received.Results {
		content := result.Content()
		log.Println("-->", content)

		//Log detail receive content
		if content != nil {
			log.Println("RECEIVE Msg:", content.IsMessage, " OP:", content.IsOperation, " type:", content.ContentType, " from:", content.From, "to:", content.To, " ID:", content.ID)
		}
		// user add friend
		if content != nil && content.IsOperation && content.OpType == linebot.OpTypeAddedAsFriend {
			out := fmt.Sprintf("Bot แปลงพิกัด Eggyo\nวิธีใช้\nเพียงแค่กดแชร์ Location ที่ต้องการ ระบบจะทำการแปลง Location เป็นพิกัดระบบต่างๆ และหาความสูงจากระดับน้ำทะเลให้\n\nหรือจะพูดคุยกับ bot ก็ได้\nกด #help เพื่อดูวิธีใช้อื่นๆ \nติดต่อผู้พัฒนา LINE ID : eggyo")
			//result.RawContent.Params[0] is who send your bot friend added operation, otherwise you cannot get in content or operation content.
			_, err = bot.SendText([]string{content.From}, out)
			if logNof == "open" {
				bot.SendText([]string{eggyoID}, "bot has a new friend :"+content.From)
			}

			addNewUser(content.From)

			if err != nil {
				log.Println(err)
			}
		}

		if content != nil && content.IsMessage && content.ContentType == linebot.ContentTypeText {

			text, err := content.TextContent()
			if logNof == "open" {
				bot.SendText([]string{eggyoID}, "bot get msg:"+text.Text+"\nfrom :"+content.From)
			}
			// reply message
			var processedText = messageCheck(text.Text)
			_, err = bot.SendText([]string{content.From}, processedText)

			if err != nil {
				log.Println(err)
			}
		}
		if content != nil && content.ContentType == linebot.ContentTypeLocation {
			_, err = bot.SendText([]string{content.From}, "ระบบกำลังประมวลผล...")

			loc, err := content.LocationContent()

<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
			// add eggyo geo test//
			resp, err := http.Get("http://eggyo-geo-node.herokuapp.com/geo/" + FloatToString(loc.Latitude) + "," + FloatToString(loc.Longitude))
=======
			// add eggyo geo test
			resp, err := http.Get("http://eggyo-geo-node.herokuapp.com/geo/" + FloatToString(loc.Latitude) + "/" + FloatToString(loc.Longitude))
>>>>>>> 09a61a620751c49e8b67d3244f5280b4b309e1ae
=======
			// add eggyo geo test//
			resp, err := http.Get("http://eggyo-geo-node.herokuapp.com/geo/" + FloatToString(loc.Latitude) + "," + FloatToString(loc.Longitude))
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
=======
			// add eggyo geo test//
			resp, err := http.Get("http://eggyo-geo-node.herokuapp.com/geo/" + FloatToString(loc.Latitude) + "," + FloatToString(loc.Longitude))
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
			if err != nil {
				println(err.Error())
				return
			}
			defer resp.Body.Close()
			body, err := ioutil.ReadAll(resp.Body)
			log.Println(string(body))

			var elev = callGoogleElev(loc.Latitude, loc.Longitude)
			geo, err := getGeoLoc([]byte(body))
			_, err = bot.SendText([]string{content.From}, "LatLong :"+geo.Results.LatLong)
			_, err = bot.SendText([]string{content.From}, "Utm :"+geo.Results.Utm+"\n\nMgrs :"+geo.Results.Mgrs+"\n\nAltitude :"+elev)
			if logNof == "open" {
				bot.SendText([]string{eggyoID}, "bot get loc:"+geo.Results.Mgrs+"\nfrom :"+content.From)
			}
			if err != nil {
				log.Println(err)
			}
		}
	}
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
=======
>>>>>>> 66cf34abf6b2ed7caa5c018b73c50380be01e401
*/
<|endoftext|>


<commit_before>package main

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

	"github.com/fsouza/go-dockerclient"
)

var (
	dockerDir = flag.String("d", "/var/lib/docker", "Specify docker directory")
	endPoint  = flag.String("h", "unix:///var/run/docker.sock", "Specify a docker endpoint")
	dryrun    = flag.Bool("n", true, "Dry-run. Do not delete volumes by default. Use -n=false to delete volumes actually.")
)

func JoinDockerDir(dirs ...string) string {
	ps := string(os.PathSeparator)
	return *dockerDir + ps + strings.Join(dirs, ps)
}

func DeleteVolume(volumeId string) {
	var err error
	err = DoDelete(JoinDockerDir("volumes", volumeId))
	err = DoDelete(JoinDockerDir("vfs", "dir", volumeId))
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s: failed to delete volume id:%s", err, volumeId)
	}
}

func ListOndiskVolumes(volumesDir string) (map[string]bool, error) {
	dirs, err := ioutil.ReadDir(volumesDir)
	if err != nil {
		return nil, err
	}

	ondiskVolumes := map[string]bool{}
	for _, dir := range dirs {
		if dir.IsDir() && len(dir.Name()) == 64 {
			ondiskVolumes[dir.Name()] = true
		}
	}
	return ondiskVolumes, nil
}

func ListOnContainerVolumes(client *docker.Client) (map[string]bool, error) {
	containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
	if err != nil {
		return nil, err
	}

	onContainerVolumes := map[string]bool{}
	for _, container := range containers {
		ci, err := client.InspectContainer(container.ID)
		if err != nil {
			return nil, err
		}
		for _, volumeDir := range ci.Volumes {
			onContainerVolumes[filepath.Base(volumeDir)] = true
		}
	}
	return onContainerVolumes, nil
}

func DoDelete(path string) error {
	if *dryrun {
		fmt.Println("dryrun:", path)
		return nil
	}
	fmt.Println("remove:", path)
	return os.RemoveAll(path)
}

func main() {
	flag.Parse()

	ondiskVolumes, err := ListOndiskVolumes(JoinDockerDir("volumes"))
	if err != nil {
		log.Fatal(err)
	}

	client, err := docker.NewClient(*endPoint)
	if err != nil {
		log.Fatal(err)
	}
	onContainerVolumes, err := ListOnContainerVolumes(client)
	if err != nil {
		log.Fatal(err)
	}

	for n := range onContainerVolumes {
		if ondiskVolumes[n] {
			delete(ondiskVolumes, n)
		}
	}

	for volume := range ondiskVolumes {
		DeleteVolume(volume)
	}
}
<commit_msg>Use filepath.Join<commit_after>package main

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

	"github.com/fsouza/go-dockerclient"
)

var (
	dockerDir = flag.String("d", "/var/lib/docker", "Specify docker directory")
	endPoint  = flag.String("h", "unix:///var/run/docker.sock", "Specify a docker endpoint")
	dryrun    = flag.Bool("n", true, "Dry-run. Do not delete volumes by default. Use -n=false to delete volumes actually.")
)

func JoinDockerDir(dirs ...string) string {
	return filepath.Join(*dockerDir, filepath.Join(dirs...))
}

func DeleteVolume(volumeId string) {
	var err error
	err = DoDelete(JoinDockerDir("volumes", volumeId))
	err = DoDelete(JoinDockerDir("vfs", "dir", volumeId))
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s: failed to delete volume id:%s", err, volumeId)
	}
}

func ListOndiskVolumes(volumesDir string) (map[string]bool, error) {
	dirs, err := ioutil.ReadDir(volumesDir)
	if err != nil {
		return nil, err
	}

	ondiskVolumes := map[string]bool{}
	for _, dir := range dirs {
		if dir.IsDir() && len(dir.Name()) == 64 {
			ondiskVolumes[dir.Name()] = true
		}
	}
	return ondiskVolumes, nil
}

func ListOnContainerVolumes(client *docker.Client) (map[string]bool, error) {
	containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
	if err != nil {
		return nil, err
	}

	onContainerVolumes := map[string]bool{}
	for _, container := range containers {
		ci, err := client.InspectContainer(container.ID)
		if err != nil {
			return nil, err
		}
		for _, volumeDir := range ci.Volumes {
			onContainerVolumes[filepath.Base(volumeDir)] = true
		}
	}
	return onContainerVolumes, nil
}

func DoDelete(path string) error {
	if *dryrun {
		fmt.Println("dryrun:", path)
		return nil
	}
	fmt.Println("remove:", path)
	return os.RemoveAll(path)
}

func main() {
	flag.Parse()

	ondiskVolumes, err := ListOndiskVolumes(JoinDockerDir("volumes"))
	if err != nil {
		log.Fatal(err)
	}

	client, err := docker.NewClient(*endPoint)
	if err != nil {
		log.Fatal(err)
	}
	onContainerVolumes, err := ListOnContainerVolumes(client)
	if err != nil {
		log.Fatal(err)
	}

	for n := range onContainerVolumes {
		if ondiskVolumes[n] {
			delete(ondiskVolumes, n)
		}
	}

	for volume := range ondiskVolumes {
		DeleteVolume(volume)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/dokterbob/ipfs-search/crawler"
	"github.com/dokterbob/ipfs-search/indexer"
	"github.com/dokterbob/ipfs-search/queue"
	"gopkg.in/ipfs/go-ipfs-api.v1"
	"gopkg.in/olivere/elastic.v3"
	"gopkg.in/urfave/cli.v1"
	"log"
	"os"
)

const (
	IPFS_API     = "localhost:5001"
	HASH_WORKERS = 10
	FILE_WORKERS = 10
)

func main() {
	app := cli.NewApp()
	app.Name = "ipfs-search"
	app.Usage = "IPFS search engine."

	app.Commands = []cli.Command{
		{
			Name:    "add",
			Aliases: []string{"a"},
			Usage:   "add `HASH` to crawler queue",
			Action:  add,
		},
		{
			Name:    "crawl",
			Aliases: []string{"c"},
			Usage:   "start crawler",
			Action:  crawl,
		},
	}

	app.Run(os.Args)
}

func get_elastic() (*elastic.Client, error) {
	el, err := elastic.NewClient()
	if err != nil {
		return nil, err
	}
	exists, err := el.IndexExists("ipfs").Do()
	if err != nil {
		return nil, err
	}
	if !exists {
		// Index does not exist yet, create
		el.CreateIndex("ipfs")
	}

	return el, nil
}

func add(c *cli.Context) error {
	if c.NArg() != 1 {
		return cli.NewExitError("Please supply one hash as argument.", 1)
	}

	hash := c.Args().Get(0)

	fmt.Printf("Adding hash '%s' to queue\n", hash)

	ch, err := queue.NewChannel()
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}
	defer ch.Close()

	queue, err := queue.NewTaskQueue(ch, "hashes")
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	err = queue.AddTask(map[string]interface{}{
		"hash": hash,
	})

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	return nil
}

func crawl(c *cli.Context) error {
	// For now, assume gateway running on default host:port
	sh := shell.NewShell(IPFS_API)

	el, err := get_elastic()

	ch, err := queue.NewChannel()
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}
	defer ch.Close()

	hq, err := queue.NewTaskQueue(ch, "hashes")
	fq, err := queue.NewTaskQueue(ch, "files")
	if err != nil {
		// do something with the error
		return cli.NewExitError(err.Error(), 1)
	}

	id := indexer.NewIndexer(el)

	crawler := crawler.NewCrawler(sh, id, fq, hq)

	errc := make(chan error, 1)

	for i := 0; i < HASH_WORKERS; i++ {
		hq.StartConsumer(func(params map[string]interface{}) error {
			// TODO: Assert hash in map, ideally by using custom type
			return crawler.CrawlHash(params["hash"].(string))
		}, errc)
	}

	for i := 0; i < FILE_WORKERS; i++ {
		fq.StartConsumer(func(params map[string]interface{}) error {
			// TODO: Assert hash in map, ideally by using custom type
			return crawler.CrawlFile(params["hash"].(string))
		}, errc)
	}

	// sigs := make(chan os.Signal, 1)
	// signal.Notify(sigs, syscall.SIGQUIT)

	log.Printf(" [*] Waiting for messages. To exit press CTRL+C")

	for {
		select {
		case err = <-errc:
			log.Println(err.Error())
		}
	}

	// No error
	return nil
}
<commit_msg>More verbose exception logging.<commit_after>package main

import (
	"fmt"
	"github.com/dokterbob/ipfs-search/crawler"
	"github.com/dokterbob/ipfs-search/indexer"
	"github.com/dokterbob/ipfs-search/queue"
	"gopkg.in/ipfs/go-ipfs-api.v1"
	"gopkg.in/olivere/elastic.v3"
	"gopkg.in/urfave/cli.v1"
	"log"
	"os"
)

const (
	IPFS_API     = "localhost:5001"
	HASH_WORKERS = 10
	FILE_WORKERS = 10
)

func main() {
	app := cli.NewApp()
	app.Name = "ipfs-search"
	app.Usage = "IPFS search engine."

	app.Commands = []cli.Command{
		{
			Name:    "add",
			Aliases: []string{"a"},
			Usage:   "add `HASH` to crawler queue",
			Action:  add,
		},
		{
			Name:    "crawl",
			Aliases: []string{"c"},
			Usage:   "start crawler",
			Action:  crawl,
		},
	}

	app.Run(os.Args)
}

func get_elastic() (*elastic.Client, error) {
	el, err := elastic.NewClient()
	if err != nil {
		return nil, err
	}
	exists, err := el.IndexExists("ipfs").Do()
	if err != nil {
		return nil, err
	}
	if !exists {
		// Index does not exist yet, create
		el.CreateIndex("ipfs")
	}

	return el, nil
}

func add(c *cli.Context) error {
	if c.NArg() != 1 {
		return cli.NewExitError("Please supply one hash as argument.", 1)
	}

	hash := c.Args().Get(0)

	fmt.Printf("Adding hash '%s' to queue\n", hash)

	ch, err := queue.NewChannel()
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}
	defer ch.Close()

	queue, err := queue.NewTaskQueue(ch, "hashes")
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	err = queue.AddTask(map[string]interface{}{
		"hash": hash,
	})

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	return nil
}

func crawl(c *cli.Context) error {
	// For now, assume gateway running on default host:port
	sh := shell.NewShell(IPFS_API)

	el, err := get_elastic()

	ch, err := queue.NewChannel()
	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}
	defer ch.Close()

	hq, err := queue.NewTaskQueue(ch, "hashes")
	fq, err := queue.NewTaskQueue(ch, "files")
	if err != nil {
		// do something with the error
		return cli.NewExitError(err.Error(), 1)
	}

	id := indexer.NewIndexer(el)

	crawler := crawler.NewCrawler(sh, id, fq, hq)

	errc := make(chan error, 1)

	for i := 0; i < HASH_WORKERS; i++ {
		hq.StartConsumer(func(params map[string]interface{}) error {
			// TODO: Assert hash in map, ideally by using custom type
			return crawler.CrawlHash(params["hash"].(string))
		}, errc)
	}

	for i := 0; i < FILE_WORKERS; i++ {
		fq.StartConsumer(func(params map[string]interface{}) error {
			// TODO: Assert hash in map, ideally by using custom type
			return crawler.CrawlFile(params["hash"].(string))
		}, errc)
	}

	// sigs := make(chan os.Signal, 1)
	// signal.Notify(sigs, syscall.SIGQUIT)

	log.Printf(" [*] Waiting for messages. To exit press CTRL+C")

	for {
		select {
		case err = <-errc:
			log.Printf("%T: %v", err, err)
		}
	}

	// No error
	return nil
}
<|endoftext|>