<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"crypto/tls"
	"html/template"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/daaku/go.httpgzip"
	"golang.org/x/crypto/acme/autocert"
)

//go:generate go-bindata ./static/... ./views
var templates = template.New("").Funcs(template.FuncMap{"add": func(a, b int) int { return a + b }})

var hostname_whitelist = []string{
	"www.sadbox.org", "sadbox.org",
	"www.sadbox.es", "sadbox.es",
	"www.geekwhack.org", "geekwhack.org",
}

type WebsiteName struct {
	Title, Brand string
}

type TemplateContext struct {
	Geekhack *Geekhack
	Webname  *WebsiteName
}

func NewContext(r *http.Request) *TemplateContext {
	title := "sadbox \u00B7 org"
	host := "sadbox.org"
	for _, v := range hostname_whitelist {
		if v == r.Host {
			title = strings.Replace(r.Host, ".", " \u00B7 ", -1)
			host = r.Host
			break
		}
	}
	return &TemplateContext{
		Webname: &WebsiteName{
			title,
			host,
		},
	}
}

func serveStatic(filename string) {
	relPath, err := filepath.Rel("static", filename)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Serving Static File:", relPath)

	handler := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Cache-Control", "max-age=31536000")
		asset, err := Asset(filename)
		if err != nil {
			http.NotFound(w, r)
		}
		assetInfo, err := AssetInfo(filename)
		if err != nil {
			http.NotFound(w, r)
		}
		http.ServeContent(w, r, filename, assetInfo.ModTime(), bytes.NewReader(asset))
	}

	http.HandleFunc(path.Join("/", relPath), handler)
}

func CatchPanic(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r := recover(); r != nil {
				log.Printf("Recovered from panic: %v", r)
				http.Error(w, "Something went wrong!", http.StatusInternalServerError)
			}
		}()

		handler.ServeHTTP(w, r)
	})
}

func SendToHTTPS(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Connection", "close")
	http.Redirect(w, r, "https://"+r.Host+r.RequestURI, http.StatusMovedPermanently)
}

func RedirectToHTTPS(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		host, _, err := net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			SendToHTTPS(w, r)
			return
		}

		ip := net.ParseIP(host)
		if ip == nil {
			SendToHTTPS(w, r)
			return
		}

		if !ip.IsLoopback() {
			SendToHTTPS(w, r)
			return
		}

		handler.ServeHTTP(w, r)
	})
}

func AddHeaders(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Cache-Control", "max-age=120")
		w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
		w.Header().Set("Content-Security-Policy", "default-src 'self'; object-src 'none'; frame-ancestors 'none'")
		w.Header().Set("X-Frame-Options", "DENY")
		w.Header().Set("X-Content-Type-Options", "nosniff")
		w.Header().Set("Referrer-Policy", "same-origin")
		w.Header().Set("X-XSS-Protection", "1; mode=block")
		handler.ServeHTTP(w, r)
	})
}

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		remoteHost := r.Header.Get("X-Forwarded-For")
		if remoteHost == "" {
			remoteHost = r.RemoteAddr
		}
		log.Printf("%s %s %s", remoteHost, r.Method, r.URL)
		handler.ServeHTTP(w, r)
	})
}

func getFiles(dir string) []string {
	files, err := AssetDir(dir)
	if err != nil {
		return []string{dir}
	}

	var outFiles []string
	for _, filename := range files {
		outFiles = append(outFiles, getFiles(path.Join(dir, filename))...)
	}
	return outFiles
}

type sessionKeys struct {
	keys [][32]byte
	buf  []byte
	tls  *tls.Config
}

func NewSessionKeys(tls *tls.Config) *sessionKeys {
	sk := &sessionKeys{
		buf: make([]byte, 32),
		tls: tls,
	}
	sk.addNewKey()
	return sk
}

func (sk *sessionKeys) addNewKey() {
	rand.Read(sk.buf)
	newKey := [32]byte{}
	copy(newKey[:], sk.buf)
	sk.keys = append([][32]byte{newKey}, sk.keys...)
	if len(sk.keys) > 4 {
		sk.keys = sk.keys[:5]
	}
	log.Println("Rotating Session Keys")
	sk.tls.SetSessionTicketKeys(sk.keys)
}

func (sk *sessionKeys) Spin() {
	for range time.Tick(24 * time.Hour) {
		sk.addNewKey()
	}
}

func main() {
	log.Println("Starting sadbox.org")

	for _, filename := range getFiles("views") {
		template.Must(templates.Parse(string(MustAsset(filename))))
	}

	// static files
	for _, filename := range getFiles("static") {
		serveStatic(filename)
	}

	geekhack, err := NewGeekhack()
	if err != nil {
		log.Fatal(err)
	}
	defer geekhack.db.Close()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/" {
			http.NotFound(w, r)
			return
		}

		ctx := NewContext(r)
		if err := templates.ExecuteTemplate(w, "main", ctx); err != nil {
			log.Println(err)
		}
	})

	// Geekhack stats! the geekhack struct will handle the routing to sub-things
	http.Handle("/geekhack/", geekhack)
	// Redirects to the right URL so I don't break old links
	http.Handle("/ghstats", http.RedirectHandler("/geekhack/", http.StatusMovedPermanently))
	http.Handle("/geekhack", http.RedirectHandler("/geekhack/", http.StatusMovedPermanently))

	localhost_znc, err := url.Parse("http://127.0.0.1:6698")
	if err != nil {
		log.Fatal(err)
	}
	http.Handle("/znc/", httputil.NewSingleHostReverseProxy(localhost_znc))
	http.Handle("/znc", http.RedirectHandler("/znc/", http.StatusMovedPermanently))

	servemux := httpgzip.NewHandler(
		CatchPanic(
			Log(
				AddHeaders(http.DefaultServeMux))))

	httpSrv := &http.Server{
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
		Handler:      RedirectToHTTPS(servemux),
	}
	go func() { log.Fatal(httpSrv.ListenAndServe()) }()

	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		Cache:      autocert.DirCache("/home/sadbox-web/cert-cache"),
		HostPolicy: autocert.HostWhitelist(hostname_whitelist...),
		Email:      "blue6249@gmail.com",
		ForceRSA:   true,
	}

	var certs []tls.Certificate
	sadboxCert, err := tls.LoadX509KeyPair("/home/sadbox-web/cert-cache/sadbox.org",
		"/home/sadbox-web/cert-cache/sadbox.org")

	if err == nil {
		certs = append(certs, sadboxCert)
	}

	tlsconfig := &tls.Config{
		PreferServerCipherSuites: true,
		GetCertificate:           m.GetCertificate,
		Certificates:             certs,
	}

	go NewSessionKeys(tlsconfig).Spin()

	server := &http.Server{
		Addr:         ":https",
		Handler:      servemux,
		TLSConfig:    tlsconfig,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
	}
	log.Fatal(server.ListenAndServeTLS("", ""))
}
<commit_msg>Move to using go-bindata-assetfs<commit_after>package main

import (
	"crypto/rand"
	"crypto/tls"
	"html/template"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/daaku/go.httpgzip"
	assetfs "github.com/elazarl/go-bindata-assetfs"
	"golang.org/x/crypto/acme/autocert"
)

//go:generate go-bindata ./views ./static/...
var templates = template.New("").Funcs(template.FuncMap{"add": func(a, b int) int { return a + b }})

var hostname_whitelist = []string{
	"www.sadbox.org", "sadbox.org",
	"www.sadbox.es", "sadbox.es",
	"www.geekwhack.org", "geekwhack.org",
}

type WebsiteName struct {
	Title, Brand string
}

type TemplateContext struct {
	Geekhack *Geekhack
	Webname  *WebsiteName
}

func NewContext(r *http.Request) *TemplateContext {
	title := "sadbox \u00B7 org"
	host := "sadbox.org"
	for _, v := range hostname_whitelist {
		if v == r.Host {
			title = strings.Replace(r.Host, ".", " \u00B7 ", -1)
			host = r.Host
			break
		}
	}
	return &TemplateContext{
		Webname: &WebsiteName{
			title,
			host,
		},
	}
}

func CatchPanic(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r := recover(); r != nil {
				log.Printf("Recovered from panic: %v", r)
				http.Error(w, "Something went wrong!", http.StatusInternalServerError)
			}
		}()

		handler.ServeHTTP(w, r)
	})
}

func SendToHTTPS(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Connection", "close")
	http.Redirect(w, r, "https://"+r.Host+r.RequestURI, http.StatusMovedPermanently)
}

func RedirectToHTTPS(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		host, _, err := net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			SendToHTTPS(w, r)
			return
		}

		ip := net.ParseIP(host)
		if ip == nil {
			SendToHTTPS(w, r)
			return
		}

		if !ip.IsLoopback() {
			SendToHTTPS(w, r)
			return
		}

		handler.ServeHTTP(w, r)
	})
}

func AddHeaders(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Cache-Control", "max-age=120")
		w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
		w.Header().Set("Content-Security-Policy", "default-src 'self'; object-src 'none'; frame-ancestors 'none'")
		w.Header().Set("X-Frame-Options", "DENY")
		w.Header().Set("X-Content-Type-Options", "nosniff")
		w.Header().Set("Referrer-Policy", "no-referrer, same-origin")
		w.Header().Set("X-XSS-Protection", "1; mode=block")
		handler.ServeHTTP(w, r)
	})
}

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		remoteHost := r.Header.Get("X-Forwarded-For")
		if remoteHost == "" {
			remoteHost = r.RemoteAddr
		}
		log.Printf("%s %s %s", remoteHost, r.Method, r.URL)
		handler.ServeHTTP(w, r)
	})
}

func getFiles(dir string) []string {
	files, err := AssetDir(dir)
	if err != nil {
		return []string{dir}
	}

	var outFiles []string
	for _, filename := range files {
		outFiles = append(outFiles, getFiles(path.Join(dir, filename))...)
	}
	return outFiles
}

type sessionKeys struct {
	keys [][32]byte
	buf  []byte
	tls  *tls.Config
}

func NewSessionKeys(tls *tls.Config) *sessionKeys {
	sk := &sessionKeys{
		buf: make([]byte, 32),
		tls: tls,
	}
	sk.addNewKey()
	return sk
}

func (sk *sessionKeys) addNewKey() {
	rand.Read(sk.buf)
	newKey := [32]byte{}
	copy(newKey[:], sk.buf)
	sk.keys = append([][32]byte{newKey}, sk.keys...)
	if len(sk.keys) > 4 {
		sk.keys = sk.keys[:5]
	}
	log.Println("Rotating Session Keys")
	sk.tls.SetSessionTicketKeys(sk.keys)
}

func (sk *sessionKeys) Spin() {
	for range time.Tick(24 * time.Hour) {
		sk.addNewKey()
	}
}

func main() {
	log.Println("Starting sadbox.org")

	for _, filename := range getFiles("views") {
		template.Must(templates.Parse(string(MustAsset(filename))))
	}

	geekhack, err := NewGeekhack()
	if err != nil {
		log.Fatal(err)
	}
	defer geekhack.db.Close()

	staticFileServer := http.FileServer(
		&assetfs.AssetFS{Asset: Asset,
			AssetDir:  AssetDir,
			AssetInfo: AssetInfo,
			Prefix:    "static"})
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/" {
			ctx := NewContext(r)
			if err := templates.ExecuteTemplate(w, "main", ctx); err != nil {
				log.Println(err)
			}
		} else {
			staticFileServer.ServeHTTP(w, r)
		}
	})

	// Geekhack stats! the geekhack struct will handle the routing to sub-things
	http.Handle("/geekhack/", geekhack)
	// Redirects to the right URL so I don't break old links
	http.Handle("/ghstats", http.RedirectHandler("/geekhack/", http.StatusMovedPermanently))
	http.Handle("/geekhack", http.RedirectHandler("/geekhack/", http.StatusMovedPermanently))

	localhost_znc, err := url.Parse("http://127.0.0.1:6698")
	if err != nil {
		log.Fatal(err)
	}
	http.Handle("/znc/", httputil.NewSingleHostReverseProxy(localhost_znc))
	http.Handle("/znc", http.RedirectHandler("/znc/", http.StatusMovedPermanently))

	servemux := httpgzip.NewHandler(
		CatchPanic(
			Log(
				AddHeaders(http.DefaultServeMux))))

	httpSrv := &http.Server{
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
		Handler:      RedirectToHTTPS(servemux),
	}
	go func() { log.Fatal(httpSrv.ListenAndServe()) }()

	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		Cache:      autocert.DirCache("/home/sadbox-web/cert-cache"),
		HostPolicy: autocert.HostWhitelist(hostname_whitelist...),
		Email:      "blue6249@gmail.com",
		ForceRSA:   true,
	}

	var certs []tls.Certificate
	sadboxCert, err := tls.LoadX509KeyPair("/home/sadbox-web/cert-cache/sadbox.org",
		"/home/sadbox-web/cert-cache/sadbox.org")

	if err == nil {
		certs = append(certs, sadboxCert)
	}

	tlsconfig := &tls.Config{
		PreferServerCipherSuites: true,
		GetCertificate:           m.GetCertificate,
		Certificates:             certs,
	}

	go NewSessionKeys(tlsconfig).Spin()

	server := &http.Server{
		Addr:         ":https",
		Handler:      servemux,
		TLSConfig:    tlsconfig,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
	}
	log.Fatal(server.ListenAndServeTLS("", ""))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	_ "net/http/pprof"
	"time"

	"crypto/tls"
	"crypto/x509"
	"github.com/prometheus/client_golang/prometheus"
	"io/ioutil"
)

const (
	namespace = "elasticsearch"
	indexHTML = `

			<title>Elasticsearch Exporter</title>

Elasticsearch Exporter

			Metrics

	`
)

func main() {
	var (
		listenAddress      = flag.String("web.listen-address", ":9108", "Address to listen on for web interface and telemetry.")
		metricsPath        = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		esHostname         = flag.String("es.hostname", "localhost", "hostname of an Elasticsearch node, where client http is enabled.")
		esProtocol         = flag.String("es.protocol", "http", "http/https protocol of an Elasticsearch node")
		esPort             = flag.String("es.port", "9200", "Port of an Elasticsearch node 9200 or 443")
		esUser             = flag.String("es.user", "", "HTTP username for basic auth of an Elasticsearch node.")
		esPassword         = flag.String("es.password", "", "HTTP password for basic auth of an Elasticsearch node.")
		esTimeout          = flag.Duration("es.timeout", 5*time.Second, "Timeout for trying to get stats from Elasticsearch.")
		esAllNodes         = flag.Bool("es.all", false, "Export stats for all nodes in the cluster.")
		esCA               = flag.String("es.ca", "", "Path to PEM file that conains trusted CAs for the Elasticsearch connection.")
		esClientPrivateKey = flag.String("es.client-private-key", "", "Path to PEM file that conains the private key for client auth when connecting to Elasticsearch.")
		esClientCert       = flag.String("es.client-cert", "", "Path to PEM file that conains the corresponding cert for the private key to connect to Elasticsearch.")
	)
	flag.Parse()

	var authString  string

	if *esUser != "" {
		authString = *esUser + ":" + *esPassword + "@"
	} else {
		authString = ""
	}

	nodesStatsURI := *esProtocol + "://" + authString + *esHostname + ":" + *esPort + "/_nodes/_local/stats"
	if *esAllNodes {
		nodesStatsURI = *esProtocol + "://" + authString + *esHostname + ":" + *esPort + "/_nodes/stats"
	}
	clusterHealthURI := *esProtocol + "://" + *esHostname + ":" + *esPort + "/_cluster/health"

	exporter := NewExporter(nodesStatsURI, clusterHealthURI, *esTimeout, *esAllNodes, createElasticSearchTlsConfig(*esCA, *esClientCert, *esClientPrivateKey))
	prometheus.MustRegister(exporter)

	log.Println("Starting Server:", *listenAddress)
	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(fmt.Sprintf(indexHTML, *metricsPath)))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}

func createElasticSearchTlsConfig(pemFile, pemCertFile, pemPrivateKeyFile string) *tls.Config {
	if len(pemFile) <= 0 {
		return nil
	}
	rootCerts, err := loadCertificatesFrom(pemFile)
	if err != nil {
		log.Fatalf("Couldn't load root certificate from %s. Got %s.", pemFile, err)
	}
	if len(pemCertFile) > 0 && len(pemPrivateKeyFile) > 0 {
		clientPrivateKey, err := loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile)
		if err != nil {
			log.Fatalf("Couldn't setup client authentication. Got %s.", err)
		}
		return &tls.Config{
			RootCAs:      rootCerts,
			Certificates: []tls.Certificate{*clientPrivateKey},
		}
	} else {
		return &tls.Config{
			RootCAs: rootCerts,
		}
	}
}

func loadCertificatesFrom(pemFile string) (*x509.CertPool, error) {
	caCert, err := ioutil.ReadFile(pemFile)
	if err != nil {
		return nil, err
	}
	certificates := x509.NewCertPool()
	certificates.AppendCertsFromPEM(caCert)
	return certificates, nil
}

func loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile string) (*tls.Certificate, error) {
	privateKey, err := tls.LoadX509KeyPair(pemCertFile, pemPrivateKeyFile)
	if err != nil {
		return nil, err
	}
	return &privateKey, nil
}
<commit_msg>check for empty password<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	_ "net/http/pprof"
	"time"

	"crypto/tls"
	"crypto/x509"
	"github.com/prometheus/client_golang/prometheus"
	"io/ioutil"
)

const (
	namespace = "elasticsearch"
	indexHTML = `

			<title>Elasticsearch Exporter</title>

Elasticsearch Exporter

			Metrics

	`
)

func main() {
	var (
		listenAddress      = flag.String("web.listen-address", ":9108", "Address to listen on for web interface and telemetry.")
		metricsPath        = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		esHostname         = flag.String("es.hostname", "localhost", "hostname of an Elasticsearch node, where client http is enabled.")
		esProtocol         = flag.String("es.protocol", "http", "http/https protocol of an Elasticsearch node")
		esPort             = flag.String("es.port", "9200", "Port of an Elasticsearch node 9200 or 443")
		esUser             = flag.String("es.user", "", "HTTP username for basic auth of an Elasticsearch node.")
		esPassword         = flag.String("es.password", "", "HTTP password for basic auth of an Elasticsearch node.")
		esTimeout          = flag.Duration("es.timeout", 5*time.Second, "Timeout for trying to get stats from Elasticsearch.")
		esAllNodes         = flag.Bool("es.all", false, "Export stats for all nodes in the cluster.")
		esCA               = flag.String("es.ca", "", "Path to PEM file that conains trusted CAs for the Elasticsearch connection.")
		esClientPrivateKey = flag.String("es.client-private-key", "", "Path to PEM file that conains the private key for client auth when connecting to Elasticsearch.")
		esClientCert       = flag.String("es.client-cert", "", "Path to PEM file that conains the corresponding cert for the private key to connect to Elasticsearch.")
	)
	flag.Parse()

	var authString  string

	if *esUser != "" && *esPassword != "" {
		authString = *esUser + ":" + *esPassword + "@"
	} else {
		authString = ""
	}

	nodesStatsURI := *esProtocol + "://" + authString + *esHostname + ":" + *esPort + "/_nodes/_local/stats"
	if *esAllNodes {
		nodesStatsURI = *esProtocol + "://" + authString + *esHostname + ":" + *esPort + "/_nodes/stats"
	}
	clusterHealthURI := *esProtocol + "://" + *esHostname + ":" + *esPort + "/_cluster/health"

	exporter := NewExporter(nodesStatsURI, clusterHealthURI, *esTimeout, *esAllNodes, createElasticSearchTlsConfig(*esCA, *esClientCert, *esClientPrivateKey))
	prometheus.MustRegister(exporter)

	log.Println("Starting Server:", *listenAddress)
	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(fmt.Sprintf(indexHTML, *metricsPath)))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}

func createElasticSearchTlsConfig(pemFile, pemCertFile, pemPrivateKeyFile string) *tls.Config {
	if len(pemFile) <= 0 {
		return nil
	}
	rootCerts, err := loadCertificatesFrom(pemFile)
	if err != nil {
		log.Fatalf("Couldn't load root certificate from %s. Got %s.", pemFile, err)
	}
	if len(pemCertFile) > 0 && len(pemPrivateKeyFile) > 0 {
		clientPrivateKey, err := loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile)
		if err != nil {
			log.Fatalf("Couldn't setup client authentication. Got %s.", err)
		}
		return &tls.Config{
			RootCAs:      rootCerts,
			Certificates: []tls.Certificate{*clientPrivateKey},
		}
	} else {
		return &tls.Config{
			RootCAs: rootCerts,
		}
	}
}

func loadCertificatesFrom(pemFile string) (*x509.CertPool, error) {
	caCert, err := ioutil.ReadFile(pemFile)
	if err != nil {
		return nil, err
	}
	certificates := x509.NewCertPool()
	certificates.AppendCertsFromPEM(caCert)
	return certificates, nil
}

func loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile string) (*tls.Certificate, error) {
	privateKey, err := tls.LoadX509KeyPair(pemCertFile, pemPrivateKeyFile)
	if err != nil {
		return nil, err
	}
	return &privateKey, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/bodymindarts/delmo/delmo"
)

var Version = "(dev)"

func main() {
	os.Exit(Run(os.Args[1:]))
}

var printfStdOut = func(format string, args ...interface{}) {
	fmt.Fprintf(os.Stdout, format, args...)
}

var printfStdErr = func(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
}

func Run(args []string) int {
	flags := flag.FlagSet{}

	for _, arg := range args {
		if arg == "-v" || arg == "--version" || arg == "version" {
			printfStdOut("delmo-v%s", Version)
			return 0
		}
	}

	var delmoFile, machine string
	var onlyBuildTask bool
	flags.StringVar(&delmoFile, "f", "delmo.yml", "Path to the delmo.yml file.")
	flags.StringVar(&machine, "m", "default", "The docker-machine to use.")
	flags.BoolVar(&onlyBuildTask, "only-build-task", false, "Only build the task_image. All othe images must be available to via pull.")
	if err := flags.Parse(args); err != nil {
		printfStdErr("Error parsing arguments\n%s", err)
		return 2
	}

	hostIp, err := setupDockerMachine(machine)
	if err != nil {
		printfStdErr("Error setting up environment\n%s", err)
		return 2
	}

	config, err := delmo.LoadConfig(delmoFile)
	if err != nil {
		printfStdErr("Error reading configuration\n%s", err)
		return 2
	}

	config.Suite.OnlyBuildTask = onlyBuildTask

	globalTaskEnvironment := []string{fmt.Sprintf("DOCKER_HOST_IP=%s", hostIp)}
	os.Setenv("DOCKER_HOST_IP", hostIp)
	suite, err := delmo.NewSuite(config, globalTaskEnvironment)
	if err != nil {
		printfStdErr("Could not initialize suite %s")
		return 2
	}
	result := suite.Run()
	return result
}

func Usage() string {
	helpText := `
Usage: delmo test [options]

  Run a test :-)
`
	return strings.TrimSpace(helpText)
}

func setupDockerMachine(machineName string) (string, error) {
	// get environment variables
	cmd := exec.Command("docker-machine", "env", machineName, "--shell", "sh")
	output, err := cmd.Output()
	if err != nil {
		return "", fmt.Errorf("Couldn't export environment from docker-machine\n%s", err)
	}
	// set each variable of form: export DOCKER_HOST="tcp://192.168.99.100:2376"
	for _, l := range strings.Split(string(output), "\n") {
		if strings.HasPrefix(l, "export ") {
			assignment := strings.Split(strings.TrimPrefix(l, "export "), "=")
			key := assignment[0]
			value := strings.Replace(assignment[1], "\"", "", -1)
			os.Setenv(key, value)
		}
	}

	cmd = exec.Command("docker-machine", "ip", machineName)
	output, err = cmd.Output()
	if err != nil {
		return "", fmt.Errorf("Couldn't read docker host ip from docker-machine\n%s", err)
	}

	return strings.TrimSpace(string(output)), nil
}
<commit_msg>Remove dead code<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/bodymindarts/delmo/delmo"
)

var Version = "(dev)"

func main() {
	os.Exit(Run(os.Args[1:]))
}

var printfStdOut = func(format string, args ...interface{}) {
	fmt.Fprintf(os.Stdout, format, args...)
}

var printfStdErr = func(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
}

func Run(args []string) int {
	flags := flag.FlagSet{}

	for _, arg := range args {
		if arg == "-v" || arg == "--version" || arg == "version" {
			printfStdOut("delmo-v%s", Version)
			return 0
		}
	}

	var delmoFile, machine string
	var onlyBuildTask bool
	flags.StringVar(&delmoFile, "f", "delmo.yml", "Path to the delmo.yml file.")
	flags.StringVar(&machine, "m", "default", "The docker-machine to use.")
	flags.BoolVar(&onlyBuildTask, "only-build-task", false, "Only build the task_image. All othe images must be available to via pull.")
	if err := flags.Parse(args); err != nil {
		printfStdErr("Error parsing arguments\n%s", err)
		return 2
	}

	hostIp, err := setupDockerMachine(machine)
	if err != nil {
		printfStdErr("Error setting up environment\n%s", err)
		return 2
	}

	config, err := delmo.LoadConfig(delmoFile)
	if err != nil {
		printfStdErr("Error reading configuration\n%s", err)
		return 2
	}

	config.Suite.OnlyBuildTask = onlyBuildTask

	globalTaskEnvironment := []string{fmt.Sprintf("DOCKER_HOST_IP=%s", hostIp)}
	os.Setenv("DOCKER_HOST_IP", hostIp)
	suite, err := delmo.NewSuite(config, globalTaskEnvironment)
	if err != nil {
		printfStdErr("Could not initialize suite %s")
		return 2
	}
	result := suite.Run()
	return result
}

func setupDockerMachine(machineName string) (string, error) {
	// get environment variables
	cmd := exec.Command("docker-machine", "env", machineName, "--shell", "sh")
	output, err := cmd.Output()
	if err != nil {
		return "", fmt.Errorf("Couldn't export environment from docker-machine\n%s", err)
	}
	// set each variable of form: export DOCKER_HOST="tcp://192.168.99.100:2376"
	for _, l := range strings.Split(string(output), "\n") {
		if strings.HasPrefix(l, "export ") {
			assignment := strings.Split(strings.TrimPrefix(l, "export "), "=")
			key := assignment[0]
			value := strings.Replace(assignment[1], "\"", "", -1)
			os.Setenv(key, value)
		}
	}

	cmd = exec.Command("docker-machine", "ip", machineName)
	output, err = cmd.Output()
	if err != nil {
		return "", fmt.Errorf("Couldn't read docker host ip from docker-machine\n%s", err)
	}

	return strings.TrimSpace(string(output)), nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/jcoene/gologger"
	"net"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strconv"
	"time"
)

type Packet struct {
	Bucket   string
	Value    string
	Modifier string
	Sampling float32
}

var log *logger.Logger

var (
	serviceAddress = flag.String("address", "0.0.0.0:8125", "UDP service address")
	libratoUser    = flag.String("user", "", "Librato Username")
	libratoToken   = flag.String("token", "", "Librato API Token")
	flushInterval  = flag.Int64("flush", 30, "Flush Interval (seconds)")
	debug          = flag.Bool("debug", false, "Enable Debugging")
)

var (
	In       = make(chan Packet, 10000)
	counters = make(map[string]int64)
	timers   = make(map[string][]float64)
	gauges   = make(map[string]float64)
)

type Measurement struct {
	Counters []Counter     `json:"counters"`
	Gauges   []interface{} `json:"gauges"`
}

func (m *Measurement) Count() int {
	return (len(m.Counters) + len(m.Gauges))
}

type Counter struct {
	Name  string `json:"name"`
	Value int64  `json:"value"`
}

type SimpleGauge struct {
	Name  string  `json:"name"`
	Value float64 `json:"value"`
}

type ComplexGauge struct {
	Name       string  `json:"name"`
	Count      int     `json:"count"`
	Sum        float64 `json:"sum"`
	Min        float64 `json:"min"`
	Max        float64 `json:"max"`
	SumSquares float64 `json:"sum_squares"`
}

func monitor() {
	t := time.NewTicker(time.Duration(*flushInterval) * time.Second)

	for {
		select {
		case <-t.C:
			if err := submit(); err != nil {
				log.Error("submit: %s", err)
			}
		case s := <-In:
			if s.Modifier == "ms" {
				_, ok := timers[s.Bucket]
				if !ok {
					var t []float64
					timers[s.Bucket] = t
				}
				floatValue, _ := strconv.ParseFloat(s.Value, 64)
				timers[s.Bucket] = append(timers[s.Bucket], floatValue)
			} else if s.Modifier == "g" {
				_, ok := gauges[s.Bucket]
				if !ok {
					gauges[s.Bucket] = float64(0)
				}
				floatValue, _ := strconv.ParseFloat(s.Value, 64)
				gauges[s.Bucket] += floatValue
			} else {
				_, ok := counters[s.Bucket]
				if !ok {
					counters[s.Bucket] = 0
				}
				floatValue, _ := strconv.ParseFloat(s.Value, 32)
				counters[s.Bucket] += int64(float32(floatValue) * (1 / s.Sampling))
			}
		}
	}
}

func submit() (err error) {
	m := new(Measurement)
	m.Counters = make([]Counter, 0)
	m.Gauges = make([]interface{}, 0)

	for k, v := range counters {
		c := new(Counter)
		c.Name = k
		c.Value = v
		m.Counters = append(m.Counters, *c)
		delete(counters, k)
	}

	for k, v := range gauges {
		g := new(SimpleGauge)
		g.Name = k
		g.Value = v
		m.Gauges = append(m.Gauges, *g)
		delete(gauges, k)
	}

	for k, t := range timers {
		g := new(ComplexGauge)
		g.Name = k
		g.Count = len(t)

		if g.Count > 0 {
			sort.Float64s(t)
			g.Min = t[0]
			g.Max = t[len(t)-1]
			for _, v := range t {
				g.Sum += v
				g.SumSquares += (v * v)
			}
		}

		m.Gauges = append(m.Gauges, *g)
		delete(timers, k)
	}

	if m.Count() == 0 {
		log.Info("no new measurements in the last %d seconds", *flushInterval)
		return
	}

	payload, err := json.MarshalIndent(m, "", "  ")
	if err != nil {
		return
	}

	req, err := http.NewRequest("POST", "https://metrics-api.librato.com/v1/metrics", bytes.NewBuffer(payload))
	if err != nil {
		return
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Set("User-Agent", "statsd/1.0")
	req.SetBasicAuth(*libratoUser, *libratoToken)
	resp, err := http.DefaultClient.Do(req)
	if err == nil && resp.StatusCode != 200 {
		if err == nil {
			err = errors.New(fmt.Sprintf("%s", resp.Status))
		}
		log.Warn("error sending %d measurements: %s", m.Count(), err)
		return
	}

	log.Debug("sending payload:\n%s", payload)

	log.Info("%d measurements sent", m.Count())

	for k, _ := range counters {
		delete(counters, k)
	}

	for k, _ := range gauges {
		delete(gauges, k)
	}

	for k, _ := range timers {
		delete(timers, k)
	}

	return
}

func handle(conn *net.UDPConn, remaddr net.Addr, buf *bytes.Buffer) {
	var packet Packet
	var value string
	var sanitizeRegexp = regexp.MustCompile("[^a-zA-Z0-9\\-_\\.:\\|@]")
	var packetRegexp = regexp.MustCompile("([a-zA-Z0-9_\\.]+):(\\-?[0-9\\.]+)\\|(c|ms)(\\|@([0-9\\.]+))?")
	s := sanitizeRegexp.ReplaceAllString(buf.String(), "")

	for _, item := range packetRegexp.FindAllStringSubmatch(s, -1) {
		value = item[2]
		if item[3] == "ms" {
			_, err := strconv.ParseFloat(item[2], 32)
			if err != nil {
				value = "0"
			}
		}

		sampleRate, err := strconv.ParseFloat(item[5], 32)
		if err != nil {
			sampleRate = 1
		}

		packet.Bucket = item[1]
		packet.Value = value
		packet.Modifier = item[3]
		packet.Sampling = float32(sampleRate)

		In <- packet
	}
}

func listen() {
	address, _ := net.ResolveUDPAddr("udp", *serviceAddress)
	listener, err := net.ListenUDP("udp", address)
	defer listener.Close()
	if err != nil {
		log.Fatal("unable to listen: %s", err)
		os.Exit(1)
	}

	log.Info("listening for events...")

	for {
		message := make([]byte, 512)
		n, remaddr, error := listener.ReadFrom(message)
		if error != nil {
			continue
		}
		buf := bytes.NewBuffer(message[0:n])
		go handle(listener, remaddr, buf)
	}
}

func main() {
	flag.Parse()

	if *debug {
		log = logger.NewLogger(logger.LOG_LEVEL_DEBUG, "statsd")
	} else {
		log = logger.NewLogger(logger.LOG_LEVEL_INFO, "statsd")
	}

	go listen()
	monitor()
}
<commit_msg>Move debug statement<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"github.com/jcoene/gologger"
	"net"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strconv"
	"time"
)

type Packet struct {
	Bucket   string
	Value    string
	Modifier string
	Sampling float32
}

var log *logger.Logger

var (
	serviceAddress = flag.String("address", "0.0.0.0:8125", "UDP service address")
	libratoUser    = flag.String("user", "", "Librato Username")
	libratoToken   = flag.String("token", "", "Librato API Token")
	flushInterval  = flag.Int64("flush", 30, "Flush Interval (seconds)")
	debug          = flag.Bool("debug", false, "Enable Debugging")
)

var (
	In       = make(chan Packet, 10000)
	counters = make(map[string]int64)
	timers   = make(map[string][]float64)
	gauges   = make(map[string]float64)
)

type Measurement struct {
	Counters []Counter     `json:"counters"`
	Gauges   []interface{} `json:"gauges"`
}

func (m *Measurement) Count() int {
	return (len(m.Counters) + len(m.Gauges))
}

type Counter struct {
	Name  string `json:"name"`
	Value int64  `json:"value"`
}

type SimpleGauge struct {
	Name  string  `json:"name"`
	Value float64 `json:"value"`
}

type ComplexGauge struct {
	Name       string  `json:"name"`
	Count      int     `json:"count"`
	Sum        float64 `json:"sum"`
	Min        float64 `json:"min"`
	Max        float64 `json:"max"`
	SumSquares float64 `json:"sum_squares"`
}

func monitor() {
	t := time.NewTicker(time.Duration(*flushInterval) * time.Second)

	for {
		select {
		case <-t.C:
			if err := submit(); err != nil {
				log.Error("submit: %s", err)
			}
		case s := <-In:
			if s.Modifier == "ms" {
				_, ok := timers[s.Bucket]
				if !ok {
					var t []float64
					timers[s.Bucket] = t
				}
				floatValue, _ := strconv.ParseFloat(s.Value, 64)
				timers[s.Bucket] = append(timers[s.Bucket], floatValue)
			} else if s.Modifier == "g" {
				_, ok := gauges[s.Bucket]
				if !ok {
					gauges[s.Bucket] = float64(0)
				}
				floatValue, _ := strconv.ParseFloat(s.Value, 64)
				gauges[s.Bucket] += floatValue
			} else {
				_, ok := counters[s.Bucket]
				if !ok {
					counters[s.Bucket] = 0
				}
				floatValue, _ := strconv.ParseFloat(s.Value, 32)
				counters[s.Bucket] += int64(float32(floatValue) * (1 / s.Sampling))
			}
		}
	}
}

func submit() (err error) {
	m := new(Measurement)
	m.Counters = make([]Counter, 0)
	m.Gauges = make([]interface{}, 0)

	for k, v := range counters {
		c := new(Counter)
		c.Name = k
		c.Value = v
		m.Counters = append(m.Counters, *c)
		delete(counters, k)
	}

	for k, v := range gauges {
		g := new(SimpleGauge)
		g.Name = k
		g.Value = v
		m.Gauges = append(m.Gauges, *g)
		delete(gauges, k)
	}

	for k, t := range timers {
		g := new(ComplexGauge)
		g.Name = k
		g.Count = len(t)

		if g.Count > 0 {
			sort.Float64s(t)
			g.Min = t[0]
			g.Max = t[len(t)-1]
			for _, v := range t {
				g.Sum += v
				g.SumSquares += (v * v)
			}
		}

		m.Gauges = append(m.Gauges, *g)
		delete(timers, k)
	}

	if m.Count() == 0 {
		log.Info("no new measurements in the last %d seconds", *flushInterval)
		return
	}

	payload, err := json.MarshalIndent(m, "", "  ")
	if err != nil {
		return
	}

	log.Debug("sending payload:\n%s", payload)

	req, err := http.NewRequest("POST", "https://metrics-api.librato.com/v1/metrics", bytes.NewBuffer(payload))
	if err != nil {
		return
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Set("User-Agent", "statsd/1.0")
	req.SetBasicAuth(*libratoUser, *libratoToken)
	resp, err := http.DefaultClient.Do(req)
	if err == nil && resp.StatusCode != 200 {
		if err == nil {
			err = errors.New(fmt.Sprintf("%s", resp.Status))
		}
		log.Warn("error sending %d measurements: %s", m.Count(), err)
		return
	}

	log.Info("%d measurements sent", m.Count())

	for k, _ := range counters {
		delete(counters, k)
	}

	for k, _ := range gauges {
		delete(gauges, k)
	}

	for k, _ := range timers {
		delete(timers, k)
	}

	return
}

func handle(conn *net.UDPConn, remaddr net.Addr, buf *bytes.Buffer) {
	var packet Packet
	var value string
	var sanitizeRegexp = regexp.MustCompile("[^a-zA-Z0-9\\-_\\.:\\|@]")
	var packetRegexp = regexp.MustCompile("([a-zA-Z0-9_\\.]+):(\\-?[0-9\\.]+)\\|(c|ms)(\\|@([0-9\\.]+))?")
	s := sanitizeRegexp.ReplaceAllString(buf.String(), "")

	for _, item := range packetRegexp.FindAllStringSubmatch(s, -1) {
		value = item[2]
		if item[3] == "ms" {
			_, err := strconv.ParseFloat(item[2], 32)
			if err != nil {
				value = "0"
			}
		}

		sampleRate, err := strconv.ParseFloat(item[5], 32)
		if err != nil {
			sampleRate = 1
		}

		packet.Bucket = item[1]
		packet.Value = value
		packet.Modifier = item[3]
		packet.Sampling = float32(sampleRate)

		In <- packet
	}
}

func listen() {
	address, _ := net.ResolveUDPAddr("udp", *serviceAddress)
	listener, err := net.ListenUDP("udp", address)
	defer listener.Close()
	if err != nil {
		log.Fatal("unable to listen: %s", err)
		os.Exit(1)
	}

	log.Info("listening for events...")

	for {
		message := make([]byte, 512)
		n, remaddr, error := listener.ReadFrom(message)
		if error != nil {
			continue
		}
		buf := bytes.NewBuffer(message[0:n])
		go handle(listener, remaddr, buf)
	}
}

func main() {
	flag.Parse()

	if *debug {
		log = logger.NewLogger(logger.LOG_LEVEL_DEBUG, "statsd")
	} else {
		log = logger.NewLogger(logger.LOG_LEVEL_INFO, "statsd")
	}

	go listen()
	monitor()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"

	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	app            = kingpin.New("scm", "ssh config manager.")
	dump           = app.Command("dump", "")
	list           = app.Command("list", "List all Host.")
	show           = app.Command("show", "Dumps one section.")
	showHost       = show.Arg("host", "Name for a host").Required().String()
	add            = app.Command("add", "Add host")
	addHost        = add.Arg("host", "Name for a host").Required().String()
	addHostName    = add.Arg("hostname", "HostName of the specified host").Required().String()
	addUser        = add.Flag("user", "").Short('u').String()
	addPort        = add.Flag("port", "").Short('p').String()
	addIdentify    = add.Flag("identify", "").Short('i').String()
	addParams      = add.Flag("params", "").Short('P').StringMap()
	update         = app.Command("update", "")
	updateHost     = update.Arg("host", "").Required().String()
	updateHostName = update.Flag("hostname", "HostName of the specified host").Short('h').String()
	updateUser     = update.Flag("user", "").Short('u').String()
	updatePort     = update.Flag("port", "").Short('p').String()
	updateIdentify = update.Flag("identify", "").Short('i').String()
	updateParams   = update.Flag("params", "").Short('P').StringMap()
	remove         = app.Command("remove", "")
	removeHost     = remove.Arg("host", "").Required().String()
)

var (
	hosts           Hosts
	ssh_config_file = os.ExpandEnv("$HOME/.ssh/config")
)

func addCommand(name, ip, user, port, identify string, params map[string]string) {
	if hosts = hosts.addHost(name, ip, user, port, identify, params); hosts != nil {
		hosts = hosts.addHost(name, ip, user, port, identify, params)
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is already exist\n", name)
	}
}

func updateCommand(name, ip, user, port, identify string, params map[string]string) {
	if hosts = hosts.updateHost(name, ip, user, port, identify, params); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", name)
	}
}

func listCommand() {
	for _, v := range hosts.listHost() {
		fmt.Println(v)
	}
}

func showCommand(name string) {
	_, host := hosts.GetHost(name)
	fmt.Println(host)
}

func removeCommand(name string) {
	if hosts = hosts.removeHost(name); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", name)
	}
}

func main() {
	file, err := os.Open(ssh_config_file)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	hosts, err = Parse(file)
	if err != nil {
		log.Fatal(err)
	}

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case dump.FullCommand():
		fmt.Print(hosts)
	case show.FullCommand():
		showCommand(*showHost)
	case add.FullCommand():
		addCommand(*addHost, *addHostName, *addUser, *addPort, *addIdentify, *addParams)
	case list.FullCommand():
		listCommand()
	case update.FullCommand():
		updateCommand(*updateHost, *updateHostName, *updateUser, *updatePort, *updateIdentify, *updateParams)
	case remove.FullCommand():
		removeCommand(*removeHost)
	}
}
<commit_msg>fix check already exist host bug<commit_after>package main

import (
	"fmt"
	"log"
	"os"

	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	app            = kingpin.New("scm", "ssh config manager.")
	dump           = app.Command("dump", "")
	list           = app.Command("list", "List all Host.")
	show           = app.Command("show", "Dumps one section.")
	showHost       = show.Arg("host", "Name for a host").Required().String()
	add            = app.Command("add", "Add host")
	addHost        = add.Arg("host", "Name for a host").Required().String()
	addHostName    = add.Arg("hostname", "HostName of the specified host").Required().String()
	addUser        = add.Flag("user", "").Short('u').String()
	addPort        = add.Flag("port", "").Short('p').String()
	addIdentify    = add.Flag("identify", "").Short('i').String()
	addParams      = add.Flag("params", "").Short('P').StringMap()
	update         = app.Command("update", "")
	updateHost     = update.Arg("host", "").Required().String()
	updateHostName = update.Flag("hostname", "HostName of the specified host").Short('h').String()
	updateUser     = update.Flag("user", "").Short('u').String()
	updatePort     = update.Flag("port", "").Short('p').String()
	updateIdentify = update.Flag("identify", "").Short('i').String()
	updateParams   = update.Flag("params", "").Short('P').StringMap()
	remove         = app.Command("remove", "")
	removeHost     = remove.Arg("host", "").Required().String()
)

var (
	hosts           Hosts
	ssh_config_file = os.ExpandEnv("$HOME/.ssh/config")
)

func addCommand(name, ip, user, port, identify string, params map[string]string) {
	if hosts = hosts.addHost(name, ip, user, port, identify, params); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is already exist\n", name)
	}
}

func updateCommand(name, ip, user, port, identify string, params map[string]string) {
	if hosts = hosts.updateHost(name, ip, user, port, identify, params); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", name)
	}
}

func listCommand() {
	for _, v := range hosts.listHost() {
		fmt.Println(v)
	}
}

func showCommand(name string) {
	_, host := hosts.GetHost(name)
	fmt.Println(host)
}

func removeCommand(name string) {
	if hosts = hosts.removeHost(name); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", name)
	}
}

func main() {
	file, err := os.Open(ssh_config_file)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	hosts, err = Parse(file)
	if err != nil {
		log.Fatal(err)
	}

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case dump.FullCommand():
		fmt.Print(hosts)
	case show.FullCommand():
		showCommand(*showHost)
	case add.FullCommand():
		addCommand(*addHost, *addHostName, *addUser, *addPort, *addIdentify, *addParams)
	case list.FullCommand():
		listCommand()
	case update.FullCommand():
		updateCommand(*updateHost, *updateHostName, *updateUser, *updatePort, *updateIdentify, *updateParams)
	case remove.FullCommand():
		removeCommand(*removeHost)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strings"
	"sync"

	"github.com/ChimeraCoder/anaconda"
	log "github.com/Sirupsen/logrus"
	"github.com/urfave/cli"
)

var (
	version = "HEAD"
)

// twitter submits a tweet through Twitter's API.
func twitter(message string, ctx *cli.Context, wg *sync.WaitGroup) {
	defer wg.Done()

	log.Info("Tweeting: " + message)
	anaconda.SetConsumerKey(ctx.String("twitter-consumer-key"))
	anaconda.SetConsumerSecret(ctx.String("twitter-consumer-secret"))
	api := anaconda.NewTwitterApi(ctx.String("twitter-acesss-token"), ctx.String("twitter-access-secret"))
	api.PostTweet(message, nil)
}

// verifyTwitterConfig checks if the configs for Twitter exists before continuing, otherwise exits early.
func verifyTwitterConfig(ctx *cli.Context) {
	missingKey := false
	for _, key := range []string{"twitter-consumer-key", "twitter-consumer-secret", "twitter-acesss-token", "twitter-access-secret"} {
		if ctx.String(key) == "" {
			log.Error(key + " is not defined.")
			missingKey = true
		}
	}
	if missingKey {
		cli.ShowAppHelp(ctx)
		os.Exit(1)
	}
}

// processContext the context passed from CLI.
func processContext(ctx *cli.Context) error {
	if len(ctx.Args()) == 0 {
		log.Error("You need to write a message to update your status... Who would want to read nothing?")
		cli.ShowAppHelp(ctx)
		return cli.NewExitError("", 1)
	}

	message := strings.Join(ctx.Args(), " ")
	var wg sync.WaitGroup
	if !ctx.Bool("twitter") && !ctx.Bool("facebook") {
		wg.Add(1)
		verifyTwitterConfig(ctx)
		go twitter(message, ctx, &wg)
	}
	if ctx.Bool("twitter") {
		wg.Add(1)
		verifyTwitterConfig(ctx)
		go twitter(message, ctx, &wg)
	}

	wg.Wait()
	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "S"
	app.Usage = "A simple command line utility for posting status messages to social networks"
	app.Version = version
	app.Author = "Dustin Blackman"
	app.Copyright = "(c) 2016 " + app.Author
	app.EnableBashCompletion = true
	app.Action = processContext

	cli.AppHelpTemplate = `NAME:
	{{.Name}} - {{.Usage}}
	https://github.com/dustinblackman/s/

EXAMPLE USAGE:
	s -t Going out for poutine.
{{if len .Authors}}
AUTHOR(S):
	{{range .Authors}}{{ . }}{{end}}{{end}}
{{if .VisibleFlags}}
GLOBAL OPTIONS:
{{range .VisibleFlags}}	{{.}}
{{end}}{{end}}{{if .Copyright }}
COPYRIGHT:
	{{.Copyright}}
{{end}}{{if .Version}}
VERSION:
	{{.Version}}
{{end}}
`

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "twitter, t",
			Usage: "Post tweet to Twitter",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-key, tck",
			Usage:  "Twitter consumer key",
			EnvVar: "TWITTER_CONSUMER_KEY",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-secret, tcs",
			Usage:  "Twitter consumer secret",
			EnvVar: "TWITTER_CONSUMER_SECRET",
		},
		cli.StringFlag{
			Name:   "twitter-acesss-token, tat",
			Usage:  "Twitter access token",
			EnvVar: "TWITTER_ACCESS_TOKEN",
		},
		cli.StringFlag{
			Name:   "twitter-access-secret, tas",
			Usage:  "Twitter access token secret",
			EnvVar: "TWITTER_ACCESS_SECRET",
		},
	}

	app.Run(os.Args)
}
<commit_msg>english<commit_after>package main

import (
	"os"
	"strings"
	"sync"

	"github.com/ChimeraCoder/anaconda"
	log "github.com/Sirupsen/logrus"
	"github.com/urfave/cli"
)

var (
	version = "HEAD"
)

// twitter submits a tweet through Twitter's API.
func twitter(message string, ctx *cli.Context, wg *sync.WaitGroup) {
	defer wg.Done()

	log.Info("Tweeting: " + message)
	anaconda.SetConsumerKey(ctx.String("twitter-consumer-key"))
	anaconda.SetConsumerSecret(ctx.String("twitter-consumer-secret"))
	api := anaconda.NewTwitterApi(ctx.String("twitter-acesss-token"), ctx.String("twitter-access-secret"))
	api.PostTweet(message, nil)
}

// verifyTwitterConfig checks if the configs for Twitter exists before continuing, otherwise exits early.
func verifyTwitterConfig(ctx *cli.Context) {
	missingKey := false
	for _, key := range []string{"twitter-consumer-key", "twitter-consumer-secret", "twitter-acesss-token", "twitter-access-secret"} {
		if ctx.String(key) == "" {
			log.Error(key + " is not defined.")
			missingKey = true
		}
	}
	if missingKey {
		cli.ShowAppHelp(ctx)
		os.Exit(1)
	}
}

// processContext parses the context passed from CLI.
func processContext(ctx *cli.Context) error {
	if len(ctx.Args()) == 0 {
		log.Error("You need to write a message to update your status... Who would want to read nothing?")
		cli.ShowAppHelp(ctx)
		return cli.NewExitError("", 1)
	}

	message := strings.Join(ctx.Args(), " ")
	var wg sync.WaitGroup
	if !ctx.Bool("twitter") && !ctx.Bool("facebook") {
		wg.Add(1)
		verifyTwitterConfig(ctx)
		go twitter(message, ctx, &wg)
	}
	if ctx.Bool("twitter") {
		wg.Add(1)
		verifyTwitterConfig(ctx)
		go twitter(message, ctx, &wg)
	}

	wg.Wait()
	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "S"
	app.Usage = "A simple command line utility for posting status messages to social networks"
	app.Version = version
	app.Author = "Dustin Blackman"
	app.Copyright = "(c) 2016 " + app.Author
	app.EnableBashCompletion = true
	app.Action = processContext

	cli.AppHelpTemplate = `NAME:
	{{.Name}} - {{.Usage}}
	https://github.com/dustinblackman/s/

EXAMPLE USAGE:
	s -t Going out for poutine.
{{if len .Authors}}
AUTHOR(S):
	{{range .Authors}}{{ . }}{{end}}{{end}}
{{if .VisibleFlags}}
GLOBAL OPTIONS:
{{range .VisibleFlags}}	{{.}}
{{end}}{{end}}{{if .Copyright }}
COPYRIGHT:
	{{.Copyright}}
{{end}}{{if .Version}}
VERSION:
	{{.Version}}
{{end}}
`

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "twitter, t",
			Usage: "Post tweet to Twitter",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-key, tck",
			Usage:  "Twitter consumer key",
			EnvVar: "TWITTER_CONSUMER_KEY",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-secret, tcs",
			Usage:  "Twitter consumer secret",
			EnvVar: "TWITTER_CONSUMER_SECRET",
		},
		cli.StringFlag{
			Name:   "twitter-acesss-token, tat",
			Usage:  "Twitter access token",
			EnvVar: "TWITTER_ACCESS_TOKEN",
		},
		cli.StringFlag{
			Name:   "twitter-access-secret, tas",
			Usage:  "Twitter access token secret",
			EnvVar: "TWITTER_ACCESS_SECRET",
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"flag"
	"io"
	"path"
	"runtime"
	"strings"
	"sync"

	"fmt"
	"os"

	"os/signal"
	"syscall"

	"github.com/joelanford/goscan/utils/archive"
	"github.com/joelanford/goscan/utils/keywords"
	"github.com/joelanford/goscan/utils/scratch"
	"github.com/pkg/errors"
)

type ScanOpts struct {
	InputFiles   []string
	KeywordsFile string
	Policies     []string
	HitContext   int
	ResultsFile  string
	Parallelism  int
}

type ScanResult struct {
	File string         `json:"file"`
	Hits []keywords.Hit `json:"hits"`
}

func main() {
	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run() error {
	//
	// Parse command line flags
	//
	scratchOpts, scanOpts, err := parseFlags()
	if err != nil {
		return err
	}

	//
	// Setup context and signal handlers, which will be needed
	// if we need to cleanly exit before completing the scan.
	//
	ctx, cancel := context.WithCancel(context.Background())
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGABRT, syscall.SIGINT, syscall.SIGKILL)
	go func() {
		sig := <-sigChan
		fmt.Fprintf(os.Stderr, "Received signal %s. Exiting", sig)
		cancel()
	}()

	//
	// Setup the keyword matcher
	//
	kw, err := keywords.Load(scanOpts.KeywordsFile, scanOpts.Policies)
	if err != nil {
		return err
	}

	//
	// Open the output file
	//
	output, err := os.Create(scanOpts.ResultsFile)
	if err != nil {
		return err
	}
	defer output.Close()
	e := json.NewEncoder(output)

	//
	// Prepare the scratch space
	//
	ss := scratch.New(*scratchOpts)
	err = ss.Setup()
	if err != nil {
		return err
	}
	defer ss.Teardown()

	//
	// errChan used for the following goroutines to be able to return an error
	// and shortcircuit the rest of the processing.
	//
	errChan := make(chan error)

	//
	// Copy the files to be scanned into the scratch space
	//
	ifiles := make(chan string)
	go func() {
		defer close(ifiles)
		for _, ifile := range scanOpts.InputFiles {
			select {
			case <-ctx.Done():
				errChan <- ctx.Err()
				return
			default:
				ofile := path.Join(ss.ScratchSpacePath, path.Base(ifile))
				if err := copyToScratchSpace(ifile, ofile); err != nil {
					errChan <- err
					return
				}
				ifiles <- ofile
			}
		}
	}()

	//
	// Recursively unarchive the files to be scanned
	//
	var unarchiveWg sync.WaitGroup
	unarchiveStarted := make(chan struct{})
	unarchiveResults := make(chan archive.UnarchiveResult)
	go func() {
		for {
			select {
			case <-ctx.Done():
				errChan <- ctx.Err()
				return
			case ifile, ok := <-ifiles:
				if !ok {
					return
				}
				unarchiveWg.Add(1)
				close(unarchiveStarted)
				go func() {
					archive.UnarchiveRecursive(ctx, ifile, ".goscan-unar", unarchiveResults)
					unarchiveWg.Done()
				}()
			}
		}
	}()

	go func() {
		<-unarchiveStarted
		unarchiveWg.Wait()
		close(unarchiveResults)
	}()

	//
	// Scan unarchived files for hits
	//
	var scanWg sync.WaitGroup
	scanResults := make(chan ScanResult)
	scanWg.Add(scanOpts.Parallelism)
	for i := 0; i < scanOpts.Parallelism; i++ {
		go func() {
			defer scanWg.Done()
			for {
				select {
				case <-ctx.Done():
					errChan <- ctx.Err()
					return
				case ur, ok := <-unarchiveResults:
					if !ok {
						return
					}
					if ur.Error != nil {
						errChan <- ur.Error
						return
					}
					hits, err := kw.MatchFile(ur.File, scanOpts.HitContext)
					if err != nil {
						errChan <- err
						return
					}
					scanResults <- ScanResult{
						File: strings.Replace(strings.Replace(ur.File, ss.ScratchSpacePath+"/", "", -1), ".goscan-unar", "", -1),
						Hits: hits,
					}
				}
			}
		}()
	}

	go func() {
		scanWg.Wait()
		close(scanResults)
	}()

	//
	// Loop until error or all hits have been found
	//
	for {
		select {
		case err = <-errChan:
			return err
		case sr, ok := <-scanResults:
			if !ok {
				return nil
			}
			err = e.Encode(sr)
			if err != nil {
				return err
			}
		}
	}
}

func parseFlags() (*scratch.Opts, *ScanOpts, error) {
	flag.Usage = func() {
		fmt.Printf("Usage: goscan [options] <scanfiles>\n")
		flag.PrintDefaults()
	}

	var scratchOpts scratch.Opts
	var policies string
	var scanOpts ScanOpts

	configureScratchOpts(&scratchOpts)
	flag.StringVar(&scanOpts.KeywordsFile, "scan.words", "", "YAML keywords file")
	flag.IntVar(&scanOpts.HitContext, "scan.context", 10, "Context to capture around each hit")
	flag.StringVar(&policies, "scan.policies", "all", "Comma-separated list of keyword policies")
	flag.StringVar(&scanOpts.ResultsFile, "scan.output", "-", "Results output file (\"-\" for stdout)")
	flag.IntVar(&scanOpts.Parallelism, "scan.parallelism", runtime.NumCPU(), "Number of goroutines to use to scan files.")

	flag.Parse()

	scanOpts.InputFiles = flag.Args()

	if scratchOpts.RamdiskEnable && scratchOpts.RamdiskMegabytes < 0 {
		return nil, nil, errors.New("error: scratch.ramdisk.mb must be positive")
	}

	if scanOpts.KeywordsFile == "" {
		return nil, nil, errors.New("error: scan.words file must be defined")
	}

	if scanOpts.HitContext < 0 {
		return nil, nil, errors.New("error: scan.context must not be negative")
	}

	if policies == "all" {
		scanOpts.Policies = nil
	} else {
		scanOpts.Policies = strings.Split(policies, ",")
	}

	if scanOpts.ResultsFile == "-" {
		scanOpts.ResultsFile = "/dev/stdout"
	}

	if scanOpts.Parallelism < 1 {
		return nil, nil, errors.New("error: scan.parallelism must be positive")
	}

	if len(scanOpts.InputFiles) == 0 {
		return nil, nil, errors.New("error: must define at least one file to scan")
	}
	return &scratchOpts, &scanOpts, nil
}

func copyToScratchSpace(ifilename, ofilename string) error {
	ifile, err := os.Open(ifilename)
	if err != nil {
		return err
	}
	defer ifile.Close()
	ofile, err := os.Create(ofilename)
	if err != nil {
		return err
	}
	defer ofile.Close()
	if _, err := io.Copy(ofile, ifile); err != nil {
		return err
	}
	return nil
}
<commit_msg>Fixes panic when scanning multiple input files<commit_after>package main

import (
	"context"
	"encoding/json"
	"flag"
	"io"
	"path"
	"runtime"
	"strings"
	"sync"

	"fmt"
	"os"

	"os/signal"
	"syscall"

	"github.com/joelanford/goscan/utils/archive"
	"github.com/joelanford/goscan/utils/keywords"
	"github.com/joelanford/goscan/utils/scratch"
	"github.com/pkg/errors"
)

type ScanOpts struct {
	InputFiles   []string
	KeywordsFile string
	Policies     []string
	HitContext   int
	ResultsFile  string
	Parallelism  int
}

type ScanResult struct {
	File string         `json:"file"`
	Hits []keywords.Hit `json:"hits"`
}

func main() {
	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run() error {
	//
	// Parse command line flags
	//
	scratchOpts, scanOpts, err := parseFlags()
	if err != nil {
		return err
	}

	//
	// Setup context and signal handlers, which will be needed
	// if we need to cleanly exit before completing the scan.
	//
	ctx, cancel := context.WithCancel(context.Background())
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGABRT, syscall.SIGINT, syscall.SIGKILL)
	go func() {
		sig := <-sigChan
		fmt.Fprintf(os.Stderr, "Received signal %s. Exiting", sig)
		cancel()
	}()

	//
	// Setup the keyword matcher
	//
	kw, err := keywords.Load(scanOpts.KeywordsFile, scanOpts.Policies)
	if err != nil {
		return err
	}

	//
	// Open the output file
	//
	output, err := os.Create(scanOpts.ResultsFile)
	if err != nil {
		return err
	}
	defer output.Close()
	e := json.NewEncoder(output)

	//
	// Prepare the scratch space
	//
	ss := scratch.New(*scratchOpts)
	err = ss.Setup()
	if err != nil {
		return err
	}
	defer ss.Teardown()

	//
	// errChan used for the following goroutines to be able to return an error
	// and shortcircuit the rest of the processing.
	//
	errChan := make(chan error)

	//
	// Copy the files to be scanned into the scratch space
	//
	ifiles := make(chan string)
	go func() {
		defer close(ifiles)
		for _, ifile := range scanOpts.InputFiles {
			select {
			case <-ctx.Done():
				errChan <- ctx.Err()
				return
			default:
				ofile := path.Join(ss.ScratchSpacePath, path.Base(ifile))
				if err := copyToScratchSpace(ifile, ofile); err != nil {
					errChan <- err
					return
				}
				ifiles <- ofile
			}
		}
	}()

	//
	// Recursively unarchive the files to be scanned
	//
	var unarchiveWg sync.WaitGroup
	unarchiveResults := make(chan archive.UnarchiveResult)
	unarchiveWg.Add(len(scanOpts.InputFiles))
	go func() {
		for {
			select {
			case <-ctx.Done():
				errChan <- ctx.Err()
				return
			case ifile, ok := <-ifiles:
				if !ok {
					return
				}
				go func() {
					archive.UnarchiveRecursive(ctx, ifile, ".goscan-unar", unarchiveResults)
					unarchiveWg.Done()
				}()
			}
		}
	}()

	go func() {
		unarchiveWg.Wait()
		close(unarchiveResults)
	}()

	//
	// Scan unarchived files for hits
	//
	var scanWg sync.WaitGroup
	scanResults := make(chan ScanResult)
	scanWg.Add(scanOpts.Parallelism)
	for i := 0; i < scanOpts.Parallelism; i++ {
		go func() {
			defer scanWg.Done()
			for {
				select {
				case <-ctx.Done():
					errChan <- ctx.Err()
					return
				case ur, ok := <-unarchiveResults:
					if !ok {
						return
					}
					if ur.Error != nil {
						errChan <- ur.Error
						return
					}
					hits, err := kw.MatchFile(ur.File, scanOpts.HitContext)
					if err != nil {
						errChan <- err
						return
					}
					scanResults <- ScanResult{
						File: strings.Replace(strings.Replace(ur.File, ss.ScratchSpacePath+"/", "", -1), ".goscan-unar", "", -1),
						Hits: hits,
					}
				}
			}
		}()
	}

	go func() {
		scanWg.Wait()
		close(scanResults)
	}()

	//
	// Loop until error or all hits have been found
	//
	for {
		select {
		case err = <-errChan:
			return err
		case sr, ok := <-scanResults:
			if !ok {
				return nil
			}
			err = e.Encode(sr)
			if err != nil {
				return err
			}
		}
	}
}

func parseFlags() (*scratch.Opts, *ScanOpts, error) {
	flag.Usage = func() {
		fmt.Printf("Usage: goscan [options] <scanfiles>\n")
		flag.PrintDefaults()
	}

	var scratchOpts scratch.Opts
	var policies string
	var scanOpts ScanOpts

	configureScratchOpts(&scratchOpts)
	flag.StringVar(&scanOpts.KeywordsFile, "scan.words", "", "YAML keywords file")
	flag.IntVar(&scanOpts.HitContext, "scan.context", 10, "Context to capture around each hit")
	flag.StringVar(&policies, "scan.policies", "all", "Comma-separated list of keyword policies")
	flag.StringVar(&scanOpts.ResultsFile, "scan.output", "-", "Results output file (\"-\" for stdout)")
	flag.IntVar(&scanOpts.Parallelism, "scan.parallelism", runtime.NumCPU(), "Number of goroutines to use to scan files.")

	flag.Parse()

	scanOpts.InputFiles = flag.Args()

	if scratchOpts.RamdiskEnable && scratchOpts.RamdiskMegabytes < 0 {
		return nil, nil, errors.New("error: scratch.ramdisk.mb must be positive")
	}

	if scanOpts.KeywordsFile == "" {
		return nil, nil, errors.New("error: scan.words file must be defined")
	}

	if scanOpts.HitContext < 0 {
		return nil, nil, errors.New("error: scan.context must not be negative")
	}

	if policies == "all" {
		scanOpts.Policies = nil
	} else {
		scanOpts.Policies = strings.Split(policies, ",")
	}

	if scanOpts.ResultsFile == "-" {
		scanOpts.ResultsFile = "/dev/stdout"
	}

	if scanOpts.Parallelism < 1 {
		return nil, nil, errors.New("error: scan.parallelism must be positive")
	}

	if len(scanOpts.InputFiles) == 0 {
		return nil, nil, errors.New("error: must define at least one file to scan")
	}
	return &scratchOpts, &scanOpts, nil
}

func copyToScratchSpace(ifilename, ofilename string) error {
	ifile, err := os.Open(ifilename)
	if err != nil {
		return err
	}
	defer ifile.Close()
	ofile, err := os.Create(ofilename)
	if err != nil {
		return err
	}
	defer ofile.Close()
	if _, err := io.Copy(ofile, ifile); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"flag"
	"log"
	"log/syslog"
	"net/smtp"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/johansundell/cocapi"
)

var db *sql.DB
var mysqlUser, mysqlPass, mysqlDb, mysqlHost string
var queryInsertUpdateMember = `INSERT INTO members (tag, name, created, last_updated, active) VALUES (?, ?, null, null, 1) ON DUPLICATE KEY UPDATE member_id=LAST_INSERT_ID(member_id), last_updated = NOW(), active = 1`
var isCocUnderUpdate bool
var failedTries int
var emailTo, emailFrom string
var myClanTag string

func init() {

	mysqlDb = "cocsniffer"
	mysqlHost = os.Getenv("MYSQL_COC_HOST")
	mysqlUser = os.Getenv("MYSQL_USER")
	mysqlPass = os.Getenv("MYSQL_PASS")

	emailTo = os.Getenv("EMAIL_TO")
	emailFrom = os.Getenv("EMAIL_FROM")

	myClanTag = os.Getenv("COC_CLANTAG")
}

func main() {
	useSyslog := flag.Bool("syslog", false, "Use syslog")
	flag.Parse()
	if *useSyslog {
		logwriter, e := syslog.New(syslog.LOG_NOTICE, "cocsniffer")
		if e == nil {
			log.SetOutput(logwriter)
		}
	}
	db, _ = sql.Open("mysql", mysqlUser+":"+mysqlPass+"@tcp("+mysqlHost+":3306)/"+mysqlDb)
	defer db.Close()

	isCocUnderUpdate = false
	failedTries = 0
	getMembersData(myClanTag)
	ticker := time.NewTicker(1 * time.Minute)
	quit := make(chan struct{})
	go func() {
		for {
			select {
			case <-ticker.C:
				getMembersData(myClanTag)
			case <-quit:
				ticker.Stop()
				return
			}
		}
	}()

	// Wait for SIGINT and SIGTERM (HIT CTRL-C)
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	log.Println(<-ch)

	close(quit)
	log.Println("Bye ;)")
}

func getMembersData(clan string) error {
	members, err := cocapi.GetMemberInfo(clan)
	if err != nil {
		reportError(err)
		return err
	}

	if isCocUnderUpdate {
		isCocUnderUpdate = false
		sendEmail("COC Alert", "Servers are up again")
	}
	failedTries = 0

	var ids = make([]string, 0)
	for _, m := range members.Items {
		if result, err := db.Exec(queryInsertUpdateMember, m.Tag, m.Name); err != nil {
			log.Println(err)
		} else {
			if id, err := result.LastInsertId(); err != nil {
				log.Println(err)
			} else {
				ids = append(ids, strconv.Itoa(int(id)))
			}
		}
		if m.Role == "member" && m.Donations >= 1000 {
			log.Println("Found member that should be upgraded", m.Name)
			var alerted int
			db.QueryRow("SELECT alert_sent_donations FROM members WHERE tag = ?", m.Tag).Scan(&alerted)
			if alerted == 0 {
				sendEmail("Member "+m.Name+" should be upgraded", "Member "+m.Name+" should be upgraded")
				db.Exec("UPDATE members SET alert_sent_donations = 1 WHERE tag = ?", m.Tag)
			}
		}
	}
	db.Exec("UPDATE members SET exited = NOW() WHERE member_id NOT IN (" + strings.Join(ids, ", ") + ") AND active = 1")
	db.Exec("UPDATE members SET active = 0 WHERE member_id NOT IN (" + strings.Join(ids, ", ") + ")")
	//log.Println("done members func")
	return nil
}

func reportError(err error) {
	switch t := err.(type) {
	case *cocapi.ServerError:
		if t.ErrorCode == 503 {
			failedTries++
			if failedTries > 3 {
				if !isCocUnderUpdate {
					isCocUnderUpdate = true
					sendEmail("COC Alert", "Servers under update")
				}
			}
		}
		break
	default:
		log.Println("Fatal error coc:", t)
		break
	}
}

func sendEmail(subject, message string) bool {
	body := "To: " + emailTo + "\r\nSubject: " + subject + "\r\n\r\n" + message
	if err := smtp.SendMail("127.0.0.1:25", nil, emailFrom, []string{emailTo}, []byte(body)); err != nil {
		log.Println(err)
		return false
	}
	return true
}
<commit_msg>New api changes<commit_after>package main

import (
	"database/sql"
	"flag"
	"log"
	"log/syslog"
	"net/smtp"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/johansundell/cocapi"
)

var db *sql.DB
var mysqlUser, mysqlPass, mysqlDb, mysqlHost string
var queryInsertUpdateMember = `INSERT INTO members (tag, name, created, last_updated, active) VALUES (?, ?, null, null, 1) ON DUPLICATE KEY UPDATE member_id=LAST_INSERT_ID(member_id), last_updated = NOW(), active = 1`
var isCocUnderUpdate bool
var failedTries int
var emailTo, emailFrom string
var myClanTag, myKey string
var cocClient cocapi.Client

func init() {

	mysqlDb = "cocsniffer"
	mysqlHost = os.Getenv("MYSQL_COC_HOST")
	mysqlUser = os.Getenv("MYSQL_USER")
	mysqlPass = os.Getenv("MYSQL_PASS")

	emailTo = os.Getenv("EMAIL_TO")
	emailFrom = os.Getenv("EMAIL_FROM")

	myClanTag = os.Getenv("COC_CLANTAG")
	myKey = os.Getenv("COC_KEY")
}

func main() {
	useSyslog := flag.Bool("syslog", false, "Use syslog")
	flag.Parse()
	if *useSyslog {
		logwriter, e := syslog.New(syslog.LOG_NOTICE, "cocsniffer")
		if e == nil {
			log.SetOutput(logwriter)
		}
	}
	db, _ = sql.Open("mysql", mysqlUser+":"+mysqlPass+"@tcp("+mysqlHost+":3306)/"+mysqlDb)
	defer db.Close()

	cocClient = cocapi.NewClient(myKey)

	isCocUnderUpdate = false
	failedTries = 0
	getMembersData(myClanTag)
	ticker := time.NewTicker(1 * time.Minute)
	quit := make(chan struct{})
	go func() {
		for {
			select {
			case <-ticker.C:
				getMembersData(myClanTag)
			case <-quit:
				ticker.Stop()
				return
			}
		}
	}()

	// Wait for SIGINT and SIGTERM (HIT CTRL-C)
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	log.Println(<-ch)

	close(quit)
	log.Println("Bye ;)")
}

func getMembersData(clan string) error {
	members, err := cocClient.GetMembers(clan)
	if err != nil {
		reportError(err)
		return err
	}

	if isCocUnderUpdate {
		isCocUnderUpdate = false
		sendEmail("COC Alert", "Servers are up again")
	}
	failedTries = 0

	var ids = make([]string, 0)
	for _, m := range members.Items {
		if result, err := db.Exec(queryInsertUpdateMember, m.Tag, m.Name); err != nil {
			log.Println(err)
		} else {
			if id, err := result.LastInsertId(); err != nil {
				log.Println(err)
			} else {
				ids = append(ids, strconv.Itoa(int(id)))
			}
		}
		if m.Role == "member" && m.Donations >= 1000 {
			log.Println("Found member that should be upgraded", m.Name)
			var alerted int
			db.QueryRow("SELECT alert_sent_donations FROM members WHERE tag = ?", m.Tag).Scan(&alerted)
			if alerted == 0 {
				sendEmail("Member "+m.Name+" should be upgraded", "Member "+m.Name+" should be upgraded")
				db.Exec("UPDATE members SET alert_sent_donations = 1 WHERE tag = ?", m.Tag)
			}
		}
	}
	db.Exec("UPDATE members SET exited = NOW() WHERE member_id NOT IN (" + strings.Join(ids, ", ") + ") AND active = 1")
	db.Exec("UPDATE members SET active = 0 WHERE member_id NOT IN (" + strings.Join(ids, ", ") + ")")
	//log.Println("done members func")
	return nil
}

func reportError(err error) {
	switch t := err.(type) {
	case *cocapi.ServerError:
		if t.ErrorCode == 503 {
			failedTries++
			if failedTries > 3 {
				if !isCocUnderUpdate {
					isCocUnderUpdate = true
					sendEmail("COC Alert", "Servers under update")
				}
			}
		}
		break
	default:
		log.Println("Fatal error coc:", t)
		break
	}
}

func sendEmail(subject, message string) bool {
	body := "To: " + emailTo + "\r\nSubject: " + subject + "\r\n\r\n" + message
	if err := smtp.SendMail("127.0.0.1:25", nil, emailFrom, []string{emailTo}, []byte(body)); err != nil {
		log.Println(err)
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/reportportal/commons-go/commons"
	"github.com/reportportal/commons-go/conf"
	"github.com/reportportal/commons-go/server"
	"github.com/unrolled/secure"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	currDir, e := os.Getwd()
	if nil != e {
		log.Fatalf("Cannot get workdir: %s", e.Error())
	}
	cfg := conf.EmptyConfig()
	rpConf := struct {
		Cfg         *conf.ServerConfig
		StaticsPath string `env:"RP_STATICS_PATH"`
	}{
		Cfg:         cfg,
		StaticsPath: currDir,
	}
	err := conf.LoadConfig(&rpConf)
	if nil != err {
		log.Fatalf("Cannot log app config")
	}
	info := commons.GetBuildInfo()
	info.Name = "Service UI"
	srv := server.New(rpConf.Cfg, info)
	configureRouter(srv, rpConf)
	srv.StartServer()
}

func configureRouter(srv *server.RpServer, rpConf struct {
	Cfg         *conf.ServerConfig
	StaticsPath string `env:"RP_STATICS_PATH"`
}) {
	srv.WithRouter(func(router *chi.Mux) {
		//apply compression
		router.Use(middleware.DefaultCompress)
		router.Use(middleware.Logger)
		//content security policy
		csp := map[string][]string{
			"default-src": {"'self'", "data:", "'unsafe-inline'", "*.uservoice.com"},
			"script-src": {
				"'self'",
				"'unsafe-inline'",
				"'unsafe-eval'",
				"status.reportportal.io",
				"www.google-analytics.com",
				"stats.g.doubleclick.net",
				"*.saucelabs.com",
				"*.epam.com",
				"*.uservoice.com",
				"*.rawgit.com",
			},
			"worker-src":     {"'self'", "blob:"},
			"font-src":       {"'self'", "data:", "fonts.googleapis.com", "fonts.gstatic.com", "*.rawgit.com"},
			"style-src-elem": {"'self'", "data:", "'unsafe-inline'", "*.googleapis.com", "*.rawgit.com"},
			"media-src":      {"'self'", "*.saucelabs.com"},
			"img-src":        {"*", "'self'", "data:", "blob:"},
			"object-src":     {"'self'"},
		}
		//apply content security policies
		var STSSeconds int64 = 315360000
		router.Use(func(next http.Handler) http.Handler {
			return secure.New(secure.Options{
				ContentTypeNosniff:    true,
				BrowserXssFilter:      true,
				ContentSecurityPolicy: buildCSP(csp),
				STSSeconds:            STSSeconds,
				STSIncludeSubdomains:  true,
				STSPreload:            true,
			}).Handler(next)
		})
		err := os.Chdir(rpConf.StaticsPath)
		if nil != err {
			log.Fatalf("Dir %s not found", rpConf.StaticsPath)
		}
		router.Handle("/*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			//trim query params
			ext := filepath.Ext(trimQuery(r.URL.String(), "?"))
			// never cache html
			if "/" == r.URL.String() || ".html" == ext {
				w.Header().Add("Cache-Control", "no-cache")
			}
			http.FileServer(http.Dir(rpConf.StaticsPath)).ServeHTTP(&redirectingRW{ResponseWriter: w, Request: r}, r)
		}))
	})
}

func trimQuery(s string, sep string) string {
	sepIndex := strings.Index(s, sep)
	if -1 != sepIndex {
		return s[:sepIndex]
	}
	return s
}

func buildCSP(csp map[string][]string) string {
	var instr []string
	for k, v := range csp {
		instr = append(instr, k+" "+strings.Join(v, " "))
	}
	return strings.Join(instr, "; ")
}

type redirectingRW struct {
	*http.Request
	http.ResponseWriter
	ignore bool
}

func (hrw *redirectingRW) Header() http.Header {
	return hrw.ResponseWriter.Header()
}

func (hrw *redirectingRW) WriteHeader(status int) {
	var notFoundStatusCode int = 404
	if notFoundStatusCode == status {
		hrw.ignore = true
		http.Redirect(hrw.ResponseWriter, hrw.Request, "/ui/#notfound", http.StatusTemporaryRedirect)
	} else {
		hrw.ResponseWriter.WriteHeader(status)
	}
}

func (hrw *redirectingRW) Write(p []byte) (int, error) {
	if hrw.ignore {
		return len(p), nil
	}
	return hrw.ResponseWriter.Write(p)
}
<commit_msg>EPMRPP-50435: Fix CSP for video attachments<commit_after>package main

import (
	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/reportportal/commons-go/commons"
	"github.com/reportportal/commons-go/conf"
	"github.com/reportportal/commons-go/server"
	"github.com/unrolled/secure"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	currDir, e := os.Getwd()
	if nil != e {
		log.Fatalf("Cannot get workdir: %s", e.Error())
	}
	cfg := conf.EmptyConfig()
	rpConf := struct {
		Cfg         *conf.ServerConfig
		StaticsPath string `env:"RP_STATICS_PATH"`
	}{
		Cfg:         cfg,
		StaticsPath: currDir,
	}
	err := conf.LoadConfig(&rpConf)
	if nil != err {
		log.Fatalf("Cannot log app config")
	}
	info := commons.GetBuildInfo()
	info.Name = "Service UI"
	srv := server.New(rpConf.Cfg, info)
	configureRouter(srv, rpConf)
	srv.StartServer()
}

func configureRouter(srv *server.RpServer, rpConf struct {
	Cfg         *conf.ServerConfig
	StaticsPath string `env:"RP_STATICS_PATH"`
}) {
	srv.WithRouter(func(router *chi.Mux) {
		//apply compression
		router.Use(middleware.DefaultCompress)
		router.Use(middleware.Logger)
		//content security policy
		csp := map[string][]string{
			"default-src": {"'self'", "data:", "'unsafe-inline'", "*.uservoice.com"},
			"script-src": {
				"'self'",
				"'unsafe-inline'",
				"'unsafe-eval'",
				"status.reportportal.io",
				"www.google-analytics.com",
				"stats.g.doubleclick.net",
				"*.saucelabs.com",
				"*.epam.com",
				"*.uservoice.com",
				"*.rawgit.com",
			},
			"worker-src":     {"'self'", "blob:"},
			"font-src":       {"'self'", "data:", "fonts.googleapis.com", "fonts.gstatic.com", "*.rawgit.com"},
			"style-src-elem": {"'self'", "data:", "'unsafe-inline'", "*.googleapis.com", "*.rawgit.com"},
			"media-src":      {"'self'", "*.saucelabs.com", "blob:"},
			"img-src":        {"*", "'self'", "data:", "blob:"},
			"object-src":     {"'self'"},
		}
		//apply content security policies
		var STSSeconds int64 = 315360000
		router.Use(func(next http.Handler) http.Handler {
			return secure.New(secure.Options{
				ContentTypeNosniff:    true,
				BrowserXssFilter:      true,
				ContentSecurityPolicy: buildCSP(csp),
				STSSeconds:            STSSeconds,
				STSIncludeSubdomains:  true,
				STSPreload:            true,
			}).Handler(next)
		})
		err := os.Chdir(rpConf.StaticsPath)
		if nil != err {
			log.Fatalf("Dir %s not found", rpConf.StaticsPath)
		}
		router.Handle("/*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			//trim query params
			ext := filepath.Ext(trimQuery(r.URL.String(), "?"))
			// never cache html
			if "/" == r.URL.String() || ".html" == ext {
				w.Header().Add("Cache-Control", "no-cache")
			}
			http.FileServer(http.Dir(rpConf.StaticsPath)).ServeHTTP(&redirectingRW{ResponseWriter: w, Request: r}, r)
		}))
	})
}

func trimQuery(s string, sep string) string {
	sepIndex := strings.Index(s, sep)
	if -1 != sepIndex {
		return s[:sepIndex]
	}
	return s
}

func buildCSP(csp map[string][]string) string {
	var instr []string
	for k, v := range csp {
		instr = append(instr, k+" "+strings.Join(v, " "))
	}
	return strings.Join(instr, "; ")
}

type redirectingRW struct {
	*http.Request
	http.ResponseWriter
	ignore bool
}

func (hrw *redirectingRW) Header() http.Header {
	return hrw.ResponseWriter.Header()
}

func (hrw *redirectingRW) WriteHeader(status int) {
	var notFoundStatusCode int = 404
	if notFoundStatusCode == status {
		hrw.ignore = true
		http.Redirect(hrw.ResponseWriter, hrw.Request, "/ui/#notfound", http.StatusTemporaryRedirect)
	} else {
		hrw.ResponseWriter.WriteHeader(status)
	}
}

func (hrw *redirectingRW) Write(p []byte) (int, error) {
	if hrw.ignore {
		return len(p), nil
	}
	return hrw.ResponseWriter.Write(p)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net"
	"os"
	"time"

	"github.com/zmap/zgrab/ztools/zlog"
	"github.com/zmap/zgrab/ztools/ztls"
)

type Flags struct {
	CertificateChainPath string
	KeyPath              string
	LogFileName          string
	ListenAddress        string
}

func (f *Flags) Validate() error {
	if f.CertificateChainPath == "" {
		return errors.New("Certificate not specified")
	}
	if f.KeyPath == "" {
		return errors.New("Key not specified")
	}
	return nil
}

var flags Flags

func init() {
	flag.StringVar(&flags.CertificateChainPath, "certificate", "", "Path to certificate chain (PEM encoded)")
	flag.StringVar(&flags.KeyPath, "key", "", "Path to key corresponding to certificate (PEM encoded, decrypted)")
	flag.StringVar(&flags.ListenAddress, "listen-address", "127.0.0.1:443", "ip:port to listen on")
	flag.StringVar(&flags.LogFileName, "log-file", "-", "defaults to stderr")
	flag.Parse()
}

type CipherList struct {
	Ciphers []ztls.CipherSuite `json:"ciphers"`
}

type HostLogEntry struct {
	Host     string             `json:"ip_address"`
	Time     string             `json:"time"`
	Ciphers  []ztls.CipherSuite `json:"ciphers,omitempty"`
	RawHello []byte             `json:"raw_hello,omitempty"`
	Request  string             `json:"request,omitempty"`
}

var logChan chan HostLogEntry

func ciphers(c *ztls.Conn) error {
	defer c.Close()
	t := time.Now()
	deadline := t.Add(time.Second * 30)
	c.SetDeadline(deadline)
	entry := HostLogEntry{}
	entry.Time = t.Format(time.RFC3339)
	host, _, _ := net.SplitHostPort(c.RemoteAddr().String())
	entry.Host = host
	handshakeErr := c.Handshake()
	entry.Ciphers = c.ClientCiphers()
	entry.RawHello = c.ClientHelloRaw()
	if handshakeErr != nil {
		logChan <- entry
		return handshakeErr
	}
	cl := CipherList{}
	cl.Ciphers = entry.Ciphers
	buf := make([]byte, 1024)
	n, _ := c.Read(buf)
	entry.Request = string(buf[0:n])
	logChan <- entry
	enc, err := json.Marshal(cl)
	if err != nil {
		return err
	}
	length := len(enc)
	c.Write([]byte("HTTP/1.1 200 OK\r\n"))
	c.Write([]byte("Connection: close\r\n"))
	c.Write([]byte("Content-Type: application/json\r\n"))
	contentLength := fmt.Sprintf("Content-Length: %d\r\n", length)
	c.Write([]byte(contentLength))
	c.Write([]byte("\r\n"))
	c.Write(enc)
	return nil
}

func main() {
	if err := flags.Validate(); err != nil {
		zlog.Fatal(err.Error())
	}
	certificate, certErr := ztls.LoadX509KeyPair(flags.CertificateChainPath, flags.KeyPath)
	if certErr != nil {
		zlog.Fatal(certErr.Error())
	}

	var tlsConfig ztls.Config
	tlsConfig.SessionTicketsDisabled = true
	tlsConfig.Certificates = []ztls.Certificate{certificate}
	listener, err := ztls.Listen("tcp", flags.ListenAddress, &tlsConfig)
	if err != nil {
		zlog.Fatal(err.Error())
	}

	// Open log file
	logFile := os.Stderr
	if flags.LogFileName != "-" {
		f, err := os.Create(flags.LogFileName)
		if err != nil {
			zlog.Fatal(err.Error())
		}
		logFile = f
	}

	logChan = make(chan HostLogEntry, 1024)

	go func() {
		encoder := json.NewEncoder(logFile)
		for {
			entry := <-logChan
			encoder.Encode(entry)
		}
	}()

	for {
		conn, err := listener.Accept()
		if err != nil {
			zlog.Info(err.Error())
			continue
		}
		c := conn.(*ztls.Conn)
		go ciphers(c)
	}
}
<commit_msg>Append to log<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net"
	"os"
	"time"

	"github.com/zmap/zgrab/ztools/zlog"
	"github.com/zmap/zgrab/ztools/ztls"
)

type Flags struct {
	CertificateChainPath string
	KeyPath              string
	LogFileName          string
	ListenAddress        string
	ErrorFileName        string
}

func (f *Flags) Validate() error {
	if f.CertificateChainPath == "" {
		return errors.New("Certificate not specified")
	}
	if f.KeyPath == "" {
		return errors.New("Key not specified")
	}
	return nil
}

var flags Flags

func init() {
	flag.StringVar(&flags.CertificateChainPath, "certificate", "", "Path to certificate chain (PEM encoded)")
	flag.StringVar(&flags.KeyPath, "key", "", "Path to key corresponding to certificate (PEM encoded, decrypted)")
	flag.StringVar(&flags.ListenAddress, "listen-address", "127.0.0.1:443", "ip:port to listen on")
	flag.StringVar(&flags.LogFileName, "log-file", "-", "defaults to stderr")
	flag.StringVar(&flags.ErrorFileName, "error-file", "errors.log", "Logs connection-level errors")
	flag.Parse()
}

type CipherList struct {
	Ciphers []ztls.CipherSuite `json:"ciphers"`
}

type HostLogEntry struct {
	Host     string             `json:"ip_address"`
	Time     string             `json:"time"`
	Ciphers  []ztls.CipherSuite `json:"ciphers,omitempty"`
	RawHello []byte             `json:"raw_hello,omitempty"`
	Request  string             `json:"request,omitempty"`
}

var logChan chan HostLogEntry

func ciphers(c *ztls.Conn) error {
	defer c.Close()
	t := time.Now()
	deadline := t.Add(time.Second * 30)
	c.SetDeadline(deadline)
	entry := HostLogEntry{}
	entry.Time = t.Format(time.RFC3339)
	host, _, _ := net.SplitHostPort(c.RemoteAddr().String())
	entry.Host = host
	handshakeErr := c.Handshake()
	entry.Ciphers = c.ClientCiphers()
	entry.RawHello = c.ClientHelloRaw()
	if handshakeErr != nil {
		logChan <- entry
		return handshakeErr
	}
	cl := CipherList{}
	cl.Ciphers = entry.Ciphers
	buf := make([]byte, 1024)
	n, _ := c.Read(buf)
	entry.Request = string(buf[0:n])
	logChan <- entry
	enc, err := json.Marshal(cl)
	if err != nil {
		return err
	}
	length := len(enc)
	c.Write([]byte("HTTP/1.1 200 OK\r\n"))
	c.Write([]byte("Connection: close\r\n"))
	c.Write([]byte("Content-Type: application/json\r\n"))
	contentLength := fmt.Sprintf("Content-Length: %d\r\n", length)
	c.Write([]byte(contentLength))
	c.Write([]byte("\r\n"))
	c.Write(enc)
	return nil
}

func main() {
	if err := flags.Validate(); err != nil {
		zlog.Fatal(err.Error())
	}
	certificate, certErr := ztls.LoadX509KeyPair(flags.CertificateChainPath, flags.KeyPath)
	if certErr != nil {
		zlog.Fatal(certErr.Error())
	}

	var tlsConfig ztls.Config
	tlsConfig.SessionTicketsDisabled = true
	tlsConfig.Certificates = []ztls.Certificate{certificate}
	listener, err := ztls.Listen("tcp", flags.ListenAddress, &tlsConfig)
	if err != nil {
		zlog.Fatal(err.Error())
	}

	// Open errors file
	errorFile, oErr := os.OpenFile(flags.ErrorFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if oErr != nil {
		zlog.Fatal(oErr.Error())
	}
	errorLog := zlog.New(errorFile, "cipher-suite-echo")

	// Open log file
	logFile := os.Stderr
	if flags.LogFileName != "-" {
		f, err := os.OpenFile(flags.LogFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			zlog.Fatal(err.Error())
		}
		logFile = f
	}

	logChan = make(chan HostLogEntry, 1024)

	go func() {
		encoder := json.NewEncoder(logFile)
		for {
			entry := <-logChan
			encoder.Encode(entry)
		}
	}()

	for {
		conn, err := listener.Accept()
		if err != nil {
			errorLog.Error(err.Error())
			continue
		}
		c := conn.(*ztls.Conn)
		go ciphers(c)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"
	"strconv"

	"github.com/seiflotfy/counts/config"
	"github.com/seiflotfy/counts/server"
	"github.com/seiflotfy/counts/utils"
)

var logger = utils.GetLogger()

func main() {
	var port uint
	flag.UintVar(&port, "p", 3596, "specifies the port for Counts to run on")
	flag.Parse()

	//TODO: Add arguments for dataDir and infoDir

	os.Setenv("COUNTS_PORT", strconv.Itoa(int(port)))

	logger.Info.Println("Starting counts...")
	conf := config.GetConfig()
	logger.Info.Println("Using data dir: ", conf.GetDataDir())
	server := server.New()
	server.Run()
}
<commit_msg>Fix main.go to use error<commit_after>package main

import (
	"flag"
	"os"
	"strconv"

	"github.com/seiflotfy/counts/config"
	"github.com/seiflotfy/counts/server"
	"github.com/seiflotfy/counts/utils"
)

var logger = utils.GetLogger()

func main() {
	var port uint
	flag.UintVar(&port, "p", 3596, "specifies the port for Counts to run on")
	flag.Parse()

	//TODO: Add arguments for dataDir and infoDir

	os.Setenv("COUNTS_PORT", strconv.Itoa(int(port)))

	logger.Info.Println("Starting counts...")
	conf := config.GetConfig()
	logger.Info.Println("Using data dir: ", conf.GetDataDir())
	server, err := server.New()
	if err != nil {
		panic(err)
	}
	server.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"encoding/csv"
	"errors"
	"flag"
	"fmt"
	"log"
	"net/smtp"
	"os"
	"strings"
	"sync"
	"time"

	ini "gopkg.in/ini.v1"
)

var (
	errExpiringSoon error = errors.New("expiring soon")
	errExpired            = errors.New("expired")
	errTimeout            = errors.New("timeout connecting to host")
)

func main() {
	urlFile := flag.String("urls", "/etc/certwatcher/urls.csv", "path to CSV containing list of URLs to monitor")
	iniFile := flag.String("config", "/etc/certwatcher/config.ini", "path to config.ini")
	days := flag.Int("days", 30, "number of days before triggering alert")
	verbose := flag.Bool("v", false, "verbose output")

	flag.Parse()

	// read config
	cfg, err := ini.Load(*iniFile)
	if err != nil {
		log.Fatalf("could not open config file: %s", err)
	}

	// load list of hosts to watch
	f, err := os.Open(*urlFile)
	if err != nil {
		log.Fatalf("could not open URL file: %s", err)
	}

	rdr := csv.NewReader(f)
	rdr.FieldsPerRecord = 2
	records, err := rdr.ReadAll()
	if err != nil {
		log.Fatal(err)
	}

	var wg sync.WaitGroup
	for _, r := range records {
		var host, desc string
		if r[1] != "" {
			host, desc = r[0], r[1]
		} else {
			host, desc = r[0], r[0]
		}

		wg.Add(1)

		go func() {
			defer wg.Done()
			if err := check(host, "443", *days, *verbose); err != nil {
				switch err {
				case errExpiringSoon, errExpired:
					notify(host, desc, cfg, *days, err, *verbose)
					log.Printf("main: sent notification for host %s - %s", host, err)
				default:
					log.Printf("main: ERROR: unexpected error checking host %s - %s", host, err)
				}
			}
		}()
	}

	wg.Wait()
}

func check(host, port string, days int, verbose bool) error {
	var conn *tls.Conn

	errc := make(chan error, 1)
	go func() {
		var err error
		conn, err = tls.Dial("tcp", host+":"+port, &tls.Config{
			InsecureSkipVerify: true,
		})
		if err != nil {
			errc <- err
		}

		errc <- nil
	}()

	select {
	case err := <-errc:
		if err != nil {
			return err
		}
	case <-time.Tick(5 * time.Second):
		return errTimeout
	}

	defer conn.Close()
	if err := conn.Handshake(); err != nil {
		return err
	}

	for i, cert := range conn.ConnectionState().PeerCertificates {
		if cert.IsCA {
			continue
		}

		if verbose {
			log.Printf("check: %s certificate %d: expires after %s (%s)", host, i, cert.NotAfter, time.Until(cert.NotAfter))
			log.Printf("check: %s certificate %d: issuer: %s", host, i, cert.Issuer.Names)
			log.Printf("check: %s certificate %d: names: %s", host, i, cert.Subject.Names)
			log.Printf("check: %s certificate %d: DNSNames: %s", host, i, cert.DNSNames)
		}

		if time.Now().After(cert.NotAfter) {
			return errExpired
		}

		if time.Until(cert.NotAfter) < time.Duration(days)*time.Hour*24 {
			return errExpiringSoon
		}
	}

	log.Printf("check: %s - certificate is ok", host)

	return nil
}

func notify(host, desc string, cfg *ini.File, days int, err error, verbose bool) {
	section := cfg.Section("certwatcher")

	if !section.Key("sendmail").MustBool() {
		log.Println("notify: refusing to send email due to config.")
		return
	}

	port := "587"
	if section.Key("port").String() != "" {
		port = section.Key("port").String()
	}
	mailhost := section.Key("host").String()

	auth := smtp.PlainAuth("",
		section.Key("username").String(),
		section.Key("password").String(),
		mailhost,
	)

	to := []string{section.Key("rcpt").String()}
	var subject, body string
	if err == errExpiringSoon {
		subject = fmt.Sprintf("Subject: %s certificate expiring soon: %s", section.Key("subjectprefix").String(), desc)
		body = fmt.Sprintf("The SSL certificate for the host %s (%s) is expiring in less than %d days.", host, desc, days)
	} else if err == errExpired {
		subject = fmt.Sprintf("Subject: %s certificate has expired! %s", section.Key("subjectprefix").String(), desc)
		body = fmt.Sprintf("The SSL certificate for the host %s (%s) has expired!", host, desc)
	}
	msg := []byte(strings.Join([]string{subject,
		fmt.Sprintf("To: %s", strings.Join(to, ", ")),
		fmt.Sprintf("From: %s", section.Key("from").String()),
		"",
		body,
		"",
		"Please take appropriate action!",
	},
		"\r\n",
	))

	if verbose {
		log.Printf("notify: sending host %s expiration notification to %s", host, section.Key("rcpt").String())
	}

	if err := smtp.SendMail(mailhost+":"+port, auth, section.Key("from").String(), to, msg); err != nil {
		log.Fatalf("could not send email: %s", err)
	}
}
<commit_msg>Improve CSV error message<commit_after>package main

import (
	"crypto/tls"
	"encoding/csv"
	"errors"
	"flag"
	"fmt"
	"log"
	"net/smtp"
	"os"
	"strings"
	"sync"
	"time"

	ini "gopkg.in/ini.v1"
)

var (
	errExpiringSoon error = errors.New("expiring soon")
	errExpired            = errors.New("expired")
	errTimeout            = errors.New("timeout connecting to host")
)

func main() {
	urlFile := flag.String("urls", "/etc/certwatcher/urls.csv", "path to CSV containing list of URLs to monitor")
	iniFile := flag.String("config", "/etc/certwatcher/config.ini", "path to config.ini")
	days := flag.Int("days", 30, "number of days before triggering alert")
	verbose := flag.Bool("v", false, "verbose output")

	flag.Parse()

	// read config
	cfg, err := ini.Load(*iniFile)
	if err != nil {
		log.Fatalf("could not open config file: %s", err)
	}

	// load list of hosts to watch
	f, err := os.Open(*urlFile)
	if err != nil {
		log.Fatalf("could not open URL file: %s", err)
	}

	rdr := csv.NewReader(f)
	rdr.FieldsPerRecord = 2
	records, err := rdr.ReadAll()
	if err != nil {
		log.Fatalf("could not read %s: %s", *urlFile, err)
	}

	var wg sync.WaitGroup
	for _, r := range records {
		var host, desc string
		if r[1] != "" {
			host, desc = r[0], r[1]
		} else {
			host, desc = r[0], r[0]
		}

		wg.Add(1)

		go func() {
			defer wg.Done()
			if err := check(host, "443", *days, *verbose); err != nil {
				switch err {
				case errExpiringSoon, errExpired:
					notify(host, desc, cfg, *days, err, *verbose)
					log.Printf("main: sent notification for host %s - %s", host, err)
				default:
					log.Printf("main: ERROR: unexpected error checking host %s - %s", host, err)
				}
			}
		}()
	}

	wg.Wait()
}

func check(host, port string, days int, verbose bool) error {
	var conn *tls.Conn

	errc := make(chan error, 1)
	go func() {
		var err error
		conn, err = tls.Dial("tcp", host+":"+port, &tls.Config{
			InsecureSkipVerify: true,
		})
		if err != nil {
			errc <- err
		}

		errc <- nil
	}()

	select {
	case err := <-errc:
		if err != nil {
			return err
		}
	case <-time.Tick(5 * time.Second):
		return errTimeout
	}

	defer conn.Close()
	if err := conn.Handshake(); err != nil {
		return err
	}

	for i, cert := range conn.ConnectionState().PeerCertificates {
		if cert.IsCA {
			continue
		}

		if verbose {
			log.Printf("check: %s certificate %d: expires after %s (%s)", host, i, cert.NotAfter, time.Until(cert.NotAfter))
			log.Printf("check: %s certificate %d: issuer: %s", host, i, cert.Issuer.Names)
			log.Printf("check: %s certificate %d: names: %s", host, i, cert.Subject.Names)
			log.Printf("check: %s certificate %d: DNSNames: %s", host, i, cert.DNSNames)
		}

		if time.Now().After(cert.NotAfter) {
			return errExpired
		}

		if time.Until(cert.NotAfter) < time.Duration(days)*time.Hour*24 {
			return errExpiringSoon
		}
	}

	log.Printf("check: %s - certificate is ok", host)

	return nil
}

func notify(host, desc string, cfg *ini.File, days int, err error, verbose bool) {
	section := cfg.Section("certwatcher")

	if !section.Key("sendmail").MustBool() {
		log.Println("notify: refusing to send email due to config.")
		return
	}

	port := "587"
	if section.Key("port").String() != "" {
		port = section.Key("port").String()
	}
	mailhost := section.Key("host").String()

	auth := smtp.PlainAuth("",
		section.Key("username").String(),
		section.Key("password").String(),
		mailhost,
	)

	to := []string{section.Key("rcpt").String()}
	var subject, body string
	if err == errExpiringSoon {
		subject = fmt.Sprintf("Subject: %s certificate expiring soon: %s", section.Key("subjectprefix").String(), desc)
		body = fmt.Sprintf("The SSL certificate for the host %s (%s) is expiring in less than %d days.", host, desc, days)
	} else if err == errExpired {
		subject = fmt.Sprintf("Subject: %s certificate has expired! %s", section.Key("subjectprefix").String(), desc)
		body = fmt.Sprintf("The SSL certificate for the host %s (%s) has expired!", host, desc)
	}
	msg := []byte(strings.Join([]string{subject,
		fmt.Sprintf("To: %s", strings.Join(to, ", ")),
		fmt.Sprintf("From: %s", section.Key("from").String()),
		"",
		body,
		"",
		"Please take appropriate action!",
	},
		"\r\n",
	))

	if verbose {
		log.Printf("notify: sending host %s expiration notification to %s", host, section.Key("rcpt").String())
	}

	if err := smtp.SendMail(mailhost+":"+port, auth, section.Key("from").String(), to, msg); err != nil {
		log.Fatalf("could not send email: %s", err)
	}
}
<|endoftext|>