<commit_before>package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"net/http"
	"os"
	"runtime"
	"time"

	"github.com/ryanuber/columnize"
	"github.com/spf13/cobra"

	"github.com/mlafeldt/ck/convertkit"
)

func main() {
	var (
		apiKey, apiSecret, apiEndpoint string
	)

	rootCmd := &cobra.Command{
		Use:          "ck",
		Short:        "The ConvertKit Tool",
		SilenceUsage: true,
	}
	rootCmd.PersistentFlags().StringVar(&apiKey, "api-key", "", "Set API key for ConvertKit account")
	rootCmd.PersistentFlags().StringVar(&apiSecret, "api-secret", "", "Set API secret for ConvertKit account")
	rootCmd.PersistentFlags().StringVar(&apiEndpoint, "api-endpoint", "", "Set ConvertKit API endpoint")

	var query convertkit.SubscriberQuery
	var csv bool
	subscribersCmd := &cobra.Command{
		Use:   "subscribers",
		Short: "List subscribers",
		RunE: func(cmd *cobra.Command, args []string) error {
			config := convertkit.DefaultConfig()
			if apiKey != "" {
				config.Key = apiKey
			}
			if apiSecret != "" {
				config.Secret = apiSecret
			}
			if apiEndpoint != "" {
				config.Endpoint = apiEndpoint
			}
			config.HTTPClient = &http.Client{Timeout: 10 * time.Second}
			client, _ := convertkit.NewClient(config)
			subscribers, err := client.Subscribers(&query)
			if err != nil {
				return err
			}
			if csv {
				return outputCSV(os.Stdout, subscribers)
			}
			return outputTable(os.Stdout, subscribers)
		},
	}
	subscribersCmd.Flags().StringVar(&query.Since, "since", "", "Filter subscribers added on or after this date")
	subscribersCmd.Flags().StringVar(&query.Until, "until", "", "Filter subscribers added on or before this date")
	subscribersCmd.Flags().BoolVar(&query.Reverse, "reverse", false, "List subscribers in reverse order")
	subscribersCmd.Flags().BoolVar(&query.Cancelled, "cancelled", false, "List cancelled subscribers")
	subscribersCmd.Flags().StringVar(&query.EmailAddress, "email", "", "Filter subscribers by email address")
	subscribersCmd.Flags().BoolVar(&csv, "csv", false, "Output in CSV format")
	rootCmd.AddCommand(subscribersCmd)

	versionCmd := &cobra.Command{
		Use:   "version",
		Short: "Show program version",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("ck %s %s/%s %s\n", Version,
				runtime.GOOS, runtime.GOARCH, runtime.Version())
		},
	}
	rootCmd.AddCommand(versionCmd)

	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}

func outputCSV(w io.Writer, subscribers []convertkit.Subscriber) error {
	records := [][]string{
		{"Email", "Signed up"},
	}
	for _, s := range subscribers {
		records = append(records, []string{
			s.EmailAddress,
			s.CreatedAt.Format(time.RFC3339),
		})
	}
	cw := csv.NewWriter(w)
	cw.WriteAll(records)
	return cw.Error()
}

func outputTable(w io.Writer, subscribers []convertkit.Subscriber) error {
	lines := []string{"#|Email|Signed up"}
	for i, s := range subscribers {
		lines = append(lines, fmt.Sprintf("%d|%s|%s",
			i+1,
			s.EmailAddress,
			s.CreatedAt.Format(time.RFC3339),
		))
	}
	_, err := fmt.Fprintln(w, columnize.SimpleFormat(lines))
	return err
}
<commit_msg>Simplify parsing of global options<commit_after>package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"net/http"
	"os"
	"runtime"
	"time"

	"github.com/ryanuber/columnize"
	"github.com/spf13/cobra"

	"github.com/mlafeldt/ck/convertkit"
)

func main() {
	config := convertkit.DefaultConfig()
	config.HTTPClient = &http.Client{Timeout: 10 * time.Second}

	rootCmd := &cobra.Command{
		Use:          "ck",
		Short:        "The ConvertKit Tool",
		SilenceUsage: true,
	}
	rootCmd.PersistentFlags().StringVar(&config.Key, "api-key", "", "Set API key for ConvertKit account")
	rootCmd.PersistentFlags().StringVar(&config.Secret, "api-secret", "", "Set API secret for ConvertKit account")
	rootCmd.PersistentFlags().StringVar(&config.Endpoint, "api-endpoint", "", "Set ConvertKit API endpoint")

	var (
		query convertkit.SubscriberQuery
		csv   bool
	)
	subscribersCmd := &cobra.Command{
		Use:   "subscribers",
		Short: "List subscribers",
		RunE: func(cmd *cobra.Command, args []string) error {
			client, _ := convertkit.NewClient(config)
			subscribers, err := client.Subscribers(&query)
			if err != nil {
				return err
			}
			if csv {
				return outputCSV(os.Stdout, subscribers)
			}
			return outputTable(os.Stdout, subscribers)
		},
	}
	subscribersCmd.Flags().StringVar(&query.Since, "since", "", "Filter subscribers added on or after this date")
	subscribersCmd.Flags().StringVar(&query.Until, "until", "", "Filter subscribers added on or before this date")
	subscribersCmd.Flags().BoolVar(&query.Reverse, "reverse", false, "List subscribers in reverse order")
	subscribersCmd.Flags().BoolVar(&query.Cancelled, "cancelled", false, "List cancelled subscribers")
	subscribersCmd.Flags().StringVar(&query.EmailAddress, "email", "", "Filter subscribers by email address")
	subscribersCmd.Flags().BoolVar(&csv, "csv", false, "Output in CSV format")
	rootCmd.AddCommand(subscribersCmd)

	versionCmd := &cobra.Command{
		Use:   "version",
		Short: "Show program version",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("ck %s %s/%s %s\n", Version,
				runtime.GOOS, runtime.GOARCH, runtime.Version())
		},
	}
	rootCmd.AddCommand(versionCmd)

	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}

func outputCSV(w io.Writer, subscribers []convertkit.Subscriber) error {
	records := [][]string{
		{"Email", "Signed up"},
	}
	for _, s := range subscribers {
		records = append(records, []string{
			s.EmailAddress,
			s.CreatedAt.Format(time.RFC3339),
		})
	}
	cw := csv.NewWriter(w)
	cw.WriteAll(records)
	return cw.Error()
}

func outputTable(w io.Writer, subscribers []convertkit.Subscriber) error {
	lines := []string{"#|Email|Signed up"}
	for i, s := range subscribers {
		lines = append(lines, fmt.Sprintf("%d|%s|%s",
			i+1,
			s.EmailAddress,
			s.CreatedAt.Format(time.RFC3339),
		))
	}
	_, err := fmt.Fprintln(w, columnize.SimpleFormat(lines))
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/ttacon/pretty"
)

var matcher = regexp.MustCompile("coverage: ([\\d]+\\.[\\d]+)% of statements")

func main() {
	http.HandleFunc("/travisci", handleBuild)
	http.ListenAndServe(":18009", nil)
}

func handleBuild(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	byt, err := ioutil.ReadAll(r.Body)
	fmt.Println("body was: ", string(byt))
	fmt.Println("==========")
	fmt.Printf("req: %#v\n", r)
	fmt.Println("==========")
	if err != nil {
		w.WriteHeader(http.StatusOK) // tell travis everything is okay
		return
	}

	clean, err := url.QueryUnescape(string(byt))
	if err != nil {
		w.WriteHeader(http.StatusOK) // tell travis everything is okay
		return
	}

	var data TravisCIPayload
	err = json.Unmarshal([]byte(strings.TrimPrefix(clean, "payload=")), &data)
	fmt.Println("err: ", err)
	pretty.Println(data)
	w.WriteHeader(http.StatusOK)
}

type TravisCIPayload struct {
	ID             int        `json:"id"`
	Number         string     `json:"number"`
	Status         *string    `json:"status"`
	StartedAt      *time.Time `json:"started_at"`
	FinishedAt     *time.Time `json:"finished_at"`
	StatusMessage  string     `json:"status_message"`
	Commit         string     `json:"commit"`
	Branch         string     `json:"master"`
	Message        string     `json:"message"`
	CompareUrl     string     `json:"compare_url"`
	CommittedAt    *time.Time `json:"committed_at"`
	CommitterName  string     `json:"committer_name"`
	CommitterEmail string     `json:"committer_email"`
	AuthorName     string     `json:"author_name"`
	AuthorEmail    string     `json:"author_email"`
	Type           string     `json:"type"`
	BuildUrl       string     `json:"build_url"`
	// TODO(ttacon): there's a lot more here but i don't need it right now
}

/*
{
  "repository": {
    "id": 1,
    "name": "minimal",
    "owner_name": "svenfuchs",
    "url": "http://github.com/svenfuchs/minimal"
   },
  "config": {
    "notifications": {
      "webhooks": ["http://evome.fr/notifications", "http://example.com/"]
    }
  },
  "matrix": [
    {
      "id": 2,
      "repository_id": 1,
      "number": "1.1",
      "state": "created",
      "started_at": null,
      "finished_at": null,
      "config": {
        "notifications": {
          "webhooks": ["http://evome.fr/notifications", "http://example.com/"]
        }
      },
      "status": null,
      "log": "",
      "result": null,
      "parent_id": 1,
      "commit": "62aae5f70ceee39123ef",
      "branch": "master",
      "message": "the commit message",
      "committed_at": "2011-11-11T11: 11: 11Z",
      "committer_name": "Sven Fuchs",
      "committer_email": "svenfuchs@artweb-design.de",
      "author_name": "Sven Fuchs",
      "author_email": "svenfuchs@artweb-design.de",
      "compare_url": "https://github.com/svenfuchs/minimal/compare/master...develop"
    }
  ]
}
*/
<commit_msg>remove payload level<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/ttacon/pretty"
)

var matcher = regexp.MustCompile("coverage: ([\\d]+\\.[\\d]+)% of statements")

func main() {
	http.HandleFunc("/travisci", handleBuild)
	http.ListenAndServe(":18009", nil)
}

func handleBuild(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	byt, err := ioutil.ReadAll(r.Body)
	fmt.Println("body was: ", string(byt))
	fmt.Println("==========")
	fmt.Printf("req: %#v\n", r)
	fmt.Println("==========")
	if err != nil {
		w.WriteHeader(http.StatusOK) // tell travis everything is okay
		return
	}

	clean, err := url.QueryUnescape(string(byt))
	if err != nil {
		w.WriteHeader(http.StatusOK) // tell travis everything is okay
		return
	}

	fmt.Println(strings.TrimPrefix(clean, "payload="))

	var data TravisCIPayload
	err = json.Unmarshal([]byte(strings.TrimPrefix(clean, "payload=")), &data)
	fmt.Println("err: ", err)
	pretty.Println(data)
	w.WriteHeader(http.StatusOK)
}

type TravisCIPayload struct {
	ID             int        `json:"id"`
	Number         string     `json:"number"`
	Status         *string    `json:"status"`
	StartedAt      *time.Time `json:"started_at"`
	FinishedAt     *time.Time `json:"finished_at"`
	StatusMessage  string     `json:"status_message"`
	Commit         string     `json:"commit"`
	Branch         string     `json:"master"`
	Message        string     `json:"message"`
	CompareUrl     string     `json:"compare_url"`
	CommittedAt    *time.Time `json:"committed_at"`
	CommitterName  string     `json:"committer_name"`
	CommitterEmail string     `json:"committer_email"`
	AuthorName     string     `json:"author_name"`
	AuthorEmail    string     `json:"author_email"`
	Type           string     `json:"type"`
	BuildUrl       string     `json:"build_url"`
	// TODO(ttacon): there's a lot more here but i don't need it right now
}

/*
{
  "repository": {
    "id": 1,
    "name": "minimal",
    "owner_name": "svenfuchs",
    "url": "http://github.com/svenfuchs/minimal"
   },
  "config": {
    "notifications": {
      "webhooks": ["http://evome.fr/notifications", "http://example.com/"]
    }
  },
  "matrix": [
    {
      "id": 2,
      "repository_id": 1,
      "number": "1.1",
      "state": "created",
      "started_at": null,
      "finished_at": null,
      "config": {
        "notifications": {
          "webhooks": ["http://evome.fr/notifications", "http://example.com/"]
        }
      },
      "status": null,
      "log": "",
      "result": null,
      "parent_id": 1,
      "commit": "62aae5f70ceee39123ef",
      "branch": "master",
      "message": "the commit message",
      "committed_at": "2011-11-11T11: 11: 11Z",
      "committer_name": "Sven Fuchs",
      "committer_email": "svenfuchs@artweb-design.de",
      "author_name": "Sven Fuchs",
      "author_email": "svenfuchs@artweb-design.de",
      "compare_url": "https://github.com/svenfuchs/minimal/compare/master...develop"
    }
  ]
}
*/
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/codegangsta/martini"
	"github.com/martini-contrib/render"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	s "strings"
	"time"
)

const filename = "db.txt"
const baseURL = "https://github.com/karan/Projects/blob/master/README.md"

type Proj struct {
	Title       string
	Description string
}

func ScrapProj() {
	fmt.Println("scraping ..")
	doc, err := goquery.NewDocument(baseURL)
	if err != nil {
		log.Fatal(err)
	}

	if !isFileExist(filename) {
		// if the file doesn't exist, create a new one
		w, err := os.Create(filename)
		if err != nil {
			panic(err)
		}
		defer w.Close()

		// write empty one first
		err = ioutil.WriteFile(filename, []byte(""), 0644)

		doc.Find(".markdown-body p").Slice(7, 95).Each(func(i int, s *goquery.Selection) {
			txt := s.Text() + "\n"
			f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600)
			if err != nil {
				panic(err)
			}

			defer f.Close()

			if _, err = f.WriteString(txt); err != nil {
				panic(err)
			}
		})
	}
}

func ReadProjFromFile() []string {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	return s.Split(string(b), "\n")
}

func GetRandomProj(projs []string) Proj {
	i := random(0, len(projs))
	project := projs[i]

	title := s.Split(project, " - ")[0]
	desc := s.Split(project, " - ")[1]

	return Proj{title, desc}
}

func random(min, max int) int {
	rand.Seed(time.Now().UTC().UnixNano())
	return rand.Intn(max-min) + min
}

func main() {

	// If the file doesn't exist, scrap it
	if !isFileExist(filename) {
		ScrapProj()
	}

	m := martini.Classic()
	m.Use(render.Renderer(render.Options{
		Layout: "layout",
	}))

	m.Get("/suggest", func(ren render.Render) {
		ren.HTML(200, "index", GetRandomProj(ReadProjFromFile()))
	})

	m.Get("/", func(ren render.Render) {
		ren.HTML(200, "index", nil)
	})

	m.Run()
}

// Check if file already exists or not
func isFileExist(filename string) bool {
	if _, err := os.Stat(filename); err == nil {
		return true
	}
	return false
}
<commit_msg>temp fix<commit_after>package main

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/codegangsta/martini"
	"github.com/martini-contrib/render"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	s "strings"
	"time"
)

const filename = "db.txt"
const baseURL = "https://github.com/karan/Projects/blob/master/README.md"

type Proj struct {
	Title       string
	Description string
}

func ScrapProj() {
	fmt.Println("scraping ..")
	doc, err := goquery.NewDocument(baseURL)
	if err != nil {
		log.Fatal(err)
	}

	if !isFileExist(filename) {
		// if the file doesn't exist, create a new one
		w, err := os.Create(filename)
		if err != nil {
			panic(err)
		}
		defer w.Close()

		// write empty one first
		err = ioutil.WriteFile(filename, []byte(""), 0644)

		doc.Find(".markdown-body p").Slice(7, 95).Each(func(i int, s *goquery.Selection) {
			txt := s.Text() + "\n"
			f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600)
			if err != nil {
				panic(err)
			}

			defer f.Close()

			if _, err = f.WriteString(txt); err != nil {
				panic(err)
			}
		})
	}
}

func ReadProjFromFile() []string {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	return s.Split(string(b), "\n")
}

func GetRandomProj(projs []string) Proj {
	i := random(0, len(projs))

	fmt.Println("i is ", i)
	project := projs[i]

	if i == 88 {
		return Proj{
			"Find PI to the Nth Digit",
			"Enter a number and have the program generate PI up to that many decimal places. Keep a limit to how far the program will go.",
		}
	}

	title := s.Split(project, " - ")[0]
	fmt.Println("title ", title)
	desc := s.Split(project, " - ")[1]
	fmt.Println("desc ", desc)

	return Proj{title, desc}
}

func random(min, max int) int {
	rand.Seed(time.Now().UTC().UnixNano())
	return rand.Intn(max-min) + min
}

func main() {

	// If the file doesn't exist, scrap it
	if !isFileExist(filename) {
		ScrapProj()
	}

	m := martini.Classic()
	m.Use(render.Renderer(render.Options{
		Layout: "layout",
	}))

	m.Get("/suggest", func(ren render.Render) {
		ren.HTML(200, "index", GetRandomProj(ReadProjFromFile()))
	})

	m.Get("/", func(ren render.Render) {
		ren.HTML(200, "index", nil)
	})

	m.Run()
}

// Check if file already exists or not
func isFileExist(filename string) bool {
	if _, err := os.Stat(filename); err == nil {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	cb "github.com/clearblade/Go-SDK"
	"github.com/clearblade/cblib"
	"strings"
)

func init() {
	flag.StringVar(&cblib.URL, "url", "", "Set the URL of the platform you want to use")
	flag.BoolVar(&cblib.ShouldImportCollectionRows, "importrows", false, "If supplied the import command will transfer collection rows from the old system to the new system")
	flag.IntVar(&cblib.ImportPageSize, "pagesize", 100, "If supplied the import command will migrate the specified number of rows per request")
}

func main() {
	flag.Parse()
	if cblib.URL != "" {
		cb.CB_ADDR = cblib.URL
	} else {
		cblib.URL = cb.CB_ADDR
	}
	cmd := strings.ToLower(flag.Arg(0))
	var err error
	var sysKey, dir string

	switch cmd {
	case "auth":
		if err := cblib.Auth_cmd(); err != nil {
			fmt.Printf("Error authenticated: %v\n", err)
		}
	case "pull":
		if flag.NArg() != 2 {
			fmt.Printf("pull requires the systemKey as an argument\n")
		}
		if err := cblib.Pull_cmd(flag.Arg(1)); err != nil {
			fmt.Printf("Error pulling data: %v\n", err)
		}
	case "push":
		if flag.NArg() != 2 {
			sysKey, err = cblib.Sys_for_dir()
			if err != nil {
				fmt.Printf("%v\n", err)
			}
			dir = "."
		} else {
			dir = ""
			sysKey = flag.Arg(1)
		}
		if err := cblib.Push_cmd(sysKey, dir); err != nil {
			fmt.Printf("Error pushing: %v\n", err)
		}
	case "export":
		if flag.NArg() != 2 {
			fmt.Printf("export requires the systemKey as an argument\n")
		}
		if err := cblib.Export_cmd(flag.Arg(1), flag.Arg(2)); err != nil {
			fmt.Printf("Error export data: %v\n", err)
		}
	case "import":
		var sysKey, dir string
		var err error
		sysKey, err = cblib.Sys_for_dir()
		if err != nil {
			fmt.Printf("%v\n", err)
			fmt.Printf("%v\n", sysKey)
			return
		}
		dir = "."

		if err := cblib.Import_cmd(dir, flag.Arg(2)); err != nil {
			fmt.Printf("Error import data: %v\n", err)
		}
	default:
		fmt.Printf("Commands: 'auth', 'pull', 'push', 'export', 'import'\n")
	}
}
<commit_msg>added return after err<commit_after>package main

import (
	"flag"
	"fmt"
	cb "github.com/clearblade/Go-SDK"
	"github.com/clearblade/cblib"
	"strings"
)

func init() {
	flag.StringVar(&cblib.URL, "url", "", "Set the URL of the platform you want to use")
	flag.BoolVar(&cblib.ShouldImportCollectionRows, "importrows", false, "If supplied the import command will transfer collection rows from the old system to the new system")
	flag.IntVar(&cblib.ImportPageSize, "pagesize", 100, "If supplied the import command will migrate the specified number of rows per request")
}

func main() {
	flag.Parse()
	if cblib.URL != "" {
		cb.CB_ADDR = cblib.URL
	} else {
		cblib.URL = cb.CB_ADDR
	}
	cmd := strings.ToLower(flag.Arg(0))
	var err error
	var sysKey, dir string

	switch cmd {
	case "auth":
		if err := cblib.Auth_cmd(); err != nil {
			fmt.Printf("Error authenticated: %v\n", err)
		}
	case "pull":
		if flag.NArg() != 2 {
			fmt.Printf("pull requires the systemKey as an argument\n")
		}
		if err := cblib.Pull_cmd(flag.Arg(1)); err != nil {
			fmt.Printf("Error pulling data: %v\n", err)
		}
	case "push":
		if flag.NArg() != 2 {
			sysKey, err = cblib.Sys_for_dir()
			if err != nil {
				fmt.Printf("%v\n", err)
				return
			}
			dir = "."
		} else {
			dir = ""
			sysKey = flag.Arg(1)
		}
		if err := cblib.Push_cmd(sysKey, dir); err != nil {
			fmt.Printf("Error pushing: %v\n", err)
		}
	case "export":
		if flag.NArg() != 2 {
			fmt.Printf("export requires the systemKey as an argument\n")
		}
		if err := cblib.Export_cmd(flag.Arg(1), flag.Arg(2)); err != nil {
			fmt.Printf("Error export data: %v\n", err)
		}
	case "import":
		var sysKey, dir string
		var err error
		sysKey, err = cblib.Sys_for_dir()
		if err != nil {
			fmt.Printf("%v\n", err)
			fmt.Printf("%v\n", sysKey)
			return
		}
		dir = "."

		if err := cblib.Import_cmd(dir, flag.Arg(2)); err != nil {
			fmt.Printf("Error import data: %v\n", err)
		}
	default:
		fmt.Printf("Commands: 'auth', 'pull', 'push', 'export', 'import'\n")
	}
}
<|endoftext|>


<commit_before>package main
import (
	"net/http"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"log"
	"encoding/hex"
	"strings"
	"html/template"
	"bufio"
	"os"
	"sync"
	"time"
	"strconv"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
)

/* Given a random 128 bits, encrypt text with an AES cipher */
func encrypt(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	b := base64.StdEncoding.EncodeToString(text)
	ciphertext := make([]byte, aes.BlockSize+len(b))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}
	cfb := cipher.NewCFBEncrypter(block, iv)
	cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
	return ciphertext, nil
}

/* Given encrypted text and the AES cipher key, decrypt the text */
func decrypt(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	if len(text) < aes.BlockSize {
		return nil, errors.New("ciphertext too short")
	}
	iv := text[:aes.BlockSize]
	text = text[aes.BlockSize:]
	cfb := cipher.NewCFBDecrypter(block, iv)
	cfb.XORKeyStream(text, text)
	data, err := base64.StdEncoding.DecodeString(string(text))
	if err != nil {
		return nil, err
	}
	return data, nil
}

/* POST to /create (Curried with db) */
func createHandler (db *sql.DB) func(http.ResponseWriter, *http.Request) {
	return  func(w http.ResponseWriter, r *http.Request) {

		text := r.FormValue("text")
		if len(text) > 16000{
			writeError(w, "Message too long. Max character length is 16000.")
			return
		}

		/* Generate two random byte []
		   One as key to encrypt, the other as secret message identifier
		*/
		keyBytes := make([]byte, 16)
		secretBytes := make([]byte, 8)
		_, err1 := rand.Read(keyBytes)
		_, err2 := rand.Read(secretBytes)
		if err1 != nil {
			log.Fatal(err1)
		} else if err2 != nil {
			log.Fatal(err2)
		}

		/* Convert byte[] keys to Strings */
		keyString := hex.EncodeToString(keyBytes)
		secretString := hex.EncodeToString(secretBytes)

		/* Encrypt the text */
		encryptedtextBytes, err := encrypt(keyBytes, []byte(text))
		if err != nil {
			log.Fatal(err)
		}
		encryptedtext := hex.EncodeToString(encryptedtextBytes)

		/* Set expiration date */
		expireMinutes, err := strconv.Atoi(r.FormValue("expireMinutes"))
		var dt_delete time.Time
		if err != nil {
			dt_delete = time.Date(9999, 0, 0, 0, 0, 0, 0, time.FixedZone("UTC", 0))	/* Never expire */
		} else {
			dt_delete =  time.Now().Add(time.Minute * time.Duration(expireMinutes))
		}

		/* Insert message into db */
		_, err = db.Exec("insert into messages values (?, ?, ?, ?)", secretString, encryptedtext, time.Now(), dt_delete)
		if err != nil {
			log.Fatal(err)
		}

		/* Write HTML */
		type Out struct {
			Host string
			SecretString string
			KeyString string
		}

		data := Out{os.Getenv("EPHEMERAL_HOST"), secretString, keyString}
		tmpl := template.Must(template.ParseFiles("static/create.html", "static/top.html", "static/head.html"))
		tmpl.ExecuteTemplate(w, "create", data)
	}
}

/* GET to /view (Curried with db) */
func viewHandler(db *sql.DB) func(http.ResponseWriter, *http.Request) {
	return  func(w http.ResponseWriter, r *http.Request) {

		/* Blacklist sites that GET the url before sending to recipient */
		blacklist := [...]string{"facebook"}

		for _,e := range blacklist {
			if strings.Contains(r.UserAgent(), e) {
				fmt.Fprintf(w, "Go away %s! This is only for the recipient!", e)
				return
			}
		}

		/* get query params */
		queryString := strings.TrimSuffix(r.URL.Path[len("/view/"):],"/")
		params := strings.Split(queryString, "/")
		if len(params) != 2 {
			writeError(w, "Message not found. It may have been deleted.")
			return
		}
		msgSectet:= params[0]
		keyString := params[1]
		keyBytes, err := hex.DecodeString(keyString)
		if err != nil {
			fmt.Println("Key is not hex")
			writeError(w, "Message not found. It may have been deleted.")
			return
		}

		var m sync.Mutex
		m.Lock() /* ONLY ONE THREAD IN HERE AT A TIME */
		message, err := loadDecryptDelete(db, msgSectet, keyBytes)
		m.Unlock()	/*DONE */

		if err != nil{
			fmt.Println(err)
			writeError(w, "Message not found. It may have been deleted.")
			return
		} else {
			fmt.Println("Message Found!")
		}

		/* Write HTML */
		type Out struct {
			Success bool
			Message string
		}
		data := Out{true, message}
		tmpl := template.Must(template.ParseFiles("static/view.html", "static/top.html", "static/head.html"))
		tmpl.ExecuteTemplate(w, "view", data)
	}
}

/* Atomic transaction */
func loadDecryptDelete(db *sql.DB, msgSectet string, keyBytes []byte) (string, error){

	/* Lookup message in db */
	var encryptedtext string
	err := db.QueryRow("SELECT encryptedtext FROM messages WHERE secret = ?", msgSectet).Scan(&encryptedtext)
	if err != nil {
		return "", errors.New("No message found with msgSecret: " + msgSectet)
	}

	/* Decrypt message */
	encryptedtextBytes , err := hex.DecodeString(encryptedtext)
	if err != nil {
		return "", errors.New("Error converting encryptedext from string to byte[]")
	}
	messageBytes, err := decrypt(keyBytes, []byte(encryptedtextBytes))
	if err != nil {
		return "", errors.New("Valid msgSecret, but invalid key")
	}
	message:= string(messageBytes)

	/* Delete message from db */
	_, err = db.Exec("DELETE FROM messages WHERE secret = ? LIMIT 1", msgSectet)
	if err != nil {
		//Weird (given atomicity), but continue anyway.
	}

	return message, nil
}

/* Write the given error message as HTML */
func writeError(w http.ResponseWriter, message string){
	type Out struct {
		Message string
	}

	/* Write HTML */
	data := Out{message}
	tmpl := template.Must(template.ParseFiles("static/error.html", "static/top.html", "static/head.html"))
	tmpl.ExecuteTemplate(w, "error", data)
}

/* GET / */
func homeHandler(w http.ResponseWriter, r *http.Request) {
	tmpl := template.Must(template.ParseFiles("static/home.html", "static/top.html", "static/head.html"))
	tmpl.ExecuteTemplate(w, "home", nil)
}

/* GET /about */
func aboutHandler(w http.ResponseWriter, r *http.Request) {
	tmpl := template.Must(template.ParseFiles("static/about.html", "static/top.html", "static/head.html"))
	tmpl.ExecuteTemplate(w, "about", nil)
}

/*  Schema:
		secret VARCHAR(16),
		encryptedtext VARCHAR(43688),
		dt_created DATETIME,
		dt_delete DATETIME
*/
func connectDb() (*sql.DB, error){

	/* Load config file */
	file, err := os.Open("mysql.priv")
	defer file.Close()
	if err != nil {
		return nil, errors.New("Could not find mysql.priv")
	}

	bio := bufio.NewReader(file)
	tablename, _, err := bio.ReadLine()
	username, _, err := bio.ReadLine()
	password, _, err := bio.ReadLine()

	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@/%s", username, password, tablename))
	if err != nil {
		return nil, err
	}

	/* Actually try to connect */
	err = db.Ping()
	if err != nil {
		return nil, err
	}

	return db, nil
}

func main() {

	db, err := connectDb()
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/about/", aboutHandler)
	http.HandleFunc("/create/", createHandler(db))
	http.HandleFunc("/view/", viewHandler(db))
	fs := http.FileServer(http.Dir("static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))

	/* SSL/TLS */
	path_to_certificate := "/etc/nginx/ssl/concat_server_and_CA_certs.pem"
	path_to_key := "/etc/nginx/ssl/private.key"
	err = http.ListenAndServeTLS(":11994", path_to_certificate, path_to_key, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>add option to reply with only url, not html<commit_after>package main
import (
	"net/http"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"log"
	"encoding/hex"
	"strings"
	"html/template"
	"bufio"
	"os"
	"sync"
	"time"
	"strconv"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
)

/* Given a random 128 bits, encrypt text with an AES cipher */
func encrypt(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	b := base64.StdEncoding.EncodeToString(text)
	ciphertext := make([]byte, aes.BlockSize+len(b))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}
	cfb := cipher.NewCFBEncrypter(block, iv)
	cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
	return ciphertext, nil
}

/* Given encrypted text and the AES cipher key, decrypt the text */
func decrypt(key, text []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	if len(text) < aes.BlockSize {
		return nil, errors.New("ciphertext too short")
	}
	iv := text[:aes.BlockSize]
	text = text[aes.BlockSize:]
	cfb := cipher.NewCFBDecrypter(block, iv)
	cfb.XORKeyStream(text, text)
	data, err := base64.StdEncoding.DecodeString(string(text))
	if err != nil {
		return nil, err
	}
	return data, nil
}

/* POST to /create (Curried with db) */
func createHandler (db *sql.DB) func(http.ResponseWriter, *http.Request) {
	return  func(w http.ResponseWriter, r *http.Request) {

		text := r.FormValue("text")
		if len(text) > 16000{
			writeError(w, "Message too long. Max character length is 16000.")
			return
		}

		/* Generate two random byte []
		   One as key to encrypt, the other as secret message identifier
		*/
		keyBytes := make([]byte, 16)
		secretBytes := make([]byte, 8)
		_, err1 := rand.Read(keyBytes)
		_, err2 := rand.Read(secretBytes)
		if err1 != nil {
			log.Fatal(err1)
		} else if err2 != nil {
			log.Fatal(err2)
		}

		/* Convert byte[] keys to Strings */
		keyString := hex.EncodeToString(keyBytes)
		secretString := hex.EncodeToString(secretBytes)

		/* Encrypt the text */
		encryptedtextBytes, err := encrypt(keyBytes, []byte(text))
		if err != nil {
			log.Fatal(err)
		}
		encryptedtext := hex.EncodeToString(encryptedtextBytes)

		/* Set expiration date */
		expireMinutes, err := strconv.Atoi(r.FormValue("expireMinutes"))
		var dt_delete time.Time
		if err != nil {
			dt_delete = time.Date(9999, 0, 0, 0, 0, 0, 0, time.FixedZone("UTC", 0))	/* Never expire */
		} else {
			dt_delete =  time.Now().Add(time.Minute * time.Duration(expireMinutes))
		}

		/* Insert message into db */
		_, err = db.Exec("insert into messages values (?, ?, ?, ?)", secretString, encryptedtext, time.Now(), dt_delete)
		if err != nil {
			log.Fatal(err)
		}

		/* Write HTML */
		type Out struct {
			Host string
			SecretString string
			KeyString string
		}

		format := r.FormValue("format")
		if format == "url" {	/* Return only url */
			url := "https://" + os.Getenv("EPHEMERAL_HOST") + "/view/" +secretString + "/" + keyString
			w.Write([]byte(url))
		} else {	/* Return html */
			data := Out{os.Getenv("EPHEMERAL_HOST"), secretString, keyString}
			tmpl := template.Must(template.ParseFiles("static/create.html", "static/top.html", "static/head.html"))
			tmpl.ExecuteTemplate(w, "create", data)
		}
	}
}

/* GET to /view (Curried with db) */
func viewHandler(db *sql.DB) func(http.ResponseWriter, *http.Request) {
	return  func(w http.ResponseWriter, r *http.Request) {

		/* Blacklist sites that GET the url before sending to recipient */
		blacklist := [...]string{"facebook"}

		for _,e := range blacklist {
			if strings.Contains(r.UserAgent(), e) {
				fmt.Fprintf(w, "Go away %s! This is only for the recipient!", e)
				return
			}
		}

		/* get query params */
		queryString := strings.TrimSuffix(r.URL.Path[len("/view/"):],"/")
		params := strings.Split(queryString, "/")
		if len(params) != 2 {
			writeError(w, "Message not found. It may have been deleted.")
			return
		}
		msgSectet:= params[0]
		keyString := params[1]
		keyBytes, err := hex.DecodeString(keyString)
		if err != nil {
			fmt.Println("Key is not hex")
			writeError(w, "Message not found. It may have been deleted.")
			return
		}

		var m sync.Mutex
		m.Lock() /* ONLY ONE THREAD IN HERE AT A TIME */
		message, err := loadDecryptDelete(db, msgSectet, keyBytes)
		m.Unlock()	/*DONE */

		if err != nil{
			fmt.Println(err)
			writeError(w, "Message not found. It may have been deleted.")
			return
		} else {
			fmt.Println("Message Found!")
		}

		/* Write HTML */
		type Out struct {
			Success bool
			Message string
		}
		data := Out{true, message}
		tmpl := template.Must(template.ParseFiles("static/view.html", "static/top.html", "static/head.html"))
		tmpl.ExecuteTemplate(w, "view", data)
	}
}

/* Atomic transaction */
func loadDecryptDelete(db *sql.DB, msgSectet string, keyBytes []byte) (string, error){

	/* Lookup message in db */
	var encryptedtext string
	err := db.QueryRow("SELECT encryptedtext FROM messages WHERE secret = ?", msgSectet).Scan(&encryptedtext)
	if err != nil {
		return "", errors.New("No message found with msgSecret: " + msgSectet)
	}

	/* Decrypt message */
	encryptedtextBytes , err := hex.DecodeString(encryptedtext)
	if err != nil {
		return "", errors.New("Error converting encryptedext from string to byte[]")
	}
	messageBytes, err := decrypt(keyBytes, []byte(encryptedtextBytes))
	if err != nil {
		return "", errors.New("Valid msgSecret, but invalid key")
	}
	message:= string(messageBytes)

	/* Delete message from db */
	_, err = db.Exec("DELETE FROM messages WHERE secret = ? LIMIT 1", msgSectet)
	if err != nil {
		//Weird (given atomicity), but continue anyway.
	}

	return message, nil
}

/* Write the given error message as HTML */
func writeError(w http.ResponseWriter, message string){
	type Out struct {
		Message string
	}

	/* Write HTML */
	data := Out{message}
	tmpl := template.Must(template.ParseFiles("static/error.html", "static/top.html", "static/head.html"))
	tmpl.ExecuteTemplate(w, "error", data)
}

/* GET / */
func homeHandler(w http.ResponseWriter, r *http.Request) {
	tmpl := template.Must(template.ParseFiles("static/home.html", "static/top.html", "static/head.html"))
	tmpl.ExecuteTemplate(w, "home", nil)
}

/* GET /about */
func aboutHandler(w http.ResponseWriter, r *http.Request) {
	tmpl := template.Must(template.ParseFiles("static/about.html", "static/top.html", "static/head.html"))
	tmpl.ExecuteTemplate(w, "about", nil)
}

/*  Schema:
		secret VARCHAR(16),
		encryptedtext VARCHAR(43688),
		dt_created DATETIME,
		dt_delete DATETIME
*/
func connectDb() (*sql.DB, error){

	/* Load config file */
	file, err := os.Open("mysql.priv")
	defer file.Close()
	if err != nil {
		return nil, errors.New("Could not find mysql.priv")
	}

	bio := bufio.NewReader(file)
	tablename, _, err := bio.ReadLine()
	username, _, err := bio.ReadLine()
	password, _, err := bio.ReadLine()

	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@/%s", username, password, tablename))
	if err != nil {
		return nil, err
	}

	/* Actually try to connect */
	err = db.Ping()
	if err != nil {
		return nil, err
	}

	return db, nil
}

func main() {

	db, err := connectDb()
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/about/", aboutHandler)
	http.HandleFunc("/create/", createHandler(db))
	http.HandleFunc("/view/", viewHandler(db))
	fs := http.FileServer(http.Dir("static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))

	/* SSL/TLS */
	path_to_certificate := "/etc/nginx/ssl/concat_server_and_CA_certs.pem"
	path_to_key := "/etc/nginx/ssl/private.key"
	err = http.ListenAndServeTLS(":11994", path_to_certificate, path_to_key, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"net/url"

	"golang.org/x/oauth2"

	"github.com/google/go-github/github"
	"github.com/kellydunn/golang-geo"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

func main() {
	// Main command
	var storePath string
	var store *Store
	mainCmd := &cobra.Command{
		Use:   "ghr",
		Short: "Scrape GitHub for tech talent",
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			s, err := NewStore("./store.db")
			if err != nil {
				log.Fatal(err)
			}
			store = s
		},
	}

	mainCmd.PersistentFlags().StringVarP(&storePath, "store", "", "",
		"Path to datastore (SQLite3 database).")

	// Initialize datastore
	initCmd := &cobra.Command{
		Use:   "init",
		Short: "Initialize datastore",
		Run: func(cmd *cobra.Command, args []string) {
			store.Init()
		},
	}

	mainCmd.AddCommand(initCmd)

	// Common network options
	var proxy, token, userAgent string
	searchFlags := pflag.NewFlagSet("connection", pflag.ExitOnError)
	searchFlags.StringVarP(&proxy, "proxy", "", "",
		"HTTP proxy")
	searchFlags.StringVarP(&token, "token", "", "",
		"API token")
	searchFlags.StringVarP(&userAgent, "useragent", "", "github.com/mmcloughlin/ghr",
		"User agent. Please use your github username.")

	// Start a new search
	var searchQuery string
	searchCmd := &cobra.Command{
		Use:   "search --query=QUERY --proxy=PROXY --token=TOKEN --useragent=AGENT",
		Short: "Start a new query",
		Run: func(cmd *cobra.Command, args []string) {
			client := BuildHTTPClient(proxy, token)
			c := github.NewClient(client)
			c.UserAgent = userAgent

			s, err := store.NewSearch(searchQuery)
			if err != nil {
				log.Fatal(err)
			}

			Query(c, store, s)
		},
	}

	searchCmd.PersistentFlags().AddFlagSet(searchFlags)
	searchCmd.PersistentFlags().StringVarP(&searchQuery, "query", "", "",
		"Search keywords and qualifiers (https://developer.github.com/v3/search/#search-repositories)")

	mainCmd.AddCommand(searchCmd)

	// Resume a search
	var searchID uint
	resumeCmd := &cobra.Command{
		Use:   "resume --id=ID --proxy=PROXY --token=TOKEN --useragent=AGENT",
		Short: "Resume another query",
		Run: func(cmd *cobra.Command, args []string) {
			client := BuildHTTPClient(proxy, token)
			c := github.NewClient(client)
			c.UserAgent = userAgent

			s := &Search{}
			q := store.DB.First(s, searchID)
			if q.Error != nil {
				log.Fatal(q.Error)
			}

			Query(c, store, s)
		},
	}

	resumeCmd.PersistentFlags().AddFlagSet(searchFlags)
	resumeCmd.PersistentFlags().UintVarP(&searchID, "id", "", 0,
		"Search ID")

	mainCmd.AddCommand(resumeCmd)

	// Filter
	var mapquestApiKey string
	var lat, lng, radius float64
	filterCmd := &cobra.Command{
		Use:   "filter --mapquest_api_key=KEY --lat=LAT --lng=LNG --radius=RADIUS",
		Short: "Filter to good-looking prospects",
		Run: func(cmd *cobra.Command, args []string) {
			Filter(store, mapquestApiKey, geo.NewPoint(lat, lng), radius)
		},
	}

	filterCmd.PersistentFlags().StringVarP(&mapquestApiKey, "mapquest_api_key", "", "",
		"MapQuest API Key (for geolocation lookups).")
	filterCmd.PersistentFlags().Float64VarP(&lat, "lat", "", 0.0,
		"Latitude of center.")
	filterCmd.PersistentFlags().Float64VarP(&lng, "lng", "", 0.0,
		"Longitude of center.")
	filterCmd.PersistentFlags().Float64VarP(&radius, "radius", "", 100.0,
		"Distance from center (km)")
	mainCmd.AddCommand(filterCmd)

	// Execute
	_ = mainCmd.Execute()
}

func BuildHTTPClient(proxy, token string) *http.Client {
	var base http.RoundTripper
	base = http.DefaultTransport

	// Proxy layer
	if len(proxy) > 0 {
		u, _ := url.Parse(proxy)
		base = &http.Transport{
			Proxy: http.ProxyURL(u),
		}
	}

	// Authentication layer
	if len(token) > 0 {
		ts := oauth2.StaticTokenSource(
			&oauth2.Token{AccessToken: token},
		)
		base = &oauth2.Transport{
			Source: ts,
			Base:   base,
		}
	}

	// Rate limiting
	transport := &RateLimitedTransport{
		Base: base,
	}

	return &http.Client{
		Transport: transport,
	}
}

func Query(c *github.Client, store *Store, s *Search) {
	// scraper
	scraper := &Scraper{
		Client: c,
		Store:  store,
	}

	err := scraper.Scrape(s)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>remove hardcoded path to datastore<commit_after>package main

import (
	"log"
	"net/http"
	"net/url"

	"golang.org/x/oauth2"

	"github.com/google/go-github/github"
	"github.com/kellydunn/golang-geo"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

func main() {
	// Main command
	var storePath string
	var store *Store
	mainCmd := &cobra.Command{
		Use:   "ghr",
		Short: "Scrape GitHub for tech talent",
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			s, err := NewStore(storePath)
			if err != nil {
				log.Fatal(err)
			}
			store = s
		},
	}

	mainCmd.PersistentFlags().StringVarP(&storePath, "store", "", "",
		"Path to datastore (SQLite3 database).")

	// Initialize datastore
	initCmd := &cobra.Command{
		Use:   "init",
		Short: "Initialize datastore",
		Run: func(cmd *cobra.Command, args []string) {
			store.Init()
		},
	}

	mainCmd.AddCommand(initCmd)

	// Common network options
	var proxy, token, userAgent string
	searchFlags := pflag.NewFlagSet("connection", pflag.ExitOnError)
	searchFlags.StringVarP(&proxy, "proxy", "", "",
		"HTTP proxy")
	searchFlags.StringVarP(&token, "token", "", "",
		"API token")
	searchFlags.StringVarP(&userAgent, "useragent", "", "github.com/mmcloughlin/ghr",
		"User agent. Please use your github username.")

	// Start a new search
	var searchQuery string
	searchCmd := &cobra.Command{
		Use:   "search --query=QUERY --proxy=PROXY --token=TOKEN --useragent=AGENT",
		Short: "Start a new query",
		Run: func(cmd *cobra.Command, args []string) {
			client := BuildHTTPClient(proxy, token)
			c := github.NewClient(client)
			c.UserAgent = userAgent

			s, err := store.NewSearch(searchQuery)
			if err != nil {
				log.Fatal(err)
			}

			Query(c, store, s)
		},
	}

	searchCmd.PersistentFlags().AddFlagSet(searchFlags)
	searchCmd.PersistentFlags().StringVarP(&searchQuery, "query", "", "",
		"Search keywords and qualifiers (https://developer.github.com/v3/search/#search-repositories)")

	mainCmd.AddCommand(searchCmd)

	// Resume a search
	var searchID uint
	resumeCmd := &cobra.Command{
		Use:   "resume --id=ID --proxy=PROXY --token=TOKEN --useragent=AGENT",
		Short: "Resume another query",
		Run: func(cmd *cobra.Command, args []string) {
			client := BuildHTTPClient(proxy, token)
			c := github.NewClient(client)
			c.UserAgent = userAgent

			s := &Search{}
			q := store.DB.First(s, searchID)
			if q.Error != nil {
				log.Fatal(q.Error)
			}

			Query(c, store, s)
		},
	}

	resumeCmd.PersistentFlags().AddFlagSet(searchFlags)
	resumeCmd.PersistentFlags().UintVarP(&searchID, "id", "", 0,
		"Search ID")

	mainCmd.AddCommand(resumeCmd)

	// Filter
	var mapquestApiKey string
	var lat, lng, radius float64
	filterCmd := &cobra.Command{
		Use:   "filter --mapquest_api_key=KEY --lat=LAT --lng=LNG --radius=RADIUS",
		Short: "Filter to good-looking prospects",
		Run: func(cmd *cobra.Command, args []string) {
			Filter(store, mapquestApiKey, geo.NewPoint(lat, lng), radius)
		},
	}

	filterCmd.PersistentFlags().StringVarP(&mapquestApiKey, "mapquest_api_key", "", "",
		"MapQuest API Key (for geolocation lookups).")
	filterCmd.PersistentFlags().Float64VarP(&lat, "lat", "", 0.0,
		"Latitude of center.")
	filterCmd.PersistentFlags().Float64VarP(&lng, "lng", "", 0.0,
		"Longitude of center.")
	filterCmd.PersistentFlags().Float64VarP(&radius, "radius", "", 100.0,
		"Distance from center (km)")
	mainCmd.AddCommand(filterCmd)

	// Execute
	_ = mainCmd.Execute()
}

func BuildHTTPClient(proxy, token string) *http.Client {
	var base http.RoundTripper
	base = http.DefaultTransport

	// Proxy layer
	if len(proxy) > 0 {
		u, _ := url.Parse(proxy)
		base = &http.Transport{
			Proxy: http.ProxyURL(u),
		}
	}

	// Authentication layer
	if len(token) > 0 {
		ts := oauth2.StaticTokenSource(
			&oauth2.Token{AccessToken: token},
		)
		base = &oauth2.Transport{
			Source: ts,
			Base:   base,
		}
	}

	// Rate limiting
	transport := &RateLimitedTransport{
		Base: base,
	}

	return &http.Client{
		Transport: transport,
	}
}

func Query(c *github.Client, store *Store, s *Search) {
	// scraper
	scraper := &Scraper{
		Client: c,
		Store:  store,
	}

	err := scraper.Scrape(s)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"strings"

	"github.com/lair-framework/api-server/client"
	"github.com/lair-framework/go-lair"
	"github.com/lair-framework/go-nikto"
)

const (
	version = "2.0.0"
	tool    = "nikto"
	usage   = `
Usage:
  drone-nikto <id> <filename>
  export LAIR_ID=<id>; drone-nikto <filename>
Options:
  -v              show version and exit
  -h              show usage and exit
  -k              allow insecure SSL connections
  -force-ports    disable data protection in the API server for excessive ports
  -tags           a comma separated list of tags to add to every host that is imported
`
)

func buildProject(nikto *nikto.NiktoData, exproject *lair.Project, tags []string) (map[string]bool, error) {
	var bNotFound map[string]bool
	exproject.Tool = tool
	var command string

	for _, scan := range nikto.NiktoScan {
		for _, item := range scan.ScanDetails {
			command = scan.Options

			// Confirm if Host and Port existing in current project
			found := false
			hname := false
			for x, h := range exproject.Hosts {
				if item.TargetIP == h.IPv4 {
					for y, p := range h.Services {
						if item.TargetPort == p.Port {
							found = true

							// Append host tags
							exproject.Hosts[x].Tags = append(h.Tags, tags...)

							// Update Modified By
							h.LastModifiedBy = tool
							p.LastModifiedBy = tool

							// Append Note information to Service
							note := &lair.Note{
								Title:          fmt.Sprintf("Nikto v%v (%v:%v)", scan.Version, item.HostHeader, item.TargetPort),
								LastModifiedBy: tool,
							}

							scheme, err := url.Parse(item.TargetHostname)
							if err != nil {
								return bNotFound, err
							}
							if scheme.Scheme == "https" {
								note.Content = fmt.Sprintf("SSL Information:\nSubject: %v\nCiphers: %v\nIssuer: %v\n\n", item.SSL.Info, item.SSL.Ciphers, item.SSL.Issuers)
							}

							for _, item := range item.Items {
								if item.OSVDBID > 0 {
									note.Content += fmt.Sprintf("%v URI: %v OSVDBID: %v\n", item.Description, item.URI, item.OSVDBID)
								} else {
									note.Content += fmt.Sprintf("%v URI: %v\n", item.Description, item.URI)
								}
							}
							note.Content += fmt.Sprintf("\nStart: %s End: %s", scan.ScanStart, scan.ScanEnd)
							exproject.Hosts[x].Services[y].Notes = append(p.Notes, *note)
						}
					}
					// Check for existing hostname and append
					for _, n := range h.Hostnames {
						if n == item.TargetHostname {
							hname = true
						}
					}
					if !hname && (item.TargetHostname != item.TargetIP) {
						exproject.Hosts[x].Hostnames = append(h.Hostnames, item.TargetHostname)
					}
				}
			}
			if !found {
				bNotFound[fmt.Sprintf("%s (%s:%v)", item.TargetHostname, item.TargetIP, item.TargetPort)] = true
				continue
			}
		}
	}
	// Update project with Nikto command
	com := make([]lair.Command, 0)
	com = append(com, lair.Command{
		Tool:    tool,
		Command: command,
	})
	exproject.Commands = com

	return bNotFound, nil
}

func main() {
	showVersion := flag.Bool("v", false, "")
	insecureSSL := flag.Bool("k", false, "")
	forcePorts := flag.Bool("force-ports", false, "")
	tags := flag.String("tags", "", "")
	flag.Usage = func() {
		fmt.Println(usage)
	}
	flag.Parse()
	if *showVersion {
		log.Println(version)
		os.Exit(0)
	}
	lairURL := os.Getenv("LAIR_API_SERVER")
	if lairURL == "" {
		log.Fatal("Fatal: Missing LAIR_API_SERVER environment variable")
	}
	lairPID := os.Getenv("LAIR_ID")
	var filename string
	switch len(flag.Args()) {
	case 2:
		lairPID = flag.Arg(0)
		filename = flag.Arg(1)
	case 1:
		filename = flag.Arg(0)
	default:
		log.Fatal("Fatal: Missing required argument")
	}

	u, err := url.Parse(lairURL)
	if err != nil {
		log.Fatalf("Fatal: Error parsing LAIR_API_SERVER URL. Error %s", err.Error())
	}
	if u.User == nil {
		log.Fatal("Fatal: Missing username and/or password")
	}
	user := u.User.Username()
	pass, _ := u.User.Password()
	if user == "" || pass == "" {
		log.Fatal("Fatal: Missing username and/or password")
	}
	c, err := client.New(&client.COptions{
		User:               user,
		Password:           pass,
		Host:               u.Host,
		Scheme:             u.Scheme,
		InsecureSkipVerify: *insecureSSL,
	})
	if err != nil {
		log.Fatalf("Fatal: Error setting up client. Error %s", err.Error())
	}

	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalf("Fatal: Could not open file. Error %s", err.Error())
	}

	niktoData, err := nikto.Parse(buf)
	if err != nil {
		log.Fatalf("Fatal: Error parsing nikto data. Error %s", err.Error())
	}

	hostTags := []string{}
	if *tags != "" {
		hostTags = strings.Split(*tags, ",")
	}

	project, err := c.ExportProject(lairPID)
	if err != nil {
		log.Fatalf("Fatal: Unable to export project. Error %s", err.Error())
	}
	bNotFound, err := buildProject(niktoData, &project, hostTags)
	if err != nil {
		log.Fatal(err.Error())
	}

	res, err := c.ImportProject(&client.DOptions{ForcePorts: *forcePorts}, &project)
	if err != nil {
		log.Fatalf("Fatal: Unable to import project. Error %s", err)
	}

	defer res.Body.Close()
	droneRes := &client.Response{}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatalf("Fatal: Error %s", err.Error())
	}
	if err := json.Unmarshal(body, droneRes); err != nil {
		log.Fatalf("Fatal: Could not unmarshal JSON. Error %s", err.Error())
	}
	if droneRes.Status == "Error" {
		log.Fatalf("Fatal: Import failed. Error %s", droneRes.Message)
	}

	if len(bNotFound) > 0 {
		log.Printf("Info: The following host ports contained Nikto results but did not exist in project %s", lairPID)
	}
	for k := range bNotFound {
		fmt.Println(k)
	}

	log.Println("Success: Operation completed successfully")
}
<commit_msg>Correct nil reference<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"go-nikto"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"strings"

	"github.com/lair-framework/api-server/client"
	"github.com/lair-framework/go-lair"
)

const (
	version = "2.0.1"
	tool    = "nikto"
	usage   = `
Usage:
  drone-nikto <id> <filename>
  export LAIR_ID=<id>; drone-nikto <filename>
Options:
  -v              show version and exit
  -h              show usage and exit
  -k              allow insecure SSL connections
  -force-ports    disable data protection in the API server for excessive ports
  -tags           a comma separated list of tags to add to every host that is imported
`
)

func buildProject(nikto *nikto.NiktoData, exproject *lair.Project, tags []string) (map[string]bool, error) {
	bNotFound := make(map[string]bool)
	exproject.Tool = tool
	var command string

	for _, scan := range nikto.NiktoScan {
		for _, item := range scan.ScanDetails {
			command = scan.Options

			// Confirm if Host and Port existing in current project
			found := false
			hname := false
			for x, h := range exproject.Hosts {
				if item.TargetIP == h.IPv4 {
					for y, p := range h.Services {
						if item.TargetPort == p.Port {
							found = true

							// Append host tags
							exproject.Hosts[x].Tags = append(h.Tags, tags...)

							// Update Modified By
							h.LastModifiedBy = tool
							p.LastModifiedBy = tool

							// Append Note information to Service
							note := &lair.Note{
								Title:          fmt.Sprintf("Nikto v%v (%v:%v)", scan.Version, item.HostHeader, item.TargetPort),
								LastModifiedBy: tool,
							}

							scheme, err := url.Parse(item.TargetHostname)
							if err != nil {
								return bNotFound, err
							}
							if scheme.Scheme == "https" {
								note.Content = fmt.Sprintf("SSL Information:\nSubject: %v\nCiphers: %v\nIssuer: %v\n\n", item.SSL.Info, item.SSL.Ciphers, item.SSL.Issuers)
							}

							for _, item := range item.Items {
								if item.OSVDBID > 0 {
									note.Content += fmt.Sprintf("%v URI: %v OSVDBID: %v\n", item.Description, item.URI, item.OSVDBID)
								} else {
									note.Content += fmt.Sprintf("%v URI: %v\n", item.Description, item.URI)
								}
							}
							note.Content += fmt.Sprintf("\nStart: %s End: %s", scan.ScanStart, scan.ScanEnd)
							exproject.Hosts[x].Services[y].Notes = append(p.Notes, *note)
						}
					}
					// Check for existing hostname and append
					for _, n := range h.Hostnames {
						if n == item.TargetHostname {
							hname = true
						}
					}
					if !hname && (item.TargetHostname != item.TargetIP) {
						exproject.Hosts[x].Hostnames = append(h.Hostnames, item.TargetHostname)
					}
				}
			}
			if !found {
				bNotFound[fmt.Sprintf("%s (%s:%v)", item.TargetHostname, item.TargetIP, item.TargetPort)] = true
				continue
			}
		}
	}
	// Update project with Nikto command
	var com []lair.Command
	com = append(com, lair.Command{
		Tool:    tool,
		Command: command,
	})
	exproject.Commands = com

	return bNotFound, nil
}

func main() {
	showVersion := flag.Bool("v", false, "")
	insecureSSL := flag.Bool("k", false, "")
	forcePorts := flag.Bool("force-ports", false, "")
	tags := flag.String("tags", "", "")
	flag.Usage = func() {
		fmt.Println(usage)
	}
	flag.Parse()
	if *showVersion {
		log.Println(version)
		os.Exit(0)
	}
	lairURL := os.Getenv("LAIR_API_SERVER")
	if lairURL == "" {
		log.Fatal("Fatal: Missing LAIR_API_SERVER environment variable")
	}
	lairPID := os.Getenv("LAIR_ID")
	var filename string
	switch len(flag.Args()) {
	case 2:
		lairPID = flag.Arg(0)
		filename = flag.Arg(1)
	case 1:
		filename = flag.Arg(0)
	default:
		log.Fatal("Fatal: Missing required argument")
	}

	u, err := url.Parse(lairURL)
	if err != nil {
		log.Fatalf("Fatal: Error parsing LAIR_API_SERVER URL. Error %s", err.Error())
	}
	if u.User == nil {
		log.Fatal("Fatal: Missing username and/or password")
	}
	user := u.User.Username()
	pass, _ := u.User.Password()
	if user == "" || pass == "" {
		log.Fatal("Fatal: Missing username and/or password")
	}
	c, err := client.New(&client.COptions{
		User:               user,
		Password:           pass,
		Host:               u.Host,
		Scheme:             u.Scheme,
		InsecureSkipVerify: *insecureSSL,
	})
	if err != nil {
		log.Fatalf("Fatal: Error setting up client. Error %s", err.Error())
	}

	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalf("Fatal: Could not open file. Error %s", err.Error())
	}

	niktoData, err := nikto.Parse(buf)
	if err != nil {
		log.Fatalf("Fatal: Error parsing nikto data. Error %s", err.Error())
	}

	hostTags := []string{}
	if *tags != "" {
		hostTags = strings.Split(*tags, ",")
	}

	project, err := c.ExportProject(lairPID)
	if err != nil {
		log.Fatalf("Fatal: Unable to export project. Error %s", err.Error())
	}
	bNotFound, err := buildProject(niktoData, &project, hostTags)
	if err != nil {
		log.Fatal(err.Error())
	}

	res, err := c.ImportProject(&client.DOptions{ForcePorts: *forcePorts}, &project)
	if err != nil {
		log.Fatalf("Fatal: Unable to import project. Error %s", err)
	}

	defer res.Body.Close()
	droneRes := &client.Response{}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatalf("Fatal: Error %s", err.Error())
	}
	if err := json.Unmarshal(body, droneRes); err != nil {
		log.Fatalf("Fatal: Could not unmarshal JSON. Error %s", err.Error())
	}
	if droneRes.Status == "Error" {
		log.Fatalf("Fatal: Import failed. Error %s", droneRes.Message)
	}

	if len(bNotFound) > 0 {
		log.Printf("Info: The following host ports contained Nikto results but did not exist in project %s", lairPID)
	}
	for k := range bNotFound {
		fmt.Println(k)
	}

	log.Println("Success: Operation completed successfully")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"

	"github.com/maxmind/geoip2-csv-converter/convert"
)

func main() {
	required := "REQUIRED"
	input := flag.String("block-file", required, "The path to the block CSV file to use as input")
	output := flag.String("output-file", required, "The path to the output CSV")
	ipRange := flag.Bool("include-range", false, "Include the IP range of the network in string format")
	intRange := flag.Bool("include-integer-range", false, "Include the IP range of the network in integer format")
	cidr := flag.Bool("include-cidr", false, "Include the network in CIDR format")

	flag.Parse()

	if *input == required || *output == required {
		printHelp()
		return
	}

	if !*ipRange && !*intRange && !*cidr {
		printHelp()
		return
	}

	err := convert.ConvertFile(*input, *output, *cidr, *ipRange, *intRange)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	}
}

func printHelp() {
	flag.PrintDefaults()
	fmt.Println("\nAt least one of -include-* param is required")

}
<commit_msg>Try to improve error output when require flag is missing<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/maxmind/geoip2-csv-converter/convert"
)

func main() {
	input := flag.String("block-file", "", "The path to the block CSV file to use as input (REQUIRED)")
	output := flag.String("output-file", "", "The path to the output CSV (REQUIRED)")
	ipRange := flag.Bool("include-range", false, "Include the IP range of the network in string format")
	intRange := flag.Bool("include-integer-range", false, "Include the IP range of the network in integer format")
	cidr := flag.Bool("include-cidr", false, "Include the network in CIDR format")

	flag.Parse()

	var errors []string

	if *input == "" {
		errors = append(errors, "-block-file is required")
	}

	if *output == "" {
		errors = append(errors, "-output-file is required")
	}

	if !*ipRange && !*intRange && !*cidr {
		errors = append(errors, "At least one of -include-* param is required")
	}

	if len(errors) != 0 {
		printHelp(errors)
		os.Exit(1)
	}

	err := convert.ConvertFile(*input, *output, *cidr, *ipRange, *intRange)
	if err != nil {
		fmt.Fprintf(flag.CommandLine.Output(), "Error: %v\n", err)
	}
}

func printHelp(errors []string) {
	for _, message := range errors {
		fmt.Fprintln(flag.CommandLine.Output(), message)
	}
	flag.PrintDefaults()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

var (
	cmdName    = "msub"
	cmdVersion = "0.3.0"

	flagset     = flag.NewFlagSet(cmdName, flag.ContinueOnError)
	useBoundary = flagset.Bool("boundary", false, "")
	isHelp      = flagset.Bool("help", false, "")
	isVersion   = flagset.Bool("version", false, "")
)

func init() {
	flagset.SetOutput(ioutil.Discard)
	flagset.BoolVar(useBoundary, "b", false, "")
	flagset.BoolVar(isHelp, "h", false, "")
	flagset.BoolVar(isVersion, "v", false, "")
}

func printUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %[1]s [OPTION]... FROM TO [FILE]...
Substitute multiple words at once
by FROM and TO patterns.

Options:
  -b, --boundary    use word boundary in matcher
  -h, --help        show this help message and exit
  -v, --version     output version information and exit

Syntax:
  pattern = group , { "/" , group } ;
  group   = branch , { "," , branch } ;
  branch  = { [ "\" ] , ? unicode character ? - "/" - "," | "\/" | "\," } ;

Examples:
  %[1]s true,false false,true ./file
  %[1]s dog,cat/s cat,dog/s ~/Document/questionnaire
`[1:], cmdName)
}

func printVersion() {
	fmt.Fprintln(os.Stderr, cmdVersion)
}

func printErr(err interface{}) {
	fmt.Fprintf(os.Stderr, "%s: %s\n", cmdName, err)
}

func do(rep *Replacer, r io.Reader) error {
	bs := bufio.NewScanner(r)
	bw := bufio.NewWriter(os.Stdout)
	for bs.Scan() {
		if _, err := bw.WriteString(rep.Replace(bs.Text()) + "\n"); err != nil {
			return err
		}
	}
	if err := bs.Err(); err != nil {
		return err
	}
	if err := bw.Flush(); err != nil {
		return err
	}
	return nil
}

func _main() int {
	if err := flagset.Parse(os.Args[1:]); err != nil {
		printErr(err)
		return 2
	}
	if *isHelp {
		printUsage()
		return 0
	}
	if *isVersion {
		printVersion()
		return 0
	}

	if flagset.NArg() < 1 {
		printErr("no specify FROM and TO")
		return 2
	}
	if flagset.NArg() < 2 {
		printErr("no specify TO")
		return 2
	}
	from, to := flagset.Arg(0), flagset.Arg(1)

	rep, err := NewReplacer(from, to, *useBoundary)
	if err != nil {
		printErr(err)
		return 2
	}

	var r io.Reader
	if flagset.NArg() < 3 {
		r = os.Stdin
	} else {
		var a []io.Reader
		for _, file := range flagset.Args()[2:] {
			f, err := os.Open(file)
			if err != nil {
				printErr(err)
				return 1
			}
			defer f.Close()
			a = append(a, f)
		}
		r = io.MultiReader(a...)
	}

	if err = do(rep, r); err != nil {
		printErr(err)
		return 1
	}
	return 0
}

func main() {
	e := _main()
	os.Exit(e)
}
<commit_msg>Rewrite to be testable<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

const (
	cmdName    = "msub"
	cmdVersion = "0.3.0"
)

type CLI struct {
	stdin  io.Reader
	stdout io.Writer
	stderr io.Writer

	useBoundary bool
	isHelp      bool
	isVersion   bool
}

func NewCLI(stdin io.Reader, stdout io.Writer, stderr io.Writer) *CLI {
	return &CLI{
		stdin:  stdin,
		stdout: stdout,
		stderr: stderr,
	}
}

func (c *CLI) parseOptions(args []string) (leftArgs []string, err error) {
	f := flag.NewFlagSet(cmdName, flag.ContinueOnError)
	f.SetOutput(ioutil.Discard)

	f.BoolVar(&c.useBoundary, "b", false, "")
	f.BoolVar(&c.useBoundary, "boundary", false, "")
	f.BoolVar(&c.isHelp, "h", false, "")
	f.BoolVar(&c.isHelp, "help", false, "")
	f.BoolVar(&c.isVersion, "v", false, "")
	f.BoolVar(&c.isVersion, "version", false, "")

	if err = f.Parse(args); err != nil {
		return nil, err
	}
	return f.Args(), nil
}

func (c *CLI) printUsage() {
	fmt.Fprintf(c.stderr, `
Usage: %[1]s [OPTION]... FROM TO [FILE]...
Substitute multiple words at once
by FROM and TO patterns.

Options:
  -b, --boundary    use word boundary in matcher
  -h, --help        show this help message and exit
  -v, --version     output version information and exit

Syntax:
  pattern = group , { "/" , group } ;
  group   = branch , { "," , branch } ;
  branch  = { [ "\" ] , ? unicode character ? - "/" - "," | "\/" | "\," } ;

Examples:
  %[1]s true,false false,true ./file
  %[1]s dog,cat/s cat,dog/s ~/Document/questionnaire
`[1:], cmdName)
}

func (c *CLI) printVersion() {
	fmt.Fprintf(c.stderr, "%s\n", cmdVersion)
}

func (c *CLI) printErr(err interface{}) {
	fmt.Fprintf(c.stderr, "%s: %s\n", cmdName, err)
}

func (c *CLI) do(rep *Replacer, r io.Reader) error {
	bs := bufio.NewScanner(r)
	for bs.Scan() {
		fmt.Fprintln(c.stdout, rep.Replace(bs.Text()))
	}
	return bs.Err()
}

func (c *CLI) Run(args []string) int {
	leftArgs, err := c.parseOptions(args)
	if err != nil {
		c.printErr(err)
		return 2
	}

	if c.isHelp {
		c.printUsage()
		return 0
	}
	if c.isVersion {
		c.printVersion()
		return 0
	}

	if len(leftArgs) < 1 {
		c.printErr("no specify FROM and TO")
		return 2
	}
	if len(leftArgs) < 2 {
		c.printErr("no specify TO")
		return 2
	}

	srcPattern := leftArgs[0]
	dstPattern := leftArgs[1]
	filePathes := leftArgs[2:]

	rep, err := NewReplacer(srcPattern, dstPattern, c.useBoundary)
	if err != nil {
		c.printErr(err)
		return 2
	}

	if len(filePathes) == 0 {
		if err = c.do(rep, c.stdin); err != nil {
			c.printErr(err)
			return 1
		}
	} else {
		var rs []io.Reader
		for _, filePath := range filePathes {
			f, err := os.Open(filePath)
			if err != nil {
				c.printErr(err)
				return 1
			}
			defer f.Close()

			rs = append(rs, f)
		}

		r := io.MultiReader(rs...)
		if err = c.do(rep, r); err != nil {
			c.printErr(err)
			return 1
		}
	}
	return 0
}

func main() {
	c := NewCLI(os.Stdin, os.Stdout, os.Stderr)
	e := c.Run(os.Args[1:])
	os.Exit(e)
}
<|endoftext|>


<commit_before>// Copyright 2017 HyperHQ Inc.
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log/syslog"
	"os"
	"os/signal"
	"runtime"
	"sync"
	"time"

	"github.com/sirupsen/logrus"
	lSyslog "github.com/sirupsen/logrus/hooks/syslog"
)

const (
	shimName    = "kata-shim"
	exitFailure = 1
	// Max number of threads the shim should consume.
	// We choose 6 as we want a couple of threads for the runtime (gc etc.)
	// and couple of threads for our parallel user code, such as the copy
	// code in shim.go
	maxThreads = 6
)

// version is the shim version. This variable is populated at build time.
var version = "unknown"

var debug bool

// if true, coredump when an internal error occurs or a fatal signal is received
var crashOnError = false

var shimLog *logrus.Entry

func logger() *logrus.Entry {
	return shimLog
}

func initLogger(logLevel, container, execID string, announceFields logrus.Fields, loggerOutput io.Writer) error {
	shimLog = logrus.WithFields(logrus.Fields{
		"name":      shimName,
		"pid":       os.Getpid(),
		"source":    "shim",
		"container": container,
		"exec-id":   execID,
	})

	shimLog.Logger.Formatter = &logrus.TextFormatter{TimestampFormat: time.RFC3339Nano}

	level, err := logrus.ParseLevel(logLevel)
	if err != nil {
		return err
	}

	shimLog.Logger.SetLevel(level)

	shimLog.Logger.Out = loggerOutput

	hook, err := lSyslog.NewSyslogHook("", "", syslog.LOG_INFO|syslog.LOG_USER, shimName)
	if err == nil {
		shimLog.Logger.AddHook(hook)
	}

	logger().WithFields(announceFields).Info("announce")

	return nil
}

func setThreads() {
	// If GOMAXPROCS has not been set, restrict our thread usage
	// so we don't grow many idle threads on large core count systems,
	// which un-necessarily consume host PID space (and thus set an
	// artificial max limit on the number of concurrent containers we can
	// run)
	if os.Getenv("GOMAXPROCS") == "" {
		if runtime.NumCPU() > maxThreads {
			runtime.GOMAXPROCS(maxThreads)
		}
	}
}

func realMain() {
	var (
		logLevel      string
		agentAddr     string
		container     string
		execID        string
		terminal      bool
		proxyExitCode bool
		showVersion   bool
	)

	setThreads()

	flag.BoolVar(&debug, "debug", false, "enable debug mode")
	flag.BoolVar(&showVersion, "version", false, "display program version and exit")
	flag.StringVar(&logLevel, "log", "warn", "set shim log level: debug, info, warn, error, fatal or panic")
	flag.StringVar(&agentAddr, "agent", "", "agent gRPC socket endpoint")

	flag.StringVar(&container, "container", "", "container id for the shim")
	flag.StringVar(&execID, "exec-id", "", "process id for the shim")
	flag.BoolVar(&terminal, "terminal", false, "specify if a terminal is setup")
	flag.BoolVar(&proxyExitCode, "proxy-exit-code", true, "proxy exit code of the process")

	flag.Parse()

	if showVersion {
		fmt.Printf("%v version %v\n", shimName, version)
		os.Exit(0)
	}

	if logLevel == "debug" {
		debug = true
	}

	if debug {
		crashOnError = true
	}

	if agentAddr == "" || container == "" || execID == "" {
		logger().WithField("agentAddr", agentAddr).WithField("container", container).WithField("exec-id", execID).Error("container ID, exec ID and agent socket endpoint must be set")
		os.Exit(exitFailure)
	}

	announceFields := logrus.Fields{
		"version":         version,
		"debug":           debug,
		"log-level":       logLevel,
		"agent-socket":    agentAddr,
		"terminal":        terminal,
		"proxy-exit-code": proxyExitCode,
	}

	// The final parameter makes sure all output going to stdout/stderr is discarded.
	err := initLogger(logLevel, container, execID, announceFields, ioutil.Discard)
	if err != nil {
		logger().WithError(err).WithField("loglevel", logLevel).Error("invalid log level")
		os.Exit(exitFailure)
	}

	shim, err := newShim(agentAddr, container, execID)
	if err != nil {
		logger().WithError(err).Error("failed to create new shim")
		os.Exit(exitFailure)
	}

	// winsize
	if terminal {
		termios, err := setupTerminal(int(os.Stdin.Fd()))
		if err != nil {
			logger().WithError(err).Error("failed to set raw terminal")
			os.Exit(exitFailure)
		}
		defer restoreTerminal(int(os.Stdin.Fd()), termios)
	}

	// signals
	sigc := shim.handleSignals(os.Stdin)
	defer signal.Stop(sigc)

	// This wait call cannot be deferred and has to wait for every
	// input/output to return before the code tries to go further
	// and wait for the process. Indeed, after the process has been
	// waited for, we cannot expect to do any more calls related to
	// this process since it is going to be removed from the agent.
	wg := &sync.WaitGroup{}
	shim.proxyStdio(wg, terminal)
	wg.Wait()

	// wait until exit
	exitcode, err := shim.wait()
	if err != nil {
		logger().WithError(err).WithField("exec-id", execID).Error("failed waiting for process")
		os.Exit(exitFailure)
	} else if proxyExitCode {
		logger().WithField("exitcode", exitcode).Info("using shim to proxy exit code")
		if exitcode != 0 {
			os.Exit(int(exitcode))
		}
	}
}

func main() {
	defer handlePanic()
	realMain()
}
<commit_msg>main: Return exitcode rather than exiting<commit_after>// Copyright 2017 HyperHQ Inc.
//
// SPDX-License-Identifier: Apache-2.0
//

package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log/syslog"
	"os"
	"os/signal"
	"runtime"
	"sync"
	"time"

	"github.com/sirupsen/logrus"
	lSyslog "github.com/sirupsen/logrus/hooks/syslog"
)

const (
	shimName    = "kata-shim"
	exitFailure = 1
	exitSuccess = 0
	// Max number of threads the shim should consume.
	// We choose 6 as we want a couple of threads for the runtime (gc etc.)
	// and couple of threads for our parallel user code, such as the copy
	// code in shim.go
	maxThreads = 6
)

// version is the shim version. This variable is populated at build time.
var version = "unknown"

var debug bool

// if true, coredump when an internal error occurs or a fatal signal is received
var crashOnError = false

var shimLog *logrus.Entry

func logger() *logrus.Entry {
	return shimLog
}

func initLogger(logLevel, container, execID string, announceFields logrus.Fields, loggerOutput io.Writer) error {
	shimLog = logrus.WithFields(logrus.Fields{
		"name":      shimName,
		"pid":       os.Getpid(),
		"source":    "shim",
		"container": container,
		"exec-id":   execID,
	})

	shimLog.Logger.Formatter = &logrus.TextFormatter{TimestampFormat: time.RFC3339Nano}

	level, err := logrus.ParseLevel(logLevel)
	if err != nil {
		return err
	}

	shimLog.Logger.SetLevel(level)

	shimLog.Logger.Out = loggerOutput

	hook, err := lSyslog.NewSyslogHook("", "", syslog.LOG_INFO|syslog.LOG_USER, shimName)
	if err == nil {
		shimLog.Logger.AddHook(hook)
	}

	logger().WithFields(announceFields).Info("announce")

	return nil
}

func setThreads() {
	// If GOMAXPROCS has not been set, restrict our thread usage
	// so we don't grow many idle threads on large core count systems,
	// which un-necessarily consume host PID space (and thus set an
	// artificial max limit on the number of concurrent containers we can
	// run)
	if os.Getenv("GOMAXPROCS") == "" {
		if runtime.NumCPU() > maxThreads {
			runtime.GOMAXPROCS(maxThreads)
		}
	}
}

func realMain() (exitCode int) {
	var (
		logLevel      string
		agentAddr     string
		container     string
		execID        string
		terminal      bool
		proxyExitCode bool
		showVersion   bool
	)

	setThreads()

	flag.BoolVar(&debug, "debug", false, "enable debug mode")
	flag.BoolVar(&showVersion, "version", false, "display program version and exit")
	flag.StringVar(&logLevel, "log", "warn", "set shim log level: debug, info, warn, error, fatal or panic")
	flag.StringVar(&agentAddr, "agent", "", "agent gRPC socket endpoint")

	flag.StringVar(&container, "container", "", "container id for the shim")
	flag.StringVar(&execID, "exec-id", "", "process id for the shim")
	flag.BoolVar(&terminal, "terminal", false, "specify if a terminal is setup")
	flag.BoolVar(&proxyExitCode, "proxy-exit-code", true, "proxy exit code of the process")

	flag.Parse()

	if showVersion {
		fmt.Printf("%v version %v\n", shimName, version)
		return exitSuccess
	}

	if logLevel == "debug" {
		debug = true
	}

	if debug {
		crashOnError = true
	}

	if agentAddr == "" || container == "" || execID == "" {
		logger().WithField("agentAddr", agentAddr).WithField("container", container).WithField("exec-id", execID).Error("container ID, exec ID and agent socket endpoint must be set")
		return exitFailure
	}

	announceFields := logrus.Fields{
		"version":         version,
		"debug":           debug,
		"log-level":       logLevel,
		"agent-socket":    agentAddr,
		"terminal":        terminal,
		"proxy-exit-code": proxyExitCode,
	}

	// The final parameter makes sure all output going to stdout/stderr is discarded.
	err := initLogger(logLevel, container, execID, announceFields, ioutil.Discard)
	if err != nil {
		logger().WithError(err).WithField("loglevel", logLevel).Error("invalid log level")
		return exitFailure
	}

	shim, err := newShim(agentAddr, container, execID)
	if err != nil {
		logger().WithError(err).Error("failed to create new shim")
		return exitFailure
	}

	// winsize
	if terminal {
		termios, err := setupTerminal(int(os.Stdin.Fd()))
		if err != nil {
			logger().WithError(err).Error("failed to set raw terminal")
			return exitFailure
		}
		defer restoreTerminal(int(os.Stdin.Fd()), termios)
	}

	// signals
	sigc := shim.handleSignals(os.Stdin)
	defer signal.Stop(sigc)

	// This wait call cannot be deferred and has to wait for every
	// input/output to return before the code tries to go further
	// and wait for the process. Indeed, after the process has been
	// waited for, we cannot expect to do any more calls related to
	// this process since it is going to be removed from the agent.
	wg := &sync.WaitGroup{}
	shim.proxyStdio(wg, terminal)
	wg.Wait()

	// wait until exit
	exitcode, err := shim.wait()
	if err != nil {
		logger().WithError(err).WithField("exec-id", execID).Error("failed waiting for process")
		return exitFailure
	} else if proxyExitCode {
		logger().WithField("exitcode", exitcode).Info("using shim to proxy exit code")
		if exitcode != 0 {
			return int(exitcode)
		}
	}

	return exitSuccess
}

func main() {
	defer handlePanic()
	exitCode := realMain()
	os.Exit(exitCode)
}
<|endoftext|>


<commit_before>// Version 1.82
// Supports Windows, Linux, Mac, and Raspberry Pi, Beagle Bone Black

package main

import (
	"errors"
	"flag"
	"fmt"
	"go/build"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	//"net/http/pprof"
	"github.com/kardianos/osext"
	//"github.com/sanbornm/go-selfupdate/selfupdate" #included in update.go to change heavily
	//"github.com/sanderhahn/gozip"
	"github.com/vharitonsky/iniflags"
	"runtime/debug"
	"text/template"
	"time"
)

var (
	version      = "1.83"
	versionFloat = float32(1.83)
	addr         = flag.String("addr", ":8989", "http service address")
	//assets       = flag.String("assets", defaultAssetPath(), "path to assets")
	verbose = flag.Bool("v", true, "show debug logging")
	//verbose = flag.Bool("v", false, "show debug logging")
	//homeTempl *template.Template
	isLaunchSelf = flag.Bool("ls", false, "launch self 5 seconds later")

	configIni = flag.String("configFile", "config.ini", "config file path")
	// regular expression to sort the serial port list
	// typically this wouldn't be provided, but if the user wants to clean
	// up their list with a regexp so it's cleaner inside their end-user interface
	// such as ChiliPeppr, this can make the massive list that Linux gives back
	// to you be a bit more manageable
	regExpFilter = flag.String("regex", "usb|acm|com", "Regular expression to filter serial port list")

	// allow garbageCollection()
	//isGC = flag.Bool("gc", false, "Is garbage collection on? Off by default.")
	//isGC = flag.Bool("gc", true, "Is garbage collection on? Off by default.")
	gcType = flag.String("gc", "std", "Type of garbage collection. std = Normal garbage collection allowing system to decide (this has been known to cause a stop the world in the middle of a CNC job which can cause lost responses from the CNC controller and thus stalled jobs. use max instead to solve.), off = let memory grow unbounded (you have to send in the gc command manually to garbage collect or you will run out of RAM eventually), max = Force garbage collection on each recv or send on a serial port (this minimizes stop the world events and thus lost serial responses, but increases CPU usage)")

	// whether to do buffer flow debugging
	bufFlowDebugType = flag.String("bufflowdebug", "off", "off = (default) We do not send back any debug JSON, on = We will send back a JSON response with debug info based on the configuration of the buffer flow that the user picked")

	// hostname. allow user to override, otherwise we look it up
	hostname = flag.String("hostname", "unknown-hostname", "Override the hostname we get from the OS")

	updateUrl = flag.String("updateUrl", "", "")
	appName   = flag.String("appName", "", "")
)

var globalConfigMap map[string]interface{}

type NullWriter int

func (NullWriter) Write([]byte) (int, error) { return 0, nil }

func defaultAssetPath() string {
	//p, err := build.Default.Import("gary.burd.info/go-websocket-chat", "", build.FindOnly)
	p, err := build.Default.Import("github.com/johnlauer/serial-port-json-server", "", build.FindOnly)
	if err != nil {
		return "."
	}
	return p.Dir
}

func homeHandler(c http.ResponseWriter, req *http.Request) {
	homeTemplate.Execute(c, req.Host)
}

func launchSelfLater() {
	log.Println("Going to launch myself 5 seconds later.")
	time.Sleep(2 * 1000 * time.Millisecond)
	log.Println("Done waiting 5 secs. Now launching...")
}

func main() {

	go func() {

		// autoextract self
		src, _ := osext.Executable()
		dest := filepath.Dir(src)

		// save the config.ini (if it exists)
		if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
			fmt.Println("First run, unzipping self")
			err := Unzip(src, dest)
			fmt.Println("Self extraction, err:", err)
		}

		if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
			flag.Parse()
			fmt.Println("No config.ini at", *configIni)
		} else {
			flag.Set("config", dest+"/"+*configIni)
			iniflags.Parse()
		}

		// setup logging
		log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

		// see if we are supposed to wait 5 seconds
		if *isLaunchSelf {
			launchSelfLater()
		}

		var updater = &Updater{
			CurrentVersion: version,
			ApiURL:         *updateUrl,
			BinURL:         *updateUrl,
			DiffURL:        "",
			Dir:            "update/",
			CmdName:        *appName,
		}

		if updater != nil {
			go updater.BackgroundRun()
		}

		// data, err := Asset("arduino.zip")
		// if err != nil {
		// 	log.Println("arduino tools not found")
		// }

		createGlobalConfigMap(&globalConfigMap)

		//getList()
		f := flag.Lookup("addr")
		log.Println("Version:" + version)

		// hostname
		hn, _ := os.Hostname()
		if *hostname == "unknown-hostname" {
			*hostname = hn
		}
		log.Println("Hostname:", *hostname)

		// turn off garbage collection
		// this is dangerous, as u could overflow memory
		//if *isGC {
		if *gcType == "std" {
			log.Println("Garbage collection is on using Standard mode, meaning we just let Golang determine when to garbage collect.")
		} else if *gcType == "max" {
			log.Println("Garbage collection is on for MAXIMUM real-time collecting on each send/recv from serial port. Higher CPU, but less stopping of the world to garbage collect since it is being done on a constant basis.")
		} else {
			log.Println("Garbage collection is off. Memory use will grow unbounded. You WILL RUN OUT OF RAM unless you send in the gc command to manually force garbage collection. Lower CPU, but progressive memory footprint.")
			debug.SetGCPercent(-1)
		}

		ip, err := externalIP()
		if err != nil {
			log.Println(err)
		}

		log.Print("Starting server and websocket on " + ip + "" + f.Value.String())
		//homeTempl = template.Must(template.ParseFiles(filepath.Join(*assets, "home.html")))

		log.Println("The Serial Port JSON Server is now running.")
		log.Println("If you are using ChiliPeppr, you may go back to it and connect to this server.")
	// launch the hub routine which is the singleton for the websocket server
	go h.run()
	// launch our serial port routine
	go sh.run()
	// launch our dummy data routine
	//go d.run()

	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/ws", wsHandler)
	if err := http.ListenAndServe(*addr, nil); err != nil {
		fmt.Printf("Error trying to bind to port: %v, so exiting...", err)
		log.Fatal("Error ListenAndServe:", err)
	}

		// see if they provided a regex filter
		if len(*regExpFilter) > 0 {
			log.Printf("You specified a serial port regular expression filter: %v\n", *regExpFilter)
		}

		// list serial ports
		portList, _ := GetList(false)
		/*if errSys != nil {
			log.Printf("Got system error trying to retrieve serial port list. Err:%v\n", errSys)
			log.Fatal("Exiting")
		}*/
		log.Println("Your serial ports:")
		if len(portList) == 0 {
			log.Println("\tThere are no serial ports to list.")
		}
		for _, element := range portList {
			log.Printf("\t%v\n", element)

		}

		if !*verbose {
			log.Println("You can enter verbose mode to see all logging by starting with the -v command line switch.")
			log.SetOutput(new(NullWriter)) //route all logging to nullwriter
		}

		// launch the hub routine which is the singleton for the websocket server
		go h.run()
		// launch our serial port routine
		go sh.run()
		// launch our dummy data routine
		//go d.run()

		go func() {
			http.HandleFunc("/", homeHandler)
			http.HandleFunc("/ws", wsHandler)
			http.HandleFunc("/upload", uploadHandler)
			if err := http.ListenAndServe(*addr, nil); err != nil {
				fmt.Printf("Error trying to bind to port: %v, so exiting...", err)
				log.Fatal("Error ListenAndServe:", err)
			}
		}()
	}()
	setupSysTray()
}

func externalIP() (string, error) {
	//log.Println("Getting external IP")
	ifaces, err := net.Interfaces()
	if err != nil {
		log.Println("Got err getting external IP addr")
		return "", err
	}
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			//log.Println("Iface down")
			continue // interface down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			//log.Println("Loopback")
			continue // loopback interface
		}
		addrs, err := iface.Addrs()
		if err != nil {
			log.Println("Got err on iface.Addrs()")
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				//log.Println("Ip was nil or loopback")
				continue
			}
			ip = ip.To4()
			if ip == nil {
				//log.Println("Was not ipv4 addr")
				continue // not an ipv4 address
			}
			//log.Println("IP is ", ip.String())
			return ip.String(), nil
		}
	}
	return "", errors.New("are you connected to the network?")
}

var homeTemplate = template.Must(template.New("home").Parse(homeTemplateHtml))

// If you navigate to this server's homepage, you'll get this HTML
// so you can directly interact with the serial port server
const homeTemplateHtml = `<!DOCTYPE html>

<title>Serial Port Example</title>

<form id="form">
    <input type="submit" value="Send" />
    <input type="text" id="msg" size="64"/>
</form>

`
<commit_msg>Fix indentation<commit_after>// Version 1.82
// Supports Windows, Linux, Mac, and Raspberry Pi, Beagle Bone Black

package main

import (
	"errors"
	"flag"
	"fmt"
	"go/build"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	//"net/http/pprof"
	"github.com/kardianos/osext"
	//"github.com/sanbornm/go-selfupdate/selfupdate" #included in update.go to change heavily
	//"github.com/sanderhahn/gozip"
	"github.com/vharitonsky/iniflags"
	"runtime/debug"
	"text/template"
	"time"
)

var (
	version      = "1.83"
	versionFloat = float32(1.83)
	addr         = flag.String("addr", ":8989", "http service address")
	//assets       = flag.String("assets", defaultAssetPath(), "path to assets")
	verbose = flag.Bool("v", true, "show debug logging")
	//verbose = flag.Bool("v", false, "show debug logging")
	//homeTempl *template.Template
	isLaunchSelf = flag.Bool("ls", false, "launch self 5 seconds later")

	configIni = flag.String("configFile", "config.ini", "config file path")
	// regular expression to sort the serial port list
	// typically this wouldn't be provided, but if the user wants to clean
	// up their list with a regexp so it's cleaner inside their end-user interface
	// such as ChiliPeppr, this can make the massive list that Linux gives back
	// to you be a bit more manageable
	regExpFilter = flag.String("regex", "usb|acm|com", "Regular expression to filter serial port list")

	// allow garbageCollection()
	//isGC = flag.Bool("gc", false, "Is garbage collection on? Off by default.")
	//isGC = flag.Bool("gc", true, "Is garbage collection on? Off by default.")
	gcType = flag.String("gc", "std", "Type of garbage collection. std = Normal garbage collection allowing system to decide (this has been known to cause a stop the world in the middle of a CNC job which can cause lost responses from the CNC controller and thus stalled jobs. use max instead to solve.), off = let memory grow unbounded (you have to send in the gc command manually to garbage collect or you will run out of RAM eventually), max = Force garbage collection on each recv or send on a serial port (this minimizes stop the world events and thus lost serial responses, but increases CPU usage)")

	// whether to do buffer flow debugging
	bufFlowDebugType = flag.String("bufflowdebug", "off", "off = (default) We do not send back any debug JSON, on = We will send back a JSON response with debug info based on the configuration of the buffer flow that the user picked")

	// hostname. allow user to override, otherwise we look it up
	hostname = flag.String("hostname", "unknown-hostname", "Override the hostname we get from the OS")

	updateUrl = flag.String("updateUrl", "", "")
	appName   = flag.String("appName", "", "")
)

var globalConfigMap map[string]interface{}

type NullWriter int

func (NullWriter) Write([]byte) (int, error) { return 0, nil }

func defaultAssetPath() string {
	//p, err := build.Default.Import("gary.burd.info/go-websocket-chat", "", build.FindOnly)
	p, err := build.Default.Import("github.com/johnlauer/serial-port-json-server", "", build.FindOnly)
	if err != nil {
		return "."
	}
	return p.Dir
}

func homeHandler(c http.ResponseWriter, req *http.Request) {
	homeTemplate.Execute(c, req.Host)
}

func launchSelfLater() {
	log.Println("Going to launch myself 5 seconds later.")
	time.Sleep(2 * 1000 * time.Millisecond)
	log.Println("Done waiting 5 secs. Now launching...")
}

func main() {

	go func() {

		// autoextract self
		src, _ := osext.Executable()
		dest := filepath.Dir(src)

		// save the config.ini (if it exists)
		if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
			fmt.Println("First run, unzipping self")
			err := Unzip(src, dest)
			fmt.Println("Self extraction, err:", err)
		}

		if _, err := os.Stat(dest + "/" + *configIni); os.IsNotExist(err) {
			flag.Parse()
			fmt.Println("No config.ini at", *configIni)
		} else {
			flag.Set("config", dest+"/"+*configIni)
			iniflags.Parse()
		}

		// setup logging
		log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

		// see if we are supposed to wait 5 seconds
		if *isLaunchSelf {
			launchSelfLater()
		}

		var updater = &Updater{
			CurrentVersion: version,
			ApiURL:         *updateUrl,
			BinURL:         *updateUrl,
			DiffURL:        "",
			Dir:            "update/",
			CmdName:        *appName,
		}

		if updater != nil {
			go updater.BackgroundRun()
		}

		// data, err := Asset("arduino.zip")
		// if err != nil {
		// 	log.Println("arduino tools not found")
		// }

		createGlobalConfigMap(&globalConfigMap)

		//getList()
		f := flag.Lookup("addr")
		log.Println("Version:" + version)

		// hostname
		hn, _ := os.Hostname()
		if *hostname == "unknown-hostname" {
			*hostname = hn
		}
		log.Println("Hostname:", *hostname)

		// turn off garbage collection
		// this is dangerous, as u could overflow memory
		//if *isGC {
		if *gcType == "std" {
			log.Println("Garbage collection is on using Standard mode, meaning we just let Golang determine when to garbage collect.")
		} else if *gcType == "max" {
			log.Println("Garbage collection is on for MAXIMUM real-time collecting on each send/recv from serial port. Higher CPU, but less stopping of the world to garbage collect since it is being done on a constant basis.")
		} else {
			log.Println("Garbage collection is off. Memory use will grow unbounded. You WILL RUN OUT OF RAM unless you send in the gc command to manually force garbage collection. Lower CPU, but progressive memory footprint.")
			debug.SetGCPercent(-1)
		}

		ip, err := externalIP()
		if err != nil {
			log.Println(err)
		}

		log.Print("Starting server and websocket on " + ip + "" + f.Value.String())
		//homeTempl = template.Must(template.ParseFiles(filepath.Join(*assets, "home.html")))

		log.Println("The Serial Port JSON Server is now running.")
		log.Println("If you are using ChiliPeppr, you may go back to it and connect to this server.")
		// launch the hub routine which is the singleton for the websocket server
		go h.run()
		// launch our serial port routine
		go sh.run()
		// launch our dummy data routine
		//go d.run()

		http.HandleFunc("/", homeHandler)
		http.HandleFunc("/ws", wsHandler)
		if err := http.ListenAndServe(*addr, nil); err != nil {
			fmt.Printf("Error trying to bind to port: %v, so exiting...", err)
			log.Fatal("Error ListenAndServe:", err)
		}

		// see if they provided a regex filter
		if len(*regExpFilter) > 0 {
			log.Printf("You specified a serial port regular expression filter: %v\n", *regExpFilter)
		}

		// list serial ports
		portList, _ := GetList(false)
		/*if errSys != nil {
			log.Printf("Got system error trying to retrieve serial port list. Err:%v\n", errSys)
			log.Fatal("Exiting")
		}*/
		log.Println("Your serial ports:")
		if len(portList) == 0 {
			log.Println("\tThere are no serial ports to list.")
		}
		for _, element := range portList {
			log.Printf("\t%v\n", element)

		}

		if !*verbose {
			log.Println("You can enter verbose mode to see all logging by starting with the -v command line switch.")
			log.SetOutput(new(NullWriter)) //route all logging to nullwriter
		}

		// launch the hub routine which is the singleton for the websocket server
		go h.run()
		// launch our serial port routine
		go sh.run()
		// launch our dummy data routine
		//go d.run()

		go func() {
			http.HandleFunc("/", homeHandler)
			http.HandleFunc("/ws", wsHandler)
			http.HandleFunc("/upload", uploadHandler)
			if err := http.ListenAndServe(*addr, nil); err != nil {
				fmt.Printf("Error trying to bind to port: %v, so exiting...", err)
				log.Fatal("Error ListenAndServe:", err)
			}
		}()
	}()
	setupSysTray()
}

func externalIP() (string, error) {
	//log.Println("Getting external IP")
	ifaces, err := net.Interfaces()
	if err != nil {
		log.Println("Got err getting external IP addr")
		return "", err
	}
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			//log.Println("Iface down")
			continue // interface down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			//log.Println("Loopback")
			continue // loopback interface
		}
		addrs, err := iface.Addrs()
		if err != nil {
			log.Println("Got err on iface.Addrs()")
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				//log.Println("Ip was nil or loopback")
				continue
			}
			ip = ip.To4()
			if ip == nil {
				//log.Println("Was not ipv4 addr")
				continue // not an ipv4 address
			}
			//log.Println("IP is ", ip.String())
			return ip.String(), nil
		}
	}
	return "", errors.New("are you connected to the network?")
}

var homeTemplate = template.Must(template.New("home").Parse(homeTemplateHtml))

// If you navigate to this server's homepage, you'll get this HTML
// so you can directly interact with the serial port server
const homeTemplateHtml = `<!DOCTYPE html>

<title>Serial Port Example</title>

<form id="form">
    <input type="submit" value="Send" />
    <input type="text" id="msg" size="64"/>
</form>

`
<|endoftext|>


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

package main

import (
	"os"

	"errors"
	"github.com/minio-io/cli"
	"github.com/minio-io/iodine"
	"github.com/minio-io/minio/pkg/server"
	"github.com/minio-io/minio/pkg/utils/log"
	"time"
)

// commitID is automatically set by git. Settings are controlled
// through .gitattributes
const commitID = "$Id$"

var flags = []cli.Flag{
	cli.StringFlag{
		Name:  "domain,d",
		Value: "",
		Usage: "domain used for routing incoming API requests",
	},
	cli.StringFlag{
		Name:  "api-address,a",
		Value: ":9000",
		Usage: "address for incoming API requests",
	},
	cli.StringFlag{
		Name:  "web-address,w",
		Value: ":9001",
		Usage: "address for incoming Management UI requests",
	},
	cli.StringFlag{
		Name:  "cert,c",
		Hide:  true,
		Value: "",
		Usage: "cert.pem",
	},
	cli.StringFlag{
		Name:  "key,k",
		Hide:  true,
		Value: "",
		Usage: "key.pem",
	},
	cli.StringFlag{
		Name:  "driver-type,t",
		Value: "donut",
		Usage: "valid entries: file,inmemory,donut",
	},
}

func getDriverType(input string) server.DriverType {
	switch {
	case input == "file":
		return server.File
	case input == "memory":
		return server.Memory
	case input == "donut":
		return server.Donut
	default:
		{
			log.Println("Unknown driver type:", input)
			log.Println("Choosing default driver type as 'file'..")
			return server.File
		}
	}
}

func runCmd(c *cli.Context) {
	driverTypeStr := c.String("driver-type")
	domain := c.String("domain")
	apiaddress := c.String("api-address")
	webaddress := c.String("web-address")
	certFile := c.String("cert")
	keyFile := c.String("key")
	if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
		log.Fatal("Both certificate and key must be provided to enable https")
	}
	tls := (certFile != "" && keyFile != "")
	driverType := getDriverType(driverTypeStr)
	var serverConfigs []server.Config
	apiServerConfig := server.Config{
		Domain:   domain,
		Address:  apiaddress,
		TLS:      tls,
		CertFile: certFile,
		KeyFile:  keyFile,
		APIType: server.MinioAPI{
			DriverType: driverType,
		},
	}
	webUIServerConfig := server.Config{
		Domain:   domain,
		Address:  webaddress,
		TLS:      false,
		CertFile: "",
		KeyFile:  "",
		APIType: server.Web{
			Websocket: false,
		},
	}
	serverConfigs = append(serverConfigs, apiServerConfig)
	serverConfigs = append(serverConfigs, webUIServerConfig)
	server.Start(serverConfigs)
}

func main() {
	// set up iodine
	iodine.SetGlobalState("minio.git", commitID)
	iodine.SetGlobalState("minio.starttime", time.Now().Format(time.RFC3339))

	// set up app
	app := cli.NewApp()
	app.Name = "minio"
	app.Version = commitID
	app.Author = "Minio.io"
	app.Usage = "Minimalist Object Storage"
	app.EnableBashCompletion = true
	app.Flags = flags
	app.Action = runCmd
	app.Run(os.Args)
}
<commit_msg>Build fix, remove 'errors' not used<commit_after>/*
 * Minimalist Object Storage, (C) 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"os"
	"time"

	"github.com/minio-io/cli"
	"github.com/minio-io/iodine"
	"github.com/minio-io/minio/pkg/server"
	"github.com/minio-io/minio/pkg/utils/log"
)

// commitID is automatically set by git. Settings are controlled
// through .gitattributes
const commitID = "$Id$"

var flags = []cli.Flag{
	cli.StringFlag{
		Name:  "domain,d",
		Value: "",
		Usage: "domain used for routing incoming API requests",
	},
	cli.StringFlag{
		Name:  "api-address,a",
		Value: ":9000",
		Usage: "address for incoming API requests",
	},
	cli.StringFlag{
		Name:  "web-address,w",
		Value: ":9001",
		Usage: "address for incoming Management UI requests",
	},
	cli.StringFlag{
		Name:  "cert,c",
		Hide:  true,
		Value: "",
		Usage: "cert.pem",
	},
	cli.StringFlag{
		Name:  "key,k",
		Hide:  true,
		Value: "",
		Usage: "key.pem",
	},
	cli.StringFlag{
		Name:  "driver-type,t",
		Value: "donut",
		Usage: "valid entries: file,inmemory,donut",
	},
}

func getDriverType(input string) server.DriverType {
	switch {
	case input == "file":
		return server.File
	case input == "memory":
		return server.Memory
	case input == "donut":
		return server.Donut
	default:
		{
			log.Println("Unknown driver type:", input)
			log.Println("Choosing default driver type as 'file'..")
			return server.File
		}
	}
}

func runCmd(c *cli.Context) {
	driverTypeStr := c.String("driver-type")
	domain := c.String("domain")
	apiaddress := c.String("api-address")
	webaddress := c.String("web-address")
	certFile := c.String("cert")
	keyFile := c.String("key")
	if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
		log.Fatal("Both certificate and key must be provided to enable https")
	}
	tls := (certFile != "" && keyFile != "")
	driverType := getDriverType(driverTypeStr)
	var serverConfigs []server.Config
	apiServerConfig := server.Config{
		Domain:   domain,
		Address:  apiaddress,
		TLS:      tls,
		CertFile: certFile,
		KeyFile:  keyFile,
		APIType: server.MinioAPI{
			DriverType: driverType,
		},
	}
	webUIServerConfig := server.Config{
		Domain:   domain,
		Address:  webaddress,
		TLS:      false,
		CertFile: "",
		KeyFile:  "",
		APIType: server.Web{
			Websocket: false,
		},
	}
	serverConfigs = append(serverConfigs, apiServerConfig)
	serverConfigs = append(serverConfigs, webUIServerConfig)
	server.Start(serverConfigs)
}

func main() {
	// set up iodine
	iodine.SetGlobalState("minio.git", commitID)
	iodine.SetGlobalState("minio.starttime", time.Now().Format(time.RFC3339))

	// set up app
	app := cli.NewApp()
	app.Name = "minio"
	app.Version = commitID
	app.Author = "Minio.io"
	app.Usage = "Minimalist Object Storage"
	app.EnableBashCompletion = true
	app.Flags = flags
	app.Action = runCmd
	app.Run(os.Args)
}
<|endoftext|>