<commit_before>package main

import (
	"bytes"
	"compress/gzip"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

type config struct {
	pkg string
	out string
	in  []string
}

type filedata map[string]string

func main() {
	c := parseConfig()
	validateConfig(c)
	d := read(c)
	write(c, d)
}

func parseConfig() *config {
	flag.Usage = func() {
		fmt.Printf("Usage: %s [options] <input directories>\n\n", os.Args[0])
		flag.PrintDefaults()
	}

	c := &config{}
	flag.StringVar(&c.pkg, "pkg", c.pkg, "Package name for generated code.")
	flag.StringVar(&c.out, "out", c.out, "Output file to be generated.")
	flag.Parse()

	c.in = make([]string, flag.NArg())
	for i := range c.in {
		c.in[i] = flag.Arg(i)
	}

	return c
}

func validateConfig(c *config) {
	if flag.NArg() == 0 {
		fmt.Fprintf(os.Stderr, "Missing <input dir>\n\n")
		flag.Usage()
		os.Exit(1)
	}

	if c.out == "" || c.pkg == "" {
		fmt.Fprintf(os.Stderr, "Package and output file are required\n\n")
		flag.Usage()
		os.Exit(2)
	}
}

func read(c *config) filedata {
	d := filedata{}
	for _, pattern := range c.in {
		readPattern(d, pattern)
	}
	return d
}

func readPattern(d filedata, pattern string) {
	matches, err := filepath.Glob(pattern)
	if err != nil {
		panic(fmt.Errorf("couldn't resolve pattern %s", pattern))
	}
	for _, filename := range matches {
		readFile(d, filename)
	}
}

func readFile(d filedata, filename string) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(fmt.Errorf("couldn't read from %s", filename))
	}
	d[filename] = compress(b)
}

func compress(b []byte) string {
	var buf bytes.Buffer
	w := gzip.NewWriter(&buf)
	w.Write(b)
	w.Close()

	s := fmt.Sprintf("%x", buf.Bytes())
	p := make([]string, len(s)/2)
	for i, j := 0, 0; i < len(s); i += 2 {
		p[j] = s[i : i+2]
		j++
	}
	return `\x` + strings.Join(p, `\x`)
}

func write(c *config, d filedata) {
	var buf bytes.Buffer
	buf.WriteString(header(c))
	buf.WriteString(filenames(d))
	buf.WriteString(data(d))
	buf.WriteString(functions())
	ioutil.WriteFile(c.out, buf.Bytes(), os.ModePerm)
}

func header(c *config) string {
	return fmt.Sprintf(`
package %s

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
)
`, c.pkg) + "\n"
}

func filenames(d filedata) string {
	var buf bytes.Buffer
	buf.WriteString("var filenames = []string{\n")
	for _, f := range sortedFilenames(d) {
		buf.WriteString(fmt.Sprintf("\t%q,\n", f))
	}
	buf.WriteString("}\n\n")
	return buf.String()
}

func sortedFilenames(d filedata) []string {
	fn := make([]string, len(d))
	i := 0
	for f := range d {
		fn[i] = f
		i++
	}
	sort.Strings(fn)
	return fn
}

func data(d filedata) string {
	var buf bytes.Buffer
	buf.WriteString("var data = map[string][]byte{\n")
	for fn, data := range d {
		buf.WriteString(fmt.Sprintf("\t%q: []byte(\"%s\"),\n", fn, data))
	}
	buf.WriteString("}\n")
	return buf.String()
}

func functions() string {
	return `
// AssetNames returns a sorted list of all bundled paths
func AssetNames() []string {
	an := make([]string, len(filenames))
	for i, n := range filenames {
		an[i] = filenames[i]
	}
	return an
}

// Get returns an asset by name
func Get(fn string) ([]byte, bool) {
	if d, ok := data[fn]; ok {
		return uncompress(d), true
	}
	return nil, false
}

// MustGet returns an asset by name or explodes
func MustGet(fn string) []byte {
	if r, ok := Get(fn); ok {
		return r
	}
	panic(fmt.Errorf("could not find asset: %s", fn))
}

func uncompress(b []byte) []byte {
	r, err := gzip.NewReader(bytes.NewBuffer(b))
	if err != nil {
		panic(err)
	}
	defer r.Close()
	var buf bytes.Buffer
	io.Copy(&buf, r)
	return buf.Bytes()
}`
}
<commit_msg>a little cleaner<commit_after>package main

import (
	"bytes"
	"compress/gzip"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
)

type config struct {
	pkg string
	out string
	in  []string
}

type filedata map[string]string

func main() {
	c := parseConfig()
	validateConfig(c)
	d := read(c)
	write(c, d)
}

func parseConfig() *config {
	flag.Usage = func() {
		fmt.Printf("Usage: %s [options] <input directories>\n\n", os.Args[0])
		flag.PrintDefaults()
	}

	c := &config{}
	flag.StringVar(&c.pkg, "pkg", c.pkg, "Package name for generated code.")
	flag.StringVar(&c.out, "out", c.out, "Output file to be generated.")
	flag.Parse()

	c.in = make([]string, flag.NArg())
	for i := range c.in {
		c.in[i] = flag.Arg(i)
	}

	return c
}

func validateConfig(c *config) {
	if flag.NArg() == 0 {
		fmt.Fprintf(os.Stderr, "Missing <input dir>\n\n")
		flag.Usage()
		os.Exit(1)
	}

	if c.out == "" || c.pkg == "" {
		fmt.Fprintf(os.Stderr, "Package and output file are required\n\n")
		flag.Usage()
		os.Exit(2)
	}
}

func read(c *config) filedata {
	d := filedata{}
	for _, pattern := range c.in {
		readPattern(d, pattern)
	}
	return d
}

func readPattern(d filedata, pattern string) {
	matches, err := filepath.Glob(pattern)
	if err != nil {
		panic(fmt.Errorf("couldn't resolve pattern %s", pattern))
	}
	for _, filename := range matches {
		readFile(d, filename)
	}
}

func readFile(d filedata, filename string) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(fmt.Errorf("couldn't read from %s", filename))
	}
	d[filename] = compress(b)
}

func compress(b []byte) string {
	var buf bytes.Buffer
	w := gzip.NewWriter(&buf)
	w.Write(b)
	w.Close()

	s := fmt.Sprintf("%x", buf.Bytes())
	p := make([]string, len(s)/2)
	for i, j := 0, 0; i < len(s); i += 2 {
		p[j] = s[i : i+2]
		j++
	}
	return `\x` + strings.Join(p, `\x`)
}

func write(c *config, d filedata) {
	var buf bytes.Buffer
	buf.WriteString(header(c))
	buf.WriteString(filenames(d))
	buf.WriteString(data(d))
	buf.WriteString(functions())

	if err := os.MkdirAll(path.Dir(c.out), os.ModePerm); err != nil {
		panic(err)
	}

	if err := ioutil.WriteFile(c.out, buf.Bytes(), os.ModePerm); err != nil {
		panic(err)
	}
}

func header(c *config) string {
	return fmt.Sprintf(`package %s

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
)
`, c.pkg) + "\n"
}

func filenames(d filedata) string {
	var buf bytes.Buffer
	buf.WriteString("var filenames = []string{\n")
	for _, f := range sortedFilenames(d) {
		buf.WriteString(fmt.Sprintf("\t%q,\n", f))
	}
	buf.WriteString("}\n\n")
	return buf.String()
}

func sortedFilenames(d filedata) []string {
	fn := make([]string, len(d))
	i := 0
	for f := range d {
		fn[i] = f
		i++
	}
	sort.Strings(fn)
	return fn
}

func data(d filedata) string {
	var buf bytes.Buffer
	buf.WriteString("var data = map[string][]byte{\n")
	for fn, data := range d {
		buf.WriteString(fmt.Sprintf("\t%q: []byte(\"%s\"),\n", fn, data))
	}
	buf.WriteString("}\n")
	return buf.String()
}

func functions() string {
	return `
// AssetNames returns a sorted list of all bundled paths
func AssetNames() []string {
	an := make([]string, len(filenames))
	for i, n := range filenames {
		an[i] = n
	}
	return an
}

// Get returns an asset by name
func Get(fn string) ([]byte, bool) {
	if d, ok := data[fn]; ok {
		return uncompress(d), true
	}
	return nil, false
}

// MustGet returns an asset by name or explodes
func MustGet(fn string) []byte {
	if r, ok := Get(fn); ok {
		return r
	}
	panic(fmt.Errorf("could not find asset: %s", fn))
}

func uncompress(b []byte) []byte {
	r, err := gzip.NewReader(bytes.NewBuffer(b))
	if err != nil {
		panic(err)
	}
	defer r.Close()
	var buf bytes.Buffer
	io.Copy(&buf, r)
	return buf.Bytes()
}`
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/tomsteele/blacksheepwall/bsw"
	"log"
	"net"
	"os"
	"runtime"
)

var usage = `
  Usage: blacksheepwall [options] <ip address or CIDR>

  Options:
  -h, --help            Show Usage and exit.
  -version              Show version and exit.
  -debug                Enable debugging and show errors returned from tasks.
  -concurrency <int>    Max amount of concurrent tasks.    [default: 100]
  -cpus <int>           Max amount of cpus  for the go runtime.    [default: runtime.NumCPU()]
  -server <string>      DNS server address.    [default: "8.8.8.8"]
  -input <string>       Line separated file of networks (CIDR) or
                        IP Addresses.
  -ipv6	                Look for additional AAAA records where applicable.
  -domain <string>      Target domain to use for certain tasks.
  -dictionary <string>  Attempt to retrieve the CNAME and A record for
                        each subdomain in the line separated file.
  -yandex <string>      Provided a Yandex search XML API url. Use the Yandex
                        search 'rhost:' operator to find subdomains of a
                        provided domain..
  -bing	<string>        Provided a base64 encoded API key. Use the Bing search
                        API's 'ip:' operator to lookup hostnames for each host.
  -headers              Perform HTTP(s) requests to each host and look for
                        hostnames in a possible Location header.
  -reverse              Retrieve the PTR for each host.
  -tls                  Attempt to retrieve names from TLS certificates
                        (CommonName and Subject Alternative Name).
  -viewdns              Lookup each host using viewdns.info's Reverse IP
                        Lookup function.
  -fcrdns               Verify results by attempting to retrieve the A or AAAA record for
                        each result previously identified hostname.
  -clean                Print results as unique hostnames for each host.
  -csv                  Print results in csv format.
  -json                 Print results as JSON.

`

func linesToIpList(lines []string) ([]string, error) {
	ipList := make([]string, 0)
	for _, line := range lines {
		if net.ParseIP(line) != nil {
			ipList = append(ipList, line)
		} else if ip, network, err := net.ParseCIDR(line); err == nil {
			for ip := ip.Mask(network.Mask); network.Contains(ip); increaseIp(ip) {
				ipList = append(ipList, ip.String())
			}
		} else {
			return ipList, errors.New("\"" + line + "\" is not an IP Address or CIDR Network")
		}
	}
	return ipList, nil
}

func increaseIp(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

func readFileLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	lines := make([]string, 0)
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

func setMaxProcs(cpus int) {
	if cpus < 1 {
		log.Fatal("-cpus must be atleast 1")
	}
	if cpus > runtime.NumCPU() {
		log.Fatal("-cpus count too high")
	}
	runtime.GOMAXPROCS(cpus)
}

type task func() ([]bsw.Result, error)
type empty struct{}

func main() {
	var flVersion = flag.Bool("version", false, "Show version and exit.")
	var flConcurrency = flag.Int("concurrency", 100, "Max amount of concurrent tasks.")
	var flCpus = flag.Int("cpus", runtime.NumCPU(), "Max amount of cpus  for the go runtime.")
	var flDebug = flag.Bool("debug", false, "Enable debugging and show errors returned from tasks.")
	var flipv6 = flag.Bool("ipv6", false, "Look for AAAA records where applicable.")
	var flServerAddr = flag.String("server", "8.8.8.8", "DNS server address.")
	var flIpFile = flag.String("input", "", "Line separated file of networks (CIDR) or IP Addresses.")
	var flReverse = flag.Bool("reverse", false, "Retrieve the PTR for each host.")
	var flHeader = flag.Bool("headers", false, "Perform HTTP(s) requests to each host and look for hostnames in a possible Location header.")
	var flTLS = flag.Bool("tls", false, "Attempt to retrieve names from TLS certificates (CommonName and Subject Alternative Name).")
	var flViewDnsInfo = flag.Bool("viewdns", false, "Lookup each host using viewdns.info's Reverse IP Lookup function.")
	var flBing = flag.String("bing", "", "Provided a base64 encoded API key. Use the Bing search API's 'ip:' operator to lookup hostnames for each host.")
	var flYandex = flag.String("yandex", "", "Provided a Yandex search XML API url. Use the Yandex search 'rhost:' operator to find subdomains of a provided domain.")
	var flDomain = flag.String("domain", "", "Target domain to use for certain tasks.")
	var flDictFile = flag.String("dictionary", "", "Attempt to retrieve the CNAME and A record for each subdomain in the line separated file.")
	var flFcrdns = flag.Bool("fcrdns", false, "Verify results by attempting to retrieve the A or AAAA record for each result previously identified hostname.")
	var flClean = flag.Bool("clean", false, "Print results as unique hostnames for each host.")
	var flCsv = flag.Bool("csv", false, "Print results in csv format.")
	var flJson = flag.Bool("json", false, "Print results as JSON.")
	flag.Usage = func() { fmt.Print(usage) }
	flag.Parse()

	if *flVersion {
		fmt.Println("blacksheepwall version ", bsw.VERSION)
		os.Exit(0)
	}

	setMaxProcs(*flCpus)

	ipAddrList := make([]string, 0)
	var flNetwork = ""
	if *flIpFile == "" && *flDomain == "" && len(flag.Args()) < 1 {
		log.Fatal("You didn't provide any work for me to do")
	}
	if *flYandex != "" && *flDomain == "" {
		log.Fatal("Yandex API requires domain set with -domain")
	}
	if *flDictFile != "" && *flDomain == "" {
		log.Fatal("Dictionary lookup requires domain set with -domain")
	}
	if *flDomain != "" && *flYandex == "" && *flDictFile == "" {
		log.Fatal("-domain provided but no methods provided that use it")
	}
	if len(flag.Args()) > 0 {
		flNetwork = flag.Arg(0)
		list, err := linesToIpList([]string{flNetwork})
		if err != nil {
			log.Fatal(err.Error())
		}
		ipAddrList = append(ipAddrList, list...)
	}
	if *flIpFile != "" {
		lines, err := readFileLines(*flIpFile)
		if err != nil {
			log.Fatal("Error reading " + *flIpFile + " " + err.Error())
		}
		list, err := linesToIpList(lines)
		if err != nil {
			log.Fatal(err.Error())
		}
		ipAddrList = append(ipAddrList, list...)
	}
	tracker := make(chan empty)
	res := make(chan []bsw.Result)
	tasks := make(chan task)
	log.Printf("Spreading tasks across %d goroutines", *flConcurrency)
	for i := 0; i < *flConcurrency; i++ {
		go func() {
			var c = 0
			for def := range tasks {
				result, err := def()
				if m := c % 2; m == 0 {
					c = 3
					os.Stderr.WriteString("\rWorking \\")
				} else {
					c = 2
					os.Stderr.WriteString("\rWorking /")
				}
				if err != nil && *flDebug {
					log.Println(err.Error())
				}
				if err == nil {
					res <- result
				}
			}
			e := empty{}
			tracker <- e
		}()
	}

	results := make([]bsw.Result, 0)
	go func() {
		for result := range res {
			if len(result) < 1 {
				continue
			}
			if *flFcrdns {
				for _, r := range result {
					ip, err := bsw.LookupName(r.Hostname, *flServerAddr)
					if err == nil && len(ip) > 0 {
						results = append(results, bsw.Result{Source: "fcrdns", IP: ip, Hostname: r.Hostname})
					}
					ip, err = bsw.LookupName6(r.Hostname, *flServerAddr)
					if err == nil && len(ip) > 0 {
						results = append(results, bsw.Result{Source: "fcrdns", IP: ip, Hostname: r.Hostname})
					}
				}
			} else {
				results = append(results, result...)
			}
		}
		e := empty{}
		tracker <- e
	}()

	var bingPath string
	if *flBing != "" {
		p, err := bsw.FindBingSearchPath(*flBing)
		if err != nil {
			log.Fatal(err.Error())
		}
		bingPath = p
	}

	for _, h := range ipAddrList {
		host := h
		if *flReverse {
			tasks <- func() ([]bsw.Result, error) {
				return bsw.Reverse(host, *flServerAddr)
			}
		}
		if *flTLS {
			tasks <- func() ([]bsw.Result, error) {
				return bsw.TLS(host)
			}
		}
		if *flViewDnsInfo {
			tasks <- func() ([]bsw.Result, error) {
				return bsw.ViewDnsInfo(host)
			}
		}
		if *flBing != "" && bingPath != "" {
			tasks <- func() ([]bsw.Result, error) {
				return bsw.BingAPI(host, *flBing, bingPath)
			}
		}
		if *flHeader {
			tasks <- func() ([]bsw.Result, error) {
				return bsw.Headers(host)
			}
		}

	}

	if *flDictFile != "" && *flDomain != "" {
		nameList, err := readFileLines(*flDictFile)
		if err != nil {
			log.Fatal("Error reading " + *flDictFile + " " + err.Error())
		}
		blacklist := bsw.GetWildCard(*flDomain, *flServerAddr)
		var blacklist6 = ""
		if *flipv6 {
			blacklist6 = bsw.GetWildCard6(*flDomain, *flServerAddr)
		}
		for _, n := range nameList {
			sub := n
			tasks <- func() ([]bsw.Result, error) {
				return bsw.Dictionary(*flDomain, sub, blacklist, *flServerAddr)
			}
			if *flipv6 {
				tasks <- func() ([]bsw.Result, error) {
					return bsw.Dictionary6(*flDomain, sub, blacklist6, *flServerAddr)
				}
			}
		}
	}

	if *flYandex != "" && *flDomain != "" {
		tasks <- func() ([]bsw.Result, error) {
			return bsw.YandexAPI(*flDomain, *flYandex, *flServerAddr)
		}
	}

	close(tasks)
	for i := 0; i < *flConcurrency; i++ {
		<-tracker
	}
	close(res)
	<-tracker

	os.Stderr.WriteString("\r")
	log.Println("All tasks completed")
	fmt.Println()

	if *flJson {
		j, _ := json.MarshalIndent(results, "", "    ")
		fmt.Println(string(j))
	} else if *flCsv {
		for _, r := range results {
			fmt.Printf("%s,%s,%s\n", r.Hostname, r.IP, r.Source)
		}
	} else if *flClean {
		cleanSet := make(map[string][]string)
		for _, r := range results {
			cleanSet[r.Hostname] = append(cleanSet[r.Hostname], r.IP)
		}
		for k, v := range cleanSet {
			fmt.Printf("%s:\n", k)
			for _, ip := range v {
				fmt.Printf("\t%s\n", ip)
			}
		}
	} else {
		for _, r := range results {
			fmt.Printf("Hostname: %s IP: %s Source: %s\n", r.Hostname, r.IP, r.Source)
		}
	}
}
<commit_msg>Cleans up code of main.go<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/tomsteele/blacksheepwall/bsw"
	"log"
	"net"
	"os"
	"runtime"
)

var usage = `
  Usage: blacksheepwall [options] <ip address or CIDR>

  Options:
  -h, --help            Show Usage and exit.
  -version              Show version and exit.
  -debug                Enable debugging and show errors returned from tasks.
  -concurrency <int>    Max amount of concurrent tasks.    [default: 100]
  -cpus <int>           Max amount of cpus  for the go runtime.    [default: runtime.NumCPU()]
  -server <string>      DNS server address.    [default: "8.8.8.8"]
  -input <string>       Line separated file of networks (CIDR) or
                        IP Addresses.
  -ipv6	                Look for additional AAAA records where applicable.
  -domain <string>      Target domain to use for certain tasks.
  -dictionary <string>  Attempt to retrieve the CNAME and A record for
                        each subdomain in the line separated file.
  -yandex <string>      Provided a Yandex search XML API url. Use the Yandex
                        search 'rhost:' operator to find subdomains of a
                        provided domain..
  -bing	<string>        Provided a base64 encoded API key. Use the Bing search
                        API's 'ip:' operator to lookup hostnames for each host.
  -headers              Perform HTTP(s) requests to each host and look for
                        hostnames in a possible Location header.
  -reverse              Retrieve the PTR for each host.
  -tls                  Attempt to retrieve names from TLS certificates
                        (CommonName and Subject Alternative Name).
  -viewdns              Lookup each host using viewdns.info's Reverse IP
                        Lookup function.
  -fcrdns               Verify results by attempting to retrieve the A or AAAA record for
                        each result previously identified hostname.
  -clean                Print results as unique hostnames for each host.
  -csv                  Print results in csv format.
  -json                 Print results as JSON.

`

// Returned all ip addresses from each CIDR range in a list
func linesToIpList(lines []string) ([]string, error) {
	ipList := make([]string, 0)
	for _, line := range lines {
		if net.ParseIP(line) != nil {
			ipList = append(ipList, line)
		} else if ip, network, err := net.ParseCIDR(line); err == nil {
			for ip := ip.Mask(network.Mask); network.Contains(ip); increaseIp(ip) {
				ipList = append(ipList, ip.String())
			}
		} else {
			return ipList, errors.New("\"" + line + "\" is not an IP Address or CIDR Network")
		}
	}
	return ipList, nil
}

// Increases each byte of a net.IP
func increaseIp(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

// Reads lines from a file and returns as a slice
func readFileLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	lines := make([]string, 0)
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

// Sets the max CPUS for the runtime
func setMaxProcs(cpus int) {
	if cpus < 1 {
		log.Fatal("-cpus must be atleast 1")
	}
	if cpus > runtime.NumCPU() {
		log.Fatal("-cpus count too high")
	}
	runtime.GOMAXPROCS(cpus)
}

type task func() ([]bsw.Result, error)
type empty struct{}

func main() {
	flVersion := flag.Bool("version", false, "Show version and exit.")
	flConcurrency := flag.Int("concurrency", 100, "Max amount of concurrent tasks.")
	flCpus := flag.Int("cpus", runtime.NumCPU(), "Max amount of cpus  for the go runtime.")
	flDebug := flag.Bool("debug", false, "Enable debugging and show errors returned from tasks.")
	flipv6 := flag.Bool("ipv6", false, "Look for AAAA records where applicable.")
	flServerAddr := flag.String("server", "8.8.8.8", "DNS server address.")
	flIpFile := flag.String("input", "", "Line separated file of networks (CIDR) or IP Addresses.")
	flReverse := flag.Bool("reverse", false, "Retrieve the PTR for each host.")
	flHeader := flag.Bool("headers", false, "Perform HTTP(s) requests to each host and look for hostnames in a possible Location header.")
	flTLS := flag.Bool("tls", false, "Attempt to retrieve names from TLS certificates (CommonName and Subject Alternative Name).")
	flViewDnsInfo := flag.Bool("viewdns", false, "Lookup each host using viewdns.info's Reverse IP Lookup function.")
	flBing := flag.String("bing", "", "Provided a base64 encoded API key. Use the Bing search API's 'ip:' operator to lookup hostnames for each host.")
	flYandex := flag.String("yandex", "", "Provided a Yandex search XML API url. Use the Yandex search 'rhost:' operator to find subdomains of a provided domain.")
	flDomain := flag.String("domain", "", "Target domain to use for certain tasks.")
	flDictFile := flag.String("dictionary", "", "Attempt to retrieve the CNAME and A record for each subdomain in the line separated file.")
	flFcrdns := flag.Bool("fcrdns", false, "Verify results by attempting to retrieve the A or AAAA record for each result previously identified hostname.")
	flClean := flag.Bool("clean", false, "Print results as unique hostnames for each host.")
	flCsv := flag.Bool("csv", false, "Print results in csv format.")
	flJson := flag.Bool("json", false, "Print results as JSON.")
	flag.Usage = func() { fmt.Print(usage) }
	flag.Parse()

	if *flVersion {
		fmt.Println("blacksheepwall version ", bsw.VERSION)
		os.Exit(0)
	}

	setMaxProcs(*flCpus)

	// Holds all ip addresses for testing
	ipAddrList := make([]string, 0)

	// Used to hold a ip or CIDR range passed as fl.Arg(0)
	var flNetwork string

	// Verify that some sort of work load was given in commands
	if *flIpFile == "" && *flDomain == "" && len(flag.Args()) < 1 {
		log.Fatal("You didn't provide any work for me to do")
	}
	if *flYandex != "" && *flDomain == "" {
		log.Fatal("Yandex API requires domain set with -domain")
	}
	if *flDictFile != "" && *flDomain == "" {
		log.Fatal("Dictionary lookup requires domain set with -domain")
	}
	if *flDomain != "" && *flYandex == "" && *flDictFile == "" {
		log.Fatal("-domain provided but no methods provided that use it")
	}

	// Get first argument that is not an option and turn it into a list of ips
	if len(flag.Args()) > 0 {
		flNetwork = flag.Arg(0)
		list, err := linesToIpList([]string{flNetwork})
		if err != nil {
			log.Fatal(err.Error())
		}
		ipAddrList = append(ipAddrList, list...)
	}

	// If file given as -input, read lines and turn each possible ip or network into
	// a list of ips. Appends list to ipAddrList. Will fail fatally if line in file
	// is not a valid ip or CIDR range.
	if *flIpFile != "" {
		lines, err := readFileLines(*flIpFile)
		if err != nil {
			log.Fatal("Error reading " + *flIpFile + " " + err.Error())
		}
		list, err := linesToIpList(lines)
		if err != nil {
			log.Fatal(err.Error())
		}
		ipAddrList = append(ipAddrList, list...)
	}

	// tracker: Chanel uses an empty struct to track when all goroutines in the pool
	//          have completed as well as a single call from the gather res.
	//
	// tasks:   Chanel used in the goroutine pool to manage incoming work. A task is
	//          a function wrapper that returns a slice of results and an error.
	//
	// res:     When each task is called in the pool, it will send valid results to
	//          the res channel. A goroutine manages this channel and appends results
	//          to results slice.
	tracker := make(chan empty)
	tasks := make(chan task)
	res := make(chan []bsw.Result)
	results := make([]bsw.Result, 0)

	// Start up *flConcurrency amount of goroutines
	log.Printf("Spreading tasks across %d goroutines", *flConcurrency)
	for i := 0; i < *flConcurrency; i++ {
		go func() {
			var c = 0
			for def := range tasks {
				result, err := def()
				if m := c % 2; m == 0 {
					c = 3
					os.Stderr.WriteString("\rWorking \\")
				} else {
					c = 2
					os.Stderr.WriteString("\rWorking /")
				}
				if err != nil && *flDebug {
					log.Println(err.Error())
				}
				if err == nil {
					res <- result
				}
			}
			e := empty{}
			tracker <- e
		}()
	}

	// Injest incoming results slices
	go func() {
		for result := range res {
			if len(result) < 1 {
				continue
			}
			if *flFcrdns {
				for _, r := range result {
					ip, err := bsw.LookupName(r.Hostname, *flServerAddr)
					if err == nil && len(ip) > 0 {
						results = append(results, bsw.Result{Source: "fcrdns", IP: ip, Hostname: r.Hostname})
					}
					ip, err = bsw.LookupName6(r.Hostname, *flServerAddr)
					if err == nil && len(ip) > 0 {
						results = append(results, bsw.Result{Source: "fcrdns", IP: ip, Hostname: r.Hostname})
					}
				}
			} else {
				results = append(results, result...)
			}
		}
		e := empty{}
		tracker <- e
	}()

	// Bing has two possible search paths. We need to find which one is valid.
	var bingPath string
	if *flBing != "" {
		p, err := bsw.FindBingSearchPath(*flBing)
		if err != nil {
			log.Fatal(err.Error())
		}
		bingPath = p
	}

	// ip based functionality should be added to the pool here
	for _, h := range ipAddrList {
		host := h
		if *flReverse {
			tasks <- func() ([]bsw.Result, error) {
				return bsw.Reverse(host, *flServerAddr)
			}
		}
		if *flTLS {
			tasks <- func() ([]bsw.Result, error) {
				return bsw.TLS(host)
			}
		}
		if *flViewDnsInfo {
			tasks <- func() ([]bsw.Result, error) {
				return bsw.ViewDnsInfo(host)
			}
		}
		if *flBing != "" && bingPath != "" {
			tasks <- func() ([]bsw.Result, error) {
				return bsw.BingAPI(host, *flBing, bingPath)
			}
		}
		if *flHeader {
			tasks <- func() ([]bsw.Result, error) {
				return bsw.Headers(host)
			}
		}

	}

	// Domain based functions will likely require separate blocks for each
	// Subdomain dictionary guessing
	if *flDictFile != "" && *flDomain != "" {
		nameList, err := readFileLines(*flDictFile)
		if err != nil {
			log.Fatal("Error reading " + *flDictFile + " " + err.Error())
		}
		// Get an ip for a possible wildcard domain and use it as a blacklist
		blacklist := bsw.GetWildCard(*flDomain, *flServerAddr)
		var blacklist6 string
		if *flipv6 {
			blacklist6 = bsw.GetWildCard6(*flDomain, *flServerAddr)
		}
		for _, n := range nameList {
			sub := n
			tasks <- func() ([]bsw.Result, error) {
				return bsw.Dictionary(*flDomain, sub, blacklist, *flServerAddr)
			}
			if *flipv6 {
				tasks <- func() ([]bsw.Result, error) {
					return bsw.Dictionary6(*flDomain, sub, blacklist6, *flServerAddr)
				}
			}
		}
	}

	if *flYandex != "" && *flDomain != "" {
		tasks <- func() ([]bsw.Result, error) {
			return bsw.YandexAPI(*flDomain, *flYandex, *flServerAddr)
		}
	}

	// Close the tasks channel after all jobs have completed and for
	// goroutine in the pool receive an empty message to the tracker
	close(tasks)
	for i := 0; i < *flConcurrency; i++ {
		<-tracker
	}
	close(res)
	// Receive and empty message from the result gatherer
	<-tracker

	os.Stderr.WriteString("\r")
	log.Println("All tasks completed\n")

	// Output options
	if *flJson {
		j, _ := json.MarshalIndent(results, "", "    ")
		fmt.Println(string(j))
	} else if *flCsv {
		for _, r := range results {
			fmt.Printf("%s,%s,%s\n", r.Hostname, r.IP, r.Source)
		}
	} else if *flClean {
		cleanSet := make(map[string][]string)
		for _, r := range results {
			cleanSet[r.Hostname] = append(cleanSet[r.Hostname], r.IP)
		}
		for k, v := range cleanSet {
			fmt.Printf("%s:\n", k)
			for _, ip := range v {
				fmt.Printf("\t%s\n", ip)
			}
		}
	} else {
		for _, r := range results {
			fmt.Printf("Hostname: %s IP: %s Source: %s\n", r.Hostname, r.IP, r.Source)
		}
	}
}
<|endoftext|>


<commit_before>// errorcheckwithauto -0 -m -d=inlfuncswithclosures=1

//go:build 386 || amd64 || arm64 || ppc64le || s390x
// +build 386 amd64 arm64 ppc64le s390x

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

// Similar to inline.go, but only for architectures that can merge loads.

package foo

import (
	"encoding/binary"
)

// Ensure that simple encoding/binary functions are cheap enough
// that functions using them can also be inlined (issue 42958).
func endian(b []byte) uint64 { // ERROR "can inline endian" "b does not escape"
	return binary.LittleEndian.Uint64(b) + binary.BigEndian.Uint64(b) // ERROR "inlining call to binary.littleEndian.Uint64" "inlining call to binary.bigEndian.Uint64"
}
<commit_msg>test: skip endian inlining test on noopt builder<commit_after>// errorcheckwithauto -0 -m -d=inlfuncswithclosures=1

//go:build (386 || amd64 || arm64 || ppc64le || s390x) && !gcflags_noopt
// +build 386 amd64 arm64 ppc64le s390x
// +build !gcflags_noopt

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

// Similar to inline.go, but only for architectures that can merge loads.

package foo

import (
	"encoding/binary"
)

// Ensure that simple encoding/binary functions are cheap enough
// that functions using them can also be inlined (issue 42958).
func endian(b []byte) uint64 { // ERROR "can inline endian" "b does not escape"
	return binary.LittleEndian.Uint64(b) + binary.BigEndian.Uint64(b) // ERROR "inlining call to binary.littleEndian.Uint64" "inlining call to binary.bigEndian.Uint64"
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"time"

	fcm "github.com/NaySoftware/go-fcm"
	"github.com/SaidinWoT/timespan"
	"github.com/heetch/sqalx"
	"github.com/jmoiron/sqlx"
	uuid "github.com/satori/go.uuid"

	sq "github.com/gbl08ma/squirrel"

	"github.com/gbl08ma/disturbancesmlx/interfaces"
	"github.com/gbl08ma/disturbancesmlx/scraper"
	"github.com/gbl08ma/disturbancesmlx/scraper/mlxscraper"
	"github.com/gbl08ma/keybox"
)

const (
	DEBUG       = true
	MLnetworkID = "pt-ml"
)

var (
	rdb           *sqlx.DB
	sdb           sq.StatementBuilderType
	rootSqalxNode sqalx.Node
	secrets       *keybox.Keybox
	fcmcl         *fcm.FcmClient
	mainLog       = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	webLog        = log.New(os.Stdout, "web", log.Ldate|log.Ltime)
	mlxscr        scraper.Scraper
	lastChange    time.Time
)

// MLlastDisturbanceTime returns the time of the latest Metro de Lisboa disturbance
func MLlastDisturbanceTime(node sqalx.Node) (t time.Time, err error) {
	tx, err := node.Beginx()
	if err != nil {
		return time.Now().UTC(), err
	}
	defer tx.Commit() // read-only tx

	n, err := interfaces.GetNetwork(rootSqalxNode, MLnetworkID)
	if err != nil {
		return time.Now().UTC(), err
	}
	d, err := n.LastDisturbance(rootSqalxNode)
	if err != nil {
		return time.Now().UTC(), err
	}

	if !d.Ended {
		return time.Now().UTC(), nil
	}

	return d.EndTime, nil
}

// MLlineAvailability returns the availability for a Metro de Lisboa line
func MLlineAvailability(node sqalx.Node, line *interfaces.Line, startTime time.Time, endTime time.Time) (float64, time.Duration, error) {
	// calculate closed time
	var closedDuration time.Duration
	ct := startTime
	wholeSpan := timespan.New(startTime, endTime.Sub(startTime))
	for ct.Before(endTime) {
		closeTime := time.Date(ct.Year(), ct.Month(), ct.Day(), 1, 0, 0, 0, ct.Location())
		openTime := time.Date(ct.Year(), ct.Month(), ct.Day(), 6, 30, 0, 0, ct.Location())

		closedSpan := timespan.New(closeTime, openTime.Sub(closeTime))
		d, hasIntersection := wholeSpan.Intersection(closedSpan)
		if hasIntersection {
			closedDuration += d.Duration()
		}
		ct = ct.AddDate(0, 0, 1)
	}

	return line.Availability(node, startTime, endTime, closedDuration)
}

func main() {
	var err error
	mainLog.Println("Server starting, opening keybox...")
	if DEBUG {
		secrets, err = keybox.Open("secrets-debug.json")
	} else {
		secrets, err = keybox.Open("secrets.json")
	}
	if err != nil {
		mainLog.Fatal(err)
	}
	mainLog.Println("Keybox opened")

	mainLog.Println("Opening database...")
	databaseURI, present := secrets.Get("databaseURI")
	if !present {
		mainLog.Fatal("Database connection string not present in keybox")
	}
	rdb, err = sqlx.Open("postgres", databaseURI)
	if err != nil {
		mainLog.Fatal(err)
	}
	defer rdb.Close()

	err = rdb.Ping()
	if err != nil {
		mainLog.Fatal(err)
	}
	sdb = sq.StatementBuilder.PlaceholderFormat(sq.Dollar).RunWith(rdb)

	rootSqalxNode, err = sqalx.New(rdb)
	if err != nil {
		mainLog.Fatal(err)
	}

	mainLog.Println("Database opened")

	l := log.New(os.Stdout, "mlxscraper", log.Ldate|log.Ltime)
	mlxscr = &mlxscraper.Scraper{
		URL:         "http://app.metrolisboa.pt/status/estado_Linhas.php",
		NetworkID:   MLnetworkID,
		NetworkName: "Metro de Lisboa",
		Source: &interfaces.Source{
			ID:          "mlxscraper-pt-ml",
			Name:        "Metro de Lisboa estado_Linhas.php",
			IsAutomatic: true,
		},
		Period: 1 * time.Minute,
	}
	mlxscr.Begin(l, func(status *interfaces.Status) {
		tx, err := rootSqalxNode.Beginx()
		if err != nil {
			mainLog.Println(err)
			return
		}
		defer tx.Rollback()

		log.Println("New status for line", status.Line.Name, "on network", status.Line.Network.Name)
		log.Println("  ", status.Status)
		if status.IsDowntime {
			log.Println("   Is disturbance!")
		}

		err = status.Update(tx)
		if err != nil {
			mainLog.Println(err)
			return
		}

		d, err := status.Line.LastDisturbance(tx)
		if err == nil {
			mainLog.Println("   Last disturbance at", d.StartTime, "description:", d.Description)
		} else {
			mainLog.Println(err)
		}

		disturbances, err := status.Line.OngoingDisturbances(tx)
		if err != nil {
			mainLog.Println(err)
			return
		}
		found := len(disturbances) > 0
		var disturbance *interfaces.Disturbance
		if found {
			mainLog.Println("   Found ongoing disturbance")
			disturbance = disturbances[len(disturbances)-1]
		} else {
			disturbance = &interfaces.Disturbance{
				ID:   uuid.NewV4().String(),
				Line: status.Line,
			}
		}
		sendNotification := false
		if status.IsDowntime && !found {
			disturbance.StartTime = status.Time
			disturbance.Description = status.Status
			disturbance.Statuses = append(disturbance.Statuses, status)
			err = disturbance.Update(tx)
			if err != nil {
				mainLog.Println(err)
				return
			}
			sendNotification = true
		} else if found {
			if !status.IsDowntime {
				// "close" this disturbance
				disturbance.EndTime = status.Time
				disturbance.Ended = true
			}
			disturbance.Statuses = append(disturbance.Statuses, status)
			err = disturbance.Update(tx)
			if err != nil {
				mainLog.Println(err)
				return
			}
			sendNotification = true
		}
		lastChange = time.Now().UTC()
		if tx.Commit() == nil && sendNotification {
			SendNotificationForDisturbance(disturbance, status)
		}
	},
		func(s scraper.Scraper) {
			for _, network := range s.Networks() {
				network.Update(rootSqalxNode)
			}
			for _, line := range s.Lines() {
				line.Update(rootSqalxNode)
			}
		})
	defer mlxscr.End()

	go WebServer()
	go APIserver()

	fcmServerKey, present := secrets.Get("firebaseServerKey")
	if !present {
		mainLog.Fatal("Firebase server key not present in keybox")
	}
	fcmcl = fcm.NewFcmClient(fcmServerKey)

	for {
		if DEBUG {
			printLatestDisturbance(rootSqalxNode)
			ld, err := MLlastDisturbanceTime(rootSqalxNode)
			if err != nil {
				mainLog.Println(err)
			}
			mainLog.Printf("Last disturbance: %s", ld.String())
		}
		time.Sleep(1 * time.Minute)
	}
}

func printLatestDisturbance(node sqalx.Node) {
	tx, err := node.Beginx()
	if err != nil {
		mainLog.Println(err)
		return
	}
	defer tx.Commit() // read-only tx

	n, err := interfaces.GetNetwork(tx, MLnetworkID)
	if err != nil {
		mainLog.Println(err)
		return
	}
	d, err := n.LastDisturbance(tx)
	if err == nil {
		mainLog.Println("Network last disturbance at", d.StartTime, "description:", d.Description)
	} else {
		mainLog.Println(err)
	}
}
<commit_msg>Do not overwrite information for existing lines on every start<commit_after>package main

import (
	"log"
	"os"
	"time"

	fcm "github.com/NaySoftware/go-fcm"
	"github.com/SaidinWoT/timespan"
	"github.com/heetch/sqalx"
	"github.com/jmoiron/sqlx"
	uuid "github.com/satori/go.uuid"

	sq "github.com/gbl08ma/squirrel"

	"github.com/gbl08ma/disturbancesmlx/interfaces"
	"github.com/gbl08ma/disturbancesmlx/scraper"
	"github.com/gbl08ma/disturbancesmlx/scraper/mlxscraper"
	"github.com/gbl08ma/keybox"
)

const (
	DEBUG       = true
	MLnetworkID = "pt-ml"
)

var (
	rdb           *sqlx.DB
	sdb           sq.StatementBuilderType
	rootSqalxNode sqalx.Node
	secrets       *keybox.Keybox
	fcmcl         *fcm.FcmClient
	mainLog       = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	webLog        = log.New(os.Stdout, "web", log.Ldate|log.Ltime)
	mlxscr        scraper.Scraper
	lastChange    time.Time
)

// MLlastDisturbanceTime returns the time of the latest Metro de Lisboa disturbance
func MLlastDisturbanceTime(node sqalx.Node) (t time.Time, err error) {
	tx, err := node.Beginx()
	if err != nil {
		return time.Now().UTC(), err
	}
	defer tx.Commit() // read-only tx

	n, err := interfaces.GetNetwork(rootSqalxNode, MLnetworkID)
	if err != nil {
		return time.Now().UTC(), err
	}
	d, err := n.LastDisturbance(rootSqalxNode)
	if err != nil {
		return time.Now().UTC(), err
	}

	if !d.Ended {
		return time.Now().UTC(), nil
	}

	return d.EndTime, nil
}

// MLlineAvailability returns the availability for a Metro de Lisboa line
func MLlineAvailability(node sqalx.Node, line *interfaces.Line, startTime time.Time, endTime time.Time) (float64, time.Duration, error) {
	// calculate closed time
	var closedDuration time.Duration
	ct := startTime
	wholeSpan := timespan.New(startTime, endTime.Sub(startTime))
	for ct.Before(endTime) {
		closeTime := time.Date(ct.Year(), ct.Month(), ct.Day(), 1, 0, 0, 0, ct.Location())
		openTime := time.Date(ct.Year(), ct.Month(), ct.Day(), 6, 30, 0, 0, ct.Location())

		closedSpan := timespan.New(closeTime, openTime.Sub(closeTime))
		d, hasIntersection := wholeSpan.Intersection(closedSpan)
		if hasIntersection {
			closedDuration += d.Duration()
		}
		ct = ct.AddDate(0, 0, 1)
	}

	return line.Availability(node, startTime, endTime, closedDuration)
}

func main() {
	var err error
	mainLog.Println("Server starting, opening keybox...")
	if DEBUG {
		secrets, err = keybox.Open("secrets-debug.json")
	} else {
		secrets, err = keybox.Open("secrets.json")
	}
	if err != nil {
		mainLog.Fatal(err)
	}
	mainLog.Println("Keybox opened")

	mainLog.Println("Opening database...")
	databaseURI, present := secrets.Get("databaseURI")
	if !present {
		mainLog.Fatal("Database connection string not present in keybox")
	}
	rdb, err = sqlx.Open("postgres", databaseURI)
	if err != nil {
		mainLog.Fatal(err)
	}
	defer rdb.Close()

	err = rdb.Ping()
	if err != nil {
		mainLog.Fatal(err)
	}
	sdb = sq.StatementBuilder.PlaceholderFormat(sq.Dollar).RunWith(rdb)

	rootSqalxNode, err = sqalx.New(rdb)
	if err != nil {
		mainLog.Fatal(err)
	}

	mainLog.Println("Database opened")

	l := log.New(os.Stdout, "mlxscraper", log.Ldate|log.Ltime)
	mlxscr = &mlxscraper.Scraper{
		URL:         "http://app.metrolisboa.pt/status/estado_Linhas.php",
		NetworkID:   MLnetworkID,
		NetworkName: "Metro de Lisboa",
		Source: &interfaces.Source{
			ID:          "mlxscraper-pt-ml",
			Name:        "Metro de Lisboa estado_Linhas.php",
			IsAutomatic: true,
		},
		Period: 1 * time.Minute,
	}
	mlxscr.Begin(l, func(status *interfaces.Status) {
		tx, err := rootSqalxNode.Beginx()
		if err != nil {
			mainLog.Println(err)
			return
		}
		defer tx.Rollback()

		log.Println("New status for line", status.Line.Name, "on network", status.Line.Network.Name)
		log.Println("  ", status.Status)
		if status.IsDowntime {
			log.Println("   Is disturbance!")
		}

		err = status.Update(tx)
		if err != nil {
			mainLog.Println(err)
			return
		}

		d, err := status.Line.LastDisturbance(tx)
		if err == nil {
			mainLog.Println("   Last disturbance at", d.StartTime, "description:", d.Description)
		} else {
			mainLog.Println(err)
		}

		disturbances, err := status.Line.OngoingDisturbances(tx)
		if err != nil {
			mainLog.Println(err)
			return
		}
		found := len(disturbances) > 0
		var disturbance *interfaces.Disturbance
		if found {
			mainLog.Println("   Found ongoing disturbance")
			disturbance = disturbances[len(disturbances)-1]
		} else {
			disturbance = &interfaces.Disturbance{
				ID:   uuid.NewV4().String(),
				Line: status.Line,
			}
		}
		sendNotification := false
		if status.IsDowntime && !found {
			disturbance.StartTime = status.Time
			disturbance.Description = status.Status
			disturbance.Statuses = append(disturbance.Statuses, status)
			err = disturbance.Update(tx)
			if err != nil {
				mainLog.Println(err)
				return
			}
			sendNotification = true
		} else if found {
			if !status.IsDowntime {
				// "close" this disturbance
				disturbance.EndTime = status.Time
				disturbance.Ended = true
			}
			disturbance.Statuses = append(disturbance.Statuses, status)
			err = disturbance.Update(tx)
			if err != nil {
				mainLog.Println(err)
				return
			}
			sendNotification = true
		}
		lastChange = time.Now().UTC()
		if tx.Commit() == nil && sendNotification {
			SendNotificationForDisturbance(disturbance, status)
		}
	},
		func(s scraper.Scraper) {
			tx, err := rootSqalxNode.Beginx()
			if err != nil {
				mainLog.Println(err)
				return
			}
			defer tx.Rollback()
			for _, newnetwork := range s.Networks() {
				newnetwork, err := interfaces.GetNetwork(tx, newnetwork.ID)
				if err != nil {
					mainLog.Println("New network " + newnetwork.ID)
					err = newnetwork.Update(tx)
					if err != nil {
						mainLog.Println(err)
						return
					}
				}
			}
			for _, newline := range s.Lines() {
				newline, err := interfaces.GetLine(tx, newline.ID)
				if err != nil {
					mainLog.Println("New line " + newline.ID)
					err = newline.Update(tx)
					if err != nil {
						mainLog.Println(err)
						return
					}
				}
			}
			tx.Commit()
		})
	defer mlxscr.End()

	go WebServer()
	go APIserver()

	fcmServerKey, present := secrets.Get("firebaseServerKey")
	if !present {
		mainLog.Fatal("Firebase server key not present in keybox")
	}
	fcmcl = fcm.NewFcmClient(fcmServerKey)

	for {
		if DEBUG {
			printLatestDisturbance(rootSqalxNode)
			ld, err := MLlastDisturbanceTime(rootSqalxNode)
			if err != nil {
				mainLog.Println(err)
			}
			mainLog.Printf("Last disturbance: %s", ld.String())
		}
		time.Sleep(1 * time.Minute)
	}
}

func printLatestDisturbance(node sqalx.Node) {
	tx, err := node.Beginx()
	if err != nil {
		mainLog.Println(err)
		return
	}
	defer tx.Commit() // read-only tx

	n, err := interfaces.GetNetwork(tx, MLnetworkID)
	if err != nil {
		mainLog.Println(err)
		return
	}
	d, err := n.LastDisturbance(tx)
	if err == nil {
		mainLog.Println("Network last disturbance at", d.StartTime, "description:", d.Description)
	} else {
		mainLog.Println(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"collector"
	"encoding/json"
	"errors"
	"fmt"
	"helper"
	"log"
	"os"
	"os/signal"
	"sse"
	"time"
)

var (
	// URL is the url that hosts the service that this program will interact with
	URL          = "http://mothership.serverstatusmonitoring.com"

	// URIRegister is the uri to be used to register the system this program will run on
	URIRegister  = "/register"

	// URICollector is the uri where collected data will be sent to
	URICollector = "/collector"

	// URIStatus is the uri to check the upstatus of URL
	URIStatus    = "/status"

	// Hostname is the hostname of the system this program will run on
	Hostname  = ""

	// IPAddress is the IP address of the system this program will run on
	IPAddress = ""

	// LogFile is the file where we want to log event data and errors
	LogFile           = "/var/log/sphire-sse.log"

	// ConfigurationFile is the configuration file we want to use
	ConfigurationFile = "/etc/sse/sse.conf"

	// Configuration is the configuration instance (loads the above LogFile)
	Configuration     = new(Config)

	// CollectFrequencySeconds is value which tells us
	// to collect a snapshot and store in cache
	// every X seconds where X is a non negative integer
	CollectFrequencySeconds = 1

	// ReportFrequencySeconds tells us the frequency
	// in seconds to report all snapshots in cache
	ReportFrequencySeconds  = 1

	// CPU is an instance of collector.CPU
	CPU     collector.CPU     = collector.CPU{}

	// Disks is an instance of collector.Disks
	Disks   collector.Disks   = collector.Disks{}

	// Memory is an instance of collector.Memory
	Memory  collector.Memory  = collector.Memory{}

	// Network is an instance of collector.Network
	Network collector.Network = collector.Network{}

	// System is an instance of collector.System
	System  collector.System  = collector.System{}

	// Version denotes the version of this program
	Version = "1.0.1"
)

/*
 Configuration struct is a direct map to the configuration located in the configuration JSON file.
*/
type Config struct {
	Identification struct {
		AccountID        string `json:"account_id"`
		OrganizationID   string `json:"organization_id"`
		OrganizationName string `json:"organization_name"`
		MachineNickname  string `json:"machine_nickname"`
	} `json:"identification"`
	Settings struct {
		Disk struct {
			IncludePartitionData bool `json:"include_partition_data"`
		} `json:"disk"`
		System struct {
			IncludeUsers bool `json:"include_users"`
		} `json:"system"`
		Reporting struct {
			CollectFrequencySeconds int `json:"collect_frequency_seconds"`
			ReportFrequencySeconds  int `json:"report_frequency_seconds"`
		} `json:"reporting"`
	} `json:"settings"`
}

func main() {
	// Define the global logger
	logger, err := os.OpenFile(LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	HandleError(err)
	defer logger.Close()
	log.SetOutput(logger)

	// Load and parse configuration file
	file, err := os.Open(ConfigurationFile)
	HandleError(err)
	err = json.NewDecoder(file).Decode(Configuration)
	HandleError(err)

	// Set any parameters that need to be set
	CollectFrequencySeconds = Configuration.Settings.Reporting.CollectFrequencySeconds
	ReportFrequencySeconds = Configuration.Settings.Reporting.ReportFrequencySeconds

	var status helper.Status = helper.Status{}
	var status_result bool = status.CheckStatus(URL + URIStatus)
	if status_result == false {
		HandleError(errors.New("Mothership unreachable. Check your internet connection."))
	}

	// Perform system initialization
	var server_obj sse.Server = sse.Server{}
	server, ipAddress, hostname, version, error := server_obj.Initialize()
	HandleError(error)

	IPAddress = ipAddress
	Hostname = hostname
	Version = version

	// Perform registration
	body, err := sse.Register(map[string]interface{}{
		"configuration":     Configuration,
		"mothership_url":    URL,
		"register_uri":      URIRegister,
		"version":           Version,
		"collect_frequency": CollectFrequencySeconds,
		"report_frequency":  ReportFrequencySeconds,
		"hostname":          Hostname,
		"ip_address":        IPAddress,
		"log_file":          LogFile,
		"config_file":       ConfigurationFile,
	}, URL+URIRegister+"/"+Version)
	if err != nil {
		HandleError(errors.New("Unable to register this machine" + string(body)))
	}

	// Set up our collector
	var counter int = 0
	var snapshot sse.Snapshot = sse.Snapshot{}
	var cache sse.Cache = sse.Cache{
		AccountId:        Configuration.Identification.AccountID,
		OrganizationID:   Configuration.Identification.OrganizationID,
		OrganizationName: Configuration.Identification.OrganizationName,
		MachineNickname:  Configuration.Identification.MachineNickname,
		Version:          Version,
		Server:           server}

	ticker := time.NewTicker(time.Duration(CollectFrequencySeconds) * time.Second)
	death := make(chan os.Signal, 1)
	signal.Notify(death, os.Interrupt, os.Kill)

	go func() {
		for {
			select {
			case <-ticker.C : // send the updated time back via to the channel
				// reset the snapshot to an empty struct
				snapshot = sse.Snapshot{}

				// fill in the Snapshot struct and add to the cache
				cache.Node = append(cache.Node, snapshot.Collector(Configuration.Settings.Disk.IncludePartitionData,
						Configuration.Settings.System.IncludeUsers))
				counter++

				if counter > 0 && counter%ReportFrequencySeconds == 0 {
					cache.Sender(URL + URICollector)
					cache.Node = nil // Clear the Node Cache
					counter = 0
				}
			case <-death:
				fmt.Println("died")
				return
			}
		}
	}()

	return

}

func HandleError(err error) {
	if err != nil {
		log.Println(helper.Trace(err, "ERROR"))
		fmt.Println(err, "ERROR")
		os.Exit(1)
	}
}
<commit_msg>add comments on Config and HandleError in main.go<commit_after>package main

import (
	"collector"
	"encoding/json"
	"errors"
	"fmt"
	"helper"
	"log"
	"os"
	"os/signal"
	"sse"
	"time"
)

var (
	// URL is the url that hosts the service that this program will interact with
	URL          = "http://mothership.serverstatusmonitoring.com"

	// URIRegister is the uri to be used to register the system this program will run on
	URIRegister  = "/register"

	// URICollector is the uri where collected data will be sent to
	URICollector = "/collector"

	// URIStatus is the uri to check the upstatus of URL
	URIStatus    = "/status"

	// Hostname is the hostname of the system this program will run on
	Hostname  = ""

	// IPAddress is the IP address of the system this program will run on
	IPAddress = ""

	// LogFile is the file where we want to log event data and errors
	LogFile           = "/var/log/sphire-sse.log"

	// ConfigurationFile is the configuration file we want to use
	ConfigurationFile = "/etc/sse/sse.conf"

	// Configuration is the configuration instance (loads the above LogFile)
	Configuration     = new(Config)

	// CollectFrequencySeconds is value which tells us
	// to collect a snapshot and store in cache
	// every X seconds where X is a non negative integer
	CollectFrequencySeconds = 1

	// ReportFrequencySeconds tells us the frequency
	// in seconds to report all snapshots in cache
	ReportFrequencySeconds  = 1

	// CPU is an instance of collector.CPU
	CPU     collector.CPU     = collector.CPU{}

	// Disks is an instance of collector.Disks
	Disks   collector.Disks   = collector.Disks{}

	// Memory is an instance of collector.Memory
	Memory  collector.Memory  = collector.Memory{}

	// Network is an instance of collector.Network
	Network collector.Network = collector.Network{}

	// System is an instance of collector.System
	System  collector.System  = collector.System{}

	// Version denotes the version of this program
	Version = "1.0.1"
)

// Config is a direct map to the configuration located in the configuration JSON file.
type Config struct {
	Identification struct {
		AccountID        string `json:"account_id"`
		OrganizationID   string `json:"organization_id"`
		OrganizationName string `json:"organization_name"`
		MachineNickname  string `json:"machine_nickname"`
	} `json:"identification"`
	Settings struct {
		Disk struct {
			IncludePartitionData bool `json:"include_partition_data"`
		} `json:"disk"`
		System struct {
			IncludeUsers bool `json:"include_users"`
		} `json:"system"`
		Reporting struct {
			CollectFrequencySeconds int `json:"collect_frequency_seconds"`
			ReportFrequencySeconds  int `json:"report_frequency_seconds"`
		} `json:"reporting"`
	} `json:"settings"`
}

func main() {
	// Define the global logger
	logger, err := os.OpenFile(LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	HandleError(err)
	defer logger.Close()
	log.SetOutput(logger)

	// Load and parse configuration file
	file, err := os.Open(ConfigurationFile)
	HandleError(err)
	err = json.NewDecoder(file).Decode(Configuration)
	HandleError(err)

	// Set any parameters that need to be set
	CollectFrequencySeconds = Configuration.Settings.Reporting.CollectFrequencySeconds
	ReportFrequencySeconds = Configuration.Settings.Reporting.ReportFrequencySeconds

	var status helper.Status = helper.Status{}
	var status_result bool = status.CheckStatus(URL + URIStatus)
	if status_result == false {
		HandleError(errors.New("Mothership unreachable. Check your internet connection."))
	}

	// Perform system initialization
	var server_obj sse.Server = sse.Server{}
	server, ipAddress, hostname, version, error := server_obj.Initialize()
	HandleError(error)

	IPAddress = ipAddress
	Hostname = hostname
	Version = version

	// Perform registration
	body, err := sse.Register(map[string]interface{}{
		"configuration":     Configuration,
		"mothership_url":    URL,
		"register_uri":      URIRegister,
		"version":           Version,
		"collect_frequency": CollectFrequencySeconds,
		"report_frequency":  ReportFrequencySeconds,
		"hostname":          Hostname,
		"ip_address":        IPAddress,
		"log_file":          LogFile,
		"config_file":       ConfigurationFile,
	}, URL+URIRegister+"/"+Version)
	if err != nil {
		HandleError(errors.New("Unable to register this machine" + string(body)))
	}

	// Set up our collector
	var counter int = 0
	var snapshot sse.Snapshot = sse.Snapshot{}
	var cache sse.Cache = sse.Cache{
		AccountId:        Configuration.Identification.AccountID,
		OrganizationID:   Configuration.Identification.OrganizationID,
		OrganizationName: Configuration.Identification.OrganizationName,
		MachineNickname:  Configuration.Identification.MachineNickname,
		Version:          Version,
		Server:           server}

	ticker := time.NewTicker(time.Duration(CollectFrequencySeconds) * time.Second)
	death := make(chan os.Signal, 1)
	signal.Notify(death, os.Interrupt, os.Kill)

	go func() {
		for {
			select {
			case <-ticker.C : // send the updated time back via to the channel
				// reset the snapshot to an empty struct
				snapshot = sse.Snapshot{}

				// fill in the Snapshot struct and add to the cache
				cache.Node = append(cache.Node, snapshot.Collector(Configuration.Settings.Disk.IncludePartitionData,
						Configuration.Settings.System.IncludeUsers))
				counter++

				if counter > 0 && counter%ReportFrequencySeconds == 0 {
					cache.Sender(URL + URICollector)
					cache.Node = nil // Clear the Node Cache
					counter = 0
				}
			case <-death:
				fmt.Println("died")
				return
			}
		}
	}()

	return

}

// HandleError abstracts the error handling so that
// we can both print the error, log the error and
// exit without panic
func HandleError(err error) {
	if err != nil {
		log.Println(helper.Trace(err, "ERROR"))
		fmt.Println(err, "ERROR")
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

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

	"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"`
	TITLE  string `json:"title"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

type Page struct {
	Title    string
	Body     []byte
	Raw      string
	Home     string
	Download string
	Clone    string
}

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, title string, expiry string) []string {
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	sha := hash(raw)
	query, err := db.Query("select id, title, hash, data, delkey from pastebin")
	for query.Next() {
		var id, title, hash, paste, delkey string
		err := query.Scan(&id, &title, &hash, &paste, &delkey)
		check(err)
		if hash == sha {
			url := ADDRESS + "/p/" + id
			return []string{id, title, hash, url, paste, delkey}
		}
	}
	id := generateName()
	var url string
	if lang == "" {
		url = ADDRESS + "/p/" + id
	} else {
		url = ADDRESS + "/p/" + id + "/" + lang
	}
	now := time.Now()
	var expiryTime string

	switch expiry {
	case "5 minutes":
		expiryTime = now.Add(time.Minute * 5).Format("2006-01-02 15:04:05")
		break

	case "1 hour":
		expiryTime = now.Add(time.Hour + 1).Format("2006-01-02 15:04:05")
		break

	case "1 day":
		expiryTime = now.Add(time.Hour * 24 * 1).Format("2006-01-02 15:04:05")
		break

	case "1 week":
		expiryTime = now.Add(time.Hour * 24 * 7).Format("2006-01-02 15:04:05")
		break

	case "1 month":
		expiryTime = now.Add(time.Hour * 24 * 30).Format("2006-01-02 15:04:05")
		break

	case "1 year":
		expiryTime = now.Add(time.Hour * 24 * 365).Format("2006-01-02 15:04:05")
		break

	default:
		expiryTime = now.Add(((time.Hour * 24) * 365) * 200).Format("2006-01-02 15:04:05")
		break

	}
	delKey := uniuri.NewLen(40)
	paste := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, title, hash, data, delkey, expiry) values(?,?,?,?,?,?)")
	check(err)
	if title == "" {
		_, err = stmt.Exec(id, id, sha, paste, delKey, expiryTime)
		check(err)
	} else {
		_, err = stmt.Exec(id, html.EscapeString(title), sha, paste, delKey, expiryTime)
		check(err)
	}
	db.Close()
	return []string{id, title, 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)

	_, 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")
		title := r.FormValue("title")
		expiry := r.FormValue("expiry")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
			return
		}
		values := save(paste, lang, title, expiry)
		b := &Response{
			ID:     values[0],
			TITLE:  values[1],
			HASH:   values[2],
			URL:    values[3],
			SIZE:   len(values[4]),
			DELKEY: values[5],
		}

		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+"
")

		case "redirect":
			http.Redirect(w, r, b.URL, 301)

		default:
			w.Header().Set("Content-Type", "plain/text")
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

func highlight(s string, lang string) (string, error) {

	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,noclasses=True,", "utf-8")
	if err != nil {
		return "", err
	}
	return highlight, nil

}

func getPaste(paste string, lang string) (string, string) {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	var title, s string
	var expiry string
	err = db.QueryRow("select title, data, expiry from pastebin where id=?", param1).Scan(&title, &s, &expiry)
	check(err)
	if time.Now().Format("2006-01-02 15:04:05") > expiry {
		stmt, err := db.Prepare("delete from pastebin where id=?")
		check(err)
		_, err = stmt.Exec(param1)
		check(err)
		return "Error invalid paste", ""
	}
	db.Close()

	if err == sql.ErrNoRows {
		return "Error invalid paste", ""
	} else {
		if lang == "" {
			return html.UnescapeString(s), html.UnescapeString(title)
		} else {
			high, err := highlight(s, lang)
			check(err)
			return high, html.UnescapeString(title)

		}
	}

}

var templates = template.Must(template.ParseFiles("assets/paste.html", "assets/index.html", "assets/clone.html"))
var syntax, _ = ioutil.ReadFile("assets/syntax.html")

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s, title := getPaste(paste, lang)
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste
	if lang == "" {
		p := &Page{
			Title:    title,
			Body:     []byte(s),
			Raw:      link,
			Home:     ADDRESS,
			Download: download,
			Clone:    clone,
		}
		err := templates.ExecuteTemplate(w, "paste.html", p)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}

	} else {
		fmt.Fprintf(w, string(syntax), paste, paste, s, ADDRESS, download, link, clone)

	}
}

func cloneHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, title := getPaste(paste, "")
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste
	p := &Page{
		Title:    title,
		Body:     []byte(s),
		Raw:      link,
		Home:     ADDRESS,
		Download: download,
		Clone:    clone,
	}
	err := templates.ExecuteTemplate(w, "clone.html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

}
func downloadHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, _ := getPaste(paste, "")
	w.Header().Set("Content-Disposition", "attachment; filename="+paste)
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
	io.WriteString(w, s)

}
func rawHandler(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("/raw/{pasteId}", rawHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", pasteHandler)
	router.HandleFunc("/clone/{pasteId}", cloneHandler)
	router.HandleFunc("/download/{pasteId}", downloadHandler)
	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>Try 20 years for 'forever expiry'<commit_after>package main

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

	"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"`
	TITLE  string `json:"title"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

type Page struct {
	Title    string
	Body     []byte
	Raw      string
	Home     string
	Download string
	Clone    string
}

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, title string, expiry string) []string {
	db, err := sql.Open("mysql", DATABASE)
	check(err)

	sha := hash(raw)
	query, err := db.Query("select id, title, hash, data, delkey from pastebin")
	for query.Next() {
		var id, title, hash, paste, delkey string
		err := query.Scan(&id, &title, &hash, &paste, &delkey)
		check(err)
		if hash == sha {
			url := ADDRESS + "/p/" + id
			return []string{id, title, hash, url, paste, delkey}
		}
	}
	id := generateName()
	var url string
	if lang == "" {
		url = ADDRESS + "/p/" + id
	} else {
		url = ADDRESS + "/p/" + id + "/" + lang
	}
	now := time.Now()
	var expiryTime string

	switch expiry {
	case "5 minutes":
		expiryTime = now.Add(time.Minute * 5).Format("2006-01-02 15:04:05")
		break

	case "1 hour":
		expiryTime = now.Add(time.Hour + 1).Format("2006-01-02 15:04:05")
		break

	case "1 day":
		expiryTime = now.Add(time.Hour * 24 * 1).Format("2006-01-02 15:04:05")
		break

	case "1 week":
		expiryTime = now.Add(time.Hour * 24 * 7).Format("2006-01-02 15:04:05")
		break

	case "1 month":
		expiryTime = now.Add(time.Hour * 24 * 30).Format("2006-01-02 15:04:05")
		break

	case "1 year":
		expiryTime = now.Add(time.Hour * 24 * 365).Format("2006-01-02 15:04:05")
		break

	case "forever":
		expiryTime = now.Add(time.Hour * 24 * (365 * 20)).Format("2006-01-02 15:04:05")
		break

	default:
		expiryTime = now.Add(time.Hour * 24 * (365 * 20)).Format("2006-01-02 15:04:05")
		break

	}
	delKey := uniuri.NewLen(40)
	paste := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, title, hash, data, delkey, expiry) values(?,?,?,?,?,?)")
	check(err)
	if title == "" {
		_, err = stmt.Exec(id, id, sha, paste, delKey, expiryTime)
		check(err)
	} else {
		_, err = stmt.Exec(id, html.EscapeString(title), sha, paste, delKey, expiryTime)
		check(err)
	}
	db.Close()
	return []string{id, title, 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)

	_, 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")
		title := r.FormValue("title")
		expiry := r.FormValue("expiry")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
			return
		}
		values := save(paste, lang, title, expiry)
		b := &Response{
			ID:     values[0],
			TITLE:  values[1],
			HASH:   values[2],
			URL:    values[3],
			SIZE:   len(values[4]),
			DELKEY: values[5],
		}

		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+"
")

		case "redirect":
			http.Redirect(w, r, b.URL, 301)

		default:
			w.Header().Set("Content-Type", "plain/text")
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

func highlight(s string, lang string) (string, error) {

	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,noclasses=True,", "utf-8")
	if err != nil {
		return "", err
	}
	return highlight, nil

}

func getPaste(paste string, lang string) (string, string) {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	var title, s string
	var expiry string
	err = db.QueryRow("select title, data, expiry from pastebin where id=?", param1).Scan(&title, &s, &expiry)
	check(err)
	if time.Now().Format("2006-01-02 15:04:05") > expiry {
		stmt, err := db.Prepare("delete from pastebin where id=?")
		check(err)
		_, err = stmt.Exec(param1)
		check(err)
		return "Error invalid paste", ""
	}
	db.Close()

	if err == sql.ErrNoRows {
		return "Error invalid paste", ""
	} else {
		if lang == "" {
			return html.UnescapeString(s), html.UnescapeString(title)
		} else {
			high, err := highlight(s, lang)
			check(err)
			return high, html.UnescapeString(title)

		}
	}

}

var templates = template.Must(template.ParseFiles("assets/paste.html", "assets/index.html", "assets/clone.html"))
var syntax, _ = ioutil.ReadFile("assets/syntax.html")

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s, title := getPaste(paste, lang)
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste
	if lang == "" {
		p := &Page{
			Title:    title,
			Body:     []byte(s),
			Raw:      link,
			Home:     ADDRESS,
			Download: download,
			Clone:    clone,
		}
		err := templates.ExecuteTemplate(w, "paste.html", p)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}

	} else {
		fmt.Fprintf(w, string(syntax), paste, paste, s, ADDRESS, download, link, clone)

	}
}

func cloneHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, title := getPaste(paste, "")
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste
	p := &Page{
		Title:    title,
		Body:     []byte(s),
		Raw:      link,
		Home:     ADDRESS,
		Download: download,
		Clone:    clone,
	}
	err := templates.ExecuteTemplate(w, "clone.html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

}
func downloadHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, _ := getPaste(paste, "")
	w.Header().Set("Content-Disposition", "attachment; filename="+paste)
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
	io.WriteString(w, s)

}
func rawHandler(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("/raw/{pasteId}", rawHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", pasteHandler)
	router.HandleFunc("/clone/{pasteId}", cloneHandler)
	router.HandleFunc("/download/{pasteId}", downloadHandler)
	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 main

import (
	"fmt"
	"os"
	"strings"
	"os/exec"
	"log"
	"io/ioutil"

	"github.com/drone/drone-plugin-go/plugin"
)

type Fabric struct {
	Commands  []string `json:"commands"`
}

func main() {
	workspace := plugin.Workspace{}
	vargs := Fabric{}

	plugin.Param("workspace", &workspace)
	plugin.Param("vargs", &vargs)

	// parse the parameters
	if err := plugin.Parse(); err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}

	err := ioutil.WriteFile("/tmp/ssh_key", []byte(workspace.Keys.Public), 0644)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(99)
	}

	fabfile := fmt.Sprintf("--fabfile=%s/fabfile.py", workspace.Path)

	for _, c := range vargs.Commands {
		command := fmt.Sprintf("%s %s", fabfile, c)
		fabArgs := strings.Split(command, " ")
		fabArgs = append(fabArgs, "-i", "/tmp/ssh_key")

		c := exec.Command("fab", fabArgs...)
		c.Stdout = os.Stdout
		c.Stderr = os.Stderr
		err := c.Run()
		if err != nil {
			log.Println(err)
			os.Exit(101)
		}
	}
}
<commit_msg>More debugging<commit_after>package main

import (
	"fmt"
	"os"
	"strings"
	"os/exec"
	"log"
	"io/ioutil"

	"github.com/drone/drone-plugin-go/plugin"
)

type Fabric struct {
	Commands  []string `json:"commands"`
}

func main() {
	workspace := plugin.Workspace{}
	vargs := Fabric{}

	plugin.Param("workspace", &workspace)
	plugin.Param("vargs", &vargs)

	// parse the parameters
	if err := plugin.Parse(); err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}

	err := ioutil.WriteFile("/tmp/ssh_key", []byte(workspace.Keys.Public), 0644)
	log.Println(workspace.Keys.Public)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(99)
	}

	fabfile := fmt.Sprintf("--fabfile=%s/fabfile.py", workspace.Path)

	for _, c := range vargs.Commands {
		command := fmt.Sprintf("%s %s", fabfile, c)
		fabArgs := strings.Split(command, " ")
		fabArgs = append(fabArgs, "-i", "/tmp/ssh_key")

		c := exec.Command("fab", fabArgs...)
		c.Stdout = os.Stdout
		c.Stderr = os.Stderr
		err := c.Run()
		if err != nil {
			log.Println(err)
			os.Exit(101)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/gen/route53"
)

var region = "us-east-1"

// awsString turns the pointers back to strings without needless nil checking each time
func awsString(val aws.StringValue) string {
	if val == nil {
		return ""
	}
	return *val
}

// zoneIDByName takes a record name and returns the Route53 zone ID
// TODO: handle paging
func zoneIDByName(r53 *route53.Route53, name string) (string, error) {
	resp, err := r53.ListHostedZones(&route53.ListHostedZonesRequest{})
	if err != nil {
		return "", err
	}
	c := strings.Split(name, ".")
	if len(c) < 3 {
		return "", fmt.Errorf("name must have at least one period")
	}
	name = strings.Join(c[len(c)-3:], ".")
	for _, zone := range resp.HostedZones {
		if *zone.Name == name {
			// zone.ID looks like /hostedzone/Z22CR2RGPPKRQB but we just want the last part
			c := strings.Split(*zone.ID, "/")
			if len(c) != 3 {
				return "", fmt.Errorf("problem splitting id from %s\n", *zone.ID)
			}
			return c[len(c)-1], nil
		}
	}
	return "", fmt.Errorf("zone %s not found", name)
}

// printResourceRecordSet is a pretty printer
func printResourceRecordSet(rrs route53.ResourceRecordSet) {
	enc := xml.NewEncoder(os.Stdout)
	enc.Indent("", "  ")
	enc.Encode(rrs)
	fmt.Println()
}

// addToARecordResourceRecordSet adds one or more IP addresses to the Resource Record Set
func addToARecordResourceRecordSet(r53 *route53.Route53, zoneID string, rrs route53.ResourceRecordSet, ips ...string) error {
	if len(ips) == 0 {
		return fmt.Errorf("at least one IP needs to be passed")
	}
	req := &route53.ChangeResourceRecordSetsRequest{HostedZoneID: aws.String(zoneID)}
	for _, ip := range ips {
		rrs.ResourceRecords = append(rrs.ResourceRecords, route53.ResourceRecord{Value: aws.String(ip)})
	}
	change := route53.Change{Action: aws.String("UPSERT"), ResourceRecordSet: &rrs}
	changeBatch := route53.ChangeBatch{Changes: []route53.Change{change}}
	req.ChangeBatch = &changeBatch
	resp, err := r53.ChangeResourceRecordSets(req)
	if err != nil {
		return err
	}
	log.Printf("ChangeResourceRecordSets response=%+v\n", resp)
	return nil
}

// getResourceRecordSet finds an existing resource record set matching the criteria
func getResourceRecordSet(r53 *route53.Route53, zoneID string, recordName string, recordType string, setID string) (route53.ResourceRecordSet, error) {
	zoneID, err := zoneIDByName(r53, recordName)
	if err != nil {
		return route53.ResourceRecordSet{}, err
	}
	req := route53.ListResourceRecordSetsRequest{HostedZoneID: &zoneID}
	req.StartRecordName = aws.String(recordName)
	req.StartRecordType = aws.String(recordType)
	resp, err := r53.ListResourceRecordSets(&req)
	if err != nil {
		return route53.ResourceRecordSet{}, err
	}

	for _, rrs := range resp.ResourceRecordSets {
		if *rrs.Name == recordName && *rrs.SetIdentifier == setID {
			return rrs, nil
		}
	}
	return route53.ResourceRecordSet{}, fmt.Errorf("no ResourceRecordSets found for zoneID=%s recordName=%s recordType=%s setIdentifier=%s\n", zoneID, recordName, recordType, setID)
}

func usage() {
	example := `
	Usage: r53cli [options] ipaddr <ipaddr2 ipaddr3 ...>

	options
	--
	-name="record.example.com.": record name
	-region="us-east-1": AWS region
	-setid="": record set identifier
	-type="A": record type
	-v=false: verbose

	This tool will update Route53 record sets with additional resources.

	Standard AWS environment variables are used to supply authentication credentials

`
	fmt.Println(example)
	os.Exit(1)
}

func main() {
	recordName := flag.String("name", "record.example.com", "record name")
	recordType := flag.String("type", "A", "record type")
	setID := flag.String("setid", "", "record set identifier")
	region := flag.String("region", "us-east-1", "AWS region")
	verbose := flag.Bool("v", false, "verbose")
	flag.Parse()

	ips := flag.Args()
	if len(ips) == 0 {
		usage()
	}

	auth, err := aws.EnvCreds()
	if err != nil {
		log.Fatal(err)
	}

	r53 := route53.New(auth, *region, http.DefaultClient)

	if !strings.HasSuffix(*recordName, ".") {
		*recordName += "."
	}

	zoneID, err := zoneIDByName(r53, *recordName)
	if err != nil {
		log.Fatal(err)
	}

	if *verbose {
		log.Printf("zoneID for %s is %s", *recordName, zoneID)
	}

	rrs, err := getResourceRecordSet(r53, zoneID, *recordName, *recordType, *setID)
	if err != nil {
		log.Fatal(err)
	}

	if *verbose {
		printResourceRecordSet(rrs)
	}

	err = addToARecordResourceRecordSet(r53, zoneID, rrs, ips...)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>remove unused func<commit_after>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/gen/route53"
)

var region = "us-east-1"

// zoneIDByName takes a record name and returns the Route53 zone ID
// TODO: handle paging
func zoneIDByName(r53 *route53.Route53, name string) (string, error) {
	resp, err := r53.ListHostedZones(&route53.ListHostedZonesRequest{})
	if err != nil {
		return "", err
	}
	c := strings.Split(name, ".")
	if len(c) < 3 {
		return "", fmt.Errorf("name must have at least one period")
	}
	name = strings.Join(c[len(c)-3:], ".")
	for _, zone := range resp.HostedZones {
		if *zone.Name == name {
			// zone.ID looks like /hostedzone/Z22CR2RGPPKRQB but we just want the last part
			c := strings.Split(*zone.ID, "/")
			if len(c) != 3 {
				return "", fmt.Errorf("problem splitting id from %s\n", *zone.ID)
			}
			return c[len(c)-1], nil
		}
	}
	return "", fmt.Errorf("zone %s not found", name)
}

// printResourceRecordSet is a pretty printer
func printResourceRecordSet(rrs route53.ResourceRecordSet) {
	enc := xml.NewEncoder(os.Stdout)
	enc.Indent("", "  ")
	enc.Encode(rrs)
	fmt.Println()
}

// addToARecordResourceRecordSet adds one or more IP addresses to the Resource Record Set
func addToARecordResourceRecordSet(r53 *route53.Route53, zoneID string, rrs route53.ResourceRecordSet, ips ...string) error {
	if len(ips) == 0 {
		return fmt.Errorf("at least one IP needs to be passed")
	}
	req := &route53.ChangeResourceRecordSetsRequest{HostedZoneID: aws.String(zoneID)}
	for _, ip := range ips {
		rrs.ResourceRecords = append(rrs.ResourceRecords, route53.ResourceRecord{Value: aws.String(ip)})
	}
	change := route53.Change{Action: aws.String("UPSERT"), ResourceRecordSet: &rrs}
	changeBatch := route53.ChangeBatch{Changes: []route53.Change{change}}
	req.ChangeBatch = &changeBatch
	resp, err := r53.ChangeResourceRecordSets(req)
	if err != nil {
		return err
	}
	log.Printf("ChangeResourceRecordSets response=%+v\n", resp)
	return nil
}

// getResourceRecordSet finds an existing resource record set matching the criteria
func getResourceRecordSet(r53 *route53.Route53, zoneID string, recordName string, recordType string, setID string) (route53.ResourceRecordSet, error) {
	zoneID, err := zoneIDByName(r53, recordName)
	if err != nil {
		return route53.ResourceRecordSet{}, err
	}
	req := route53.ListResourceRecordSetsRequest{HostedZoneID: &zoneID}
	req.StartRecordName = aws.String(recordName)
	req.StartRecordType = aws.String(recordType)
	resp, err := r53.ListResourceRecordSets(&req)
	if err != nil {
		return route53.ResourceRecordSet{}, err
	}

	for _, rrs := range resp.ResourceRecordSets {
		if *rrs.Name == recordName && *rrs.SetIdentifier == setID {
			return rrs, nil
		}
	}
	return route53.ResourceRecordSet{}, fmt.Errorf("no ResourceRecordSets found for zoneID=%s recordName=%s recordType=%s setIdentifier=%s\n", zoneID, recordName, recordType, setID)
}

func usage() {
	example := `
	Usage: r53tool [options] ipaddr <ipaddr2 ipaddr3 ...>

	options
	--
	-name="record.example.com.": record name
	-region="us-east-1": AWS region
	-setid="": record set identifier
	-type="A": record type
	-v=false: verbose

	This tool will update Route53 record sets with additional resources.

	Standard AWS environment variables are used to supply authentication credentials

`
	fmt.Println(example)
	os.Exit(1)
}

func main() {
	recordName := flag.String("name", "record.example.com", "record name")
	recordType := flag.String("type", "A", "record type")
	setID := flag.String("setid", "", "record set identifier")
	region := flag.String("region", "us-east-1", "AWS region")
	verbose := flag.Bool("v", false, "verbose")
	flag.Parse()

	ips := flag.Args()
	if len(ips) == 0 {
		usage()
	}

	auth, err := aws.EnvCreds()
	if err != nil {
		log.Fatal(err)
	}

	r53 := route53.New(auth, *region, http.DefaultClient)

	if !strings.HasSuffix(*recordName, ".") {
		*recordName += "."
	}

	zoneID, err := zoneIDByName(r53, *recordName)
	if err != nil {
		log.Fatal(err)
	}

	if *verbose {
		log.Printf("zoneID for %s is %s", *recordName, zoneID)
	}

	rrs, err := getResourceRecordSet(r53, zoneID, *recordName, *recordType, *setID)
	if err != nil {
		log.Fatal(err)
	}

	if *verbose {
		printResourceRecordSet(rrs)
	}

	err = addToARecordResourceRecordSet(r53, zoneID, rrs, ips...)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/phrase/phraseapp-client/Godeps/_workspace/src/github.com/dynport/dgtk/cli"
	"github.com/phrase/phraseapp-client/Godeps/_workspace/src/github.com/phrase/phraseapp-go/phraseapp"
)

func main() {
	Run()
}

func Run() {
	validateVersion()

	cfg, err := phraseapp.ReadConfig()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(2)
	}

	r, err := router(cfg)
	if err != nil {
		printErr(err)
		os.Exit(3)
	}

	switch err := r.RunWithArgs(); err {
	case cli.ErrorHelpRequested, cli.ErrorNoRoute:
		os.Exit(1)
	case nil:
		os.Exit(0)
	default:
		printErr(err)
		os.Exit(1)
	}
}

const PHRASEAPP_VERSION_TMP_FILE = "/tmp/.phraseapp.version"

func validateVersion() {
	var version string
	stat, err := os.Stat(PHRASEAPP_VERSION_TMP_FILE)
	if PHRASEAPP_CLIENT_VERSION == "test" {
		fmt.Fprintf(os.Stderr, "You're running a development version of the PhraseApp CLI client!\n\n")
		return
	} else if os.IsNotExist(err) || time.Since(stat.ModTime()) > time.Hour {
		// fetch new version, if not done so or over an hour ago
		version, err = getCurrentVersion()
		if err == nil { // persist the version for the next hour
			err = ioutil.WriteFile(PHRASEAPP_VERSION_TMP_FILE, []byte(version), 0600)
		}
	} else if err == nil {
		// otherwise load the version (fetched less than an hour ago) from the temp file
		var buf []byte
		buf, err = ioutil.ReadFile(PHRASEAPP_VERSION_TMP_FILE)
		if err == nil {
			version = string(buf)
		}
	}

	if err == nil && version != PHRASEAPP_CLIENT_VERSION {
		fmt.Fprintf(os.Stderr, "Please consider updating the PhraseApp CLI client (%s < %s)\nSee https://phraseapp.com/en/cli\n\n", PHRASEAPP_CLIENT_VERSION, version)
	}
}

func getCurrentVersion() (string, error) {
	req, err := http.NewRequest("HEAD", "https://github.com/phrase/phraseapp-client/releases/latest", nil)
	if err != nil {
		return "", err
	}

	transport := http.Transport{}
	resp, err := transport.RoundTrip(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close() // body is empty as it is only a HEAD request

	if resp.StatusCode != 302 {
		return "", fmt.Errorf("failed to request the file")
	}

	url, err := resp.Location()
	if err != nil {
		return "", err
	}

	segments := strings.Split(url.Path, "/")
	for i := len(segments) - 1; i >= 0; i-- {
		if segments[i] != "" {
			return segments[i], nil
		}
	}
	return "", fmt.Errorf("no valid version segment found")
}
<commit_msg>set ClientVersion in library<commit_after>package main

import (
	"os"

	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/phrase/phraseapp-client/Godeps/_workspace/src/github.com/dynport/dgtk/cli"
	"github.com/phrase/phraseapp-client/Godeps/_workspace/src/github.com/phrase/phraseapp-go/phraseapp"
)

func main() {
	Run()
}

func Run() {
	phraseapp.ClientVersion = PHRASEAPP_CLIENT_VERSION
	validateVersion()

	cfg, err := phraseapp.ReadConfig()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(2)
	}

	r, err := router(cfg)
	if err != nil {
		printErr(err)
		os.Exit(3)
	}

	switch err := r.RunWithArgs(); err {
	case cli.ErrorHelpRequested, cli.ErrorNoRoute:
		os.Exit(1)
	case nil:
		os.Exit(0)
	default:
		printErr(err)
		os.Exit(1)
	}
}

const PHRASEAPP_VERSION_TMP_FILE = "/tmp/.phraseapp.version"

func validateVersion() {
	var version string
	stat, err := os.Stat(PHRASEAPP_VERSION_TMP_FILE)
	if PHRASEAPP_CLIENT_VERSION == "test" {
		fmt.Fprintf(os.Stderr, "You're running a development version of the PhraseApp CLI client!\n\n")
		return
	} else if os.IsNotExist(err) || time.Since(stat.ModTime()) > time.Hour {
		// fetch new version, if not done so or over an hour ago
		version, err = getCurrentVersion()
		if err == nil { // persist the version for the next hour
			err = ioutil.WriteFile(PHRASEAPP_VERSION_TMP_FILE, []byte(version), 0600)
		}
	} else if err == nil {
		// otherwise load the version (fetched less than an hour ago) from the temp file
		var buf []byte
		buf, err = ioutil.ReadFile(PHRASEAPP_VERSION_TMP_FILE)
		if err == nil {
			version = string(buf)
		}
	}

	if err == nil && version != PHRASEAPP_CLIENT_VERSION {
		fmt.Fprintf(os.Stderr, "Please consider updating the PhraseApp CLI client (%s < %s)\nSee https://phraseapp.com/en/cli\n\n", PHRASEAPP_CLIENT_VERSION, version)
	}
}

func getCurrentVersion() (string, error) {
	req, err := http.NewRequest("HEAD", "https://github.com/phrase/phraseapp-client/releases/latest", nil)
	if err != nil {
		return "", err
	}

	transport := http.Transport{}
	resp, err := transport.RoundTrip(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close() // body is empty as it is only a HEAD request

	if resp.StatusCode != 302 {
		return "", fmt.Errorf("failed to request the file")
	}

	url, err := resp.Location()
	if err != nil {
		return "", err
	}

	segments := strings.Split(url.Path, "/")
	for i := len(segments) - 1; i >= 0; i-- {
		if segments[i] != "" {
			return segments[i], nil
		}
	}
	return "", fmt.Errorf("no valid version segment found")
}
<|endoftext|>