<commit_before>package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"github.com/minotar/minecraft"
	"image"
	"log"
	"net/http"
	"strconv"
	"time"
)

const (
	DefaultSize = uint(180)
	MaxSize     = uint(300)
	MinSize     = uint(8)

	SkinCache

	ListenOn = ":9999"

	Minutes            uint = 60
	Hours                   = 60 * Minutes
	Days                    = 24 * Hours
	TimeoutActualSkin       = 2 * Days
	TimeoutFailedFetch      = 15 * Minutes

	MinotarVersion = "2.0"
)

type NotFoundHandler struct{}

func (h NotFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(404)
	fmt.Fprintf(w, "404 not found")
}

func notFoundPage(w http.ResponseWriter, r *http.Request) {
	nfh := NotFoundHandler{}
	nfh.ServeHTTP(w, r)
}
func serverErrorPage(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(500)
	fmt.Fprintf(w, "500 internal server error")
}

func rationalizeSize(inp string) uint {
	out64, err := strconv.ParseUint(inp, 10, 0)
	out := uint(out64)
	if err != nil {
		return DefaultSize
	} else if out > MaxSize {
		return MaxSize
	} else if out < MinSize {
		return MinSize
	}
	return out
}

func addCacheTimeoutHeader(w http.ResponseWriter, timeout uint) {
	w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d", timeout))
}

func timeBetween(timeA time.Time, timeB time.Time) int64 {
	// millis between two timestamps

	if timeB.Before(timeA) {
		timeA, timeB = timeB, timeA
	}
	return timeB.Sub(timeA).Nanoseconds() / 1000000
}

func fetchImageProcessThen(callback func(minecraft.Skin) (image.Image, error)) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		timeReqStart := time.Now()

		vars := mux.Vars(r)

		username := vars["username"]
		size := rationalizeSize(vars["size"])
		ok := true

		var skin minecraft.Skin
		var err error

		skin = fetchSkin(username)

		timeFetch := time.Now()

		img, err := callback(skin)
		if err != nil {
			serverErrorPage(w, r)
			return
		}
		timeProcess := time.Now()

		imgResized := Resize(size, size, img)
		timeResize := time.Now()

		w.Header().Add("Content-Type", "image/png")
		w.Header().Add("X-Requested", "processed")
		var timeout uint
		if ok {
			w.Header().Add("X-Result", "ok")
			timeout = TimeoutActualSkin
		} else {
			w.Header().Add("X-Result", "failed")
			timeout = TimeoutFailedFetch
		}
		w.Header().Add("X-Timing", fmt.Sprintf("%d+%d+%d=%dms", timeBetween(timeReqStart, timeFetch), timeBetween(timeFetch, timeProcess), timeBetween(timeProcess, timeResize), timeBetween(timeReqStart, timeResize)))
		addCacheTimeoutHeader(w, timeout)
		WritePNG(w, imgResized)
	}
}
func skinPage(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	username := vars["username"]

	skin := fetchSkin(username)

	w.Header().Add("Content-Type", "image/png")
	w.Header().Add("X-Requested", "skin")
	w.Header().Add("X-Result", "ok")

	WritePNG(w, skin.Image)
}
func downloadPage(w http.ResponseWriter, r *http.Request) {
	headers := w.Header()
	headers.Add("Content-Disposition", "attachment; filename=\"skin.png\"")
	skinPage(w, r)
}

func fetchSkin(username string) minecraft.Skin {
	skin, err := minecraft.GetSkin(minecraft.User{Name: username})
	if err != nil {
		// Problem with the returned image, probably means we have an incorrect username
		// Hit the accounts api
		user, err := minecraft.GetUser(username)

		if err != nil {
			// There's no account for this person, serve char
			skin, _ = minecraft.FetchSkinForChar()
		} else {
			// Get valid skin
			skin, err = minecraft.GetSkin(user)
			if err != nil {
				// Their skin somehow errored, fallback
				skin, _ = minecraft.FetchSkinForChar()
			}
		}
	}

	return skin
}

func main() {
	avatarPage := fetchImageProcessThen(func(skin minecraft.Skin) (image.Image, error) {
		return GetHead(skin)
	})
	helmPage := fetchImageProcessThen(func(skin minecraft.Skin) (image.Image, error) {
		return GetHelm(skin)
	})

	r := mux.NewRouter()
	r.NotFoundHandler = NotFoundHandler{}

	r.HandleFunc("/avatar/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", avatarPage)
	r.HandleFunc("/avatar/{username:"+minecraft.ValidUsernameRegex+"}/{size:[0-9]+}{extension:(.png)?}", avatarPage)

	r.HandleFunc("/helm/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", helmPage)
	r.HandleFunc("/helm/{username:"+minecraft.ValidUsernameRegex+"}/{size:[0-9]+}{extension:(.png)?}", helmPage)

	r.HandleFunc("/download/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", downloadPage)

	r.HandleFunc("/skin/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", skinPage)

	r.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "%s", MinotarVersion)
	})

	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Location", "https://minotar.net/")
	})

	http.Handle("/", r)
	log.Fatalln(http.ListenAndServe(ListenOn, nil))
}
<commit_msg>Fixing rate limiting for now<commit_after>package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"github.com/minotar/minecraft"
	"image"
	"log"
	"net/http"
	"strconv"
	"time"
)

const (
	DefaultSize = uint(180)
	MaxSize     = uint(300)
	MinSize     = uint(8)

	SkinCache

	ListenOn = ":9999"

	Minutes            uint = 60
	Hours                   = 60 * Minutes
	Days                    = 24 * Hours
	TimeoutActualSkin       = 2 * Days
	TimeoutFailedFetch      = 15 * Minutes

	MinotarVersion = "2.0"
)

type NotFoundHandler struct{}

func (h NotFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(404)
	fmt.Fprintf(w, "404 not found")
}

func notFoundPage(w http.ResponseWriter, r *http.Request) {
	nfh := NotFoundHandler{}
	nfh.ServeHTTP(w, r)
}
func serverErrorPage(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(500)
	fmt.Fprintf(w, "500 internal server error")
}

func rationalizeSize(inp string) uint {
	out64, err := strconv.ParseUint(inp, 10, 0)
	out := uint(out64)
	if err != nil {
		return DefaultSize
	} else if out > MaxSize {
		return MaxSize
	} else if out < MinSize {
		return MinSize
	}
	return out
}

func addCacheTimeoutHeader(w http.ResponseWriter, timeout uint) {
	w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d", timeout))
}

func timeBetween(timeA time.Time, timeB time.Time) int64 {
	// millis between two timestamps

	if timeB.Before(timeA) {
		timeA, timeB = timeB, timeA
	}
	return timeB.Sub(timeA).Nanoseconds() / 1000000
}

func fetchImageProcessThen(callback func(minecraft.Skin) (image.Image, error)) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		timeReqStart := time.Now()

		vars := mux.Vars(r)

		username := vars["username"]
		size := rationalizeSize(vars["size"])
		ok := true

		var skin minecraft.Skin
		var err error

		skin = fetchSkin(username)

		timeFetch := time.Now()

		img, err := callback(skin)
		if err != nil {
			serverErrorPage(w, r)
			return
		}
		timeProcess := time.Now()

		imgResized := Resize(size, size, img)
		timeResize := time.Now()

		w.Header().Add("Content-Type", "image/png")
		w.Header().Add("X-Requested", "processed")
		var timeout uint
		if ok {
			w.Header().Add("X-Result", "ok")
			timeout = TimeoutActualSkin
		} else {
			w.Header().Add("X-Result", "failed")
			timeout = TimeoutFailedFetch
		}
		w.Header().Add("X-Timing", fmt.Sprintf("%d+%d+%d=%dms", timeBetween(timeReqStart, timeFetch), timeBetween(timeFetch, timeProcess), timeBetween(timeProcess, timeResize), timeBetween(timeReqStart, timeResize)))
		addCacheTimeoutHeader(w, timeout)
		WritePNG(w, imgResized)
	}
}
func skinPage(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	username := vars["username"]

	skin := fetchSkin(username)

	w.Header().Add("Content-Type", "image/png")
	w.Header().Add("X-Requested", "skin")
	w.Header().Add("X-Result", "ok")

	WritePNG(w, skin.Image)
}
func downloadPage(w http.ResponseWriter, r *http.Request) {
	headers := w.Header()
	headers.Add("Content-Disposition", "attachment; filename=\"skin.png\"")
	skinPage(w, r)
}

func fetchSkin(username string) minecraft.Skin {
	skin, err := minecraft.FetchSkinFromUrl(username)
	if err != nil {
		skin, _ = minecraft.FetchSkinForChar()
	}

	return skin

	/* We're not using this for now due to rate limiting restrictions
	skin, err := minecraft.GetSkin(minecraft.User{Name: username})
	if err != nil {
		// Problem with the returned image, probably means we have an incorrect username
		// Hit the accounts api
		user, err := minecraft.GetUser(username)

		if err != nil {
			// There's no account for this person, serve char
			skin, _ = minecraft.FetchSkinForChar()
		} else {
			// Get valid skin
			skin, err = minecraft.GetSkin(user)
			if err != nil {
				// Their skin somehow errored, fallback
				skin, _ = minecraft.FetchSkinForChar()
			}
		}
	}

	return skin
	*/
}

func main() {
	avatarPage := fetchImageProcessThen(func(skin minecraft.Skin) (image.Image, error) {
		return GetHead(skin)
	})
	helmPage := fetchImageProcessThen(func(skin minecraft.Skin) (image.Image, error) {
		return GetHelm(skin)
	})

	r := mux.NewRouter()
	r.NotFoundHandler = NotFoundHandler{}

	r.HandleFunc("/avatar/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", avatarPage)
	r.HandleFunc("/avatar/{username:"+minecraft.ValidUsernameRegex+"}/{size:[0-9]+}{extension:(.png)?}", avatarPage)

	r.HandleFunc("/helm/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", helmPage)
	r.HandleFunc("/helm/{username:"+minecraft.ValidUsernameRegex+"}/{size:[0-9]+}{extension:(.png)?}", helmPage)

	r.HandleFunc("/download/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", downloadPage)

	r.HandleFunc("/skin/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", skinPage)

	r.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "%s", MinotarVersion)
	})

	r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Location", "https://minotar.net/")
	})

	http.Handle("/", r)
	log.Fatalln(http.ListenAndServe(ListenOn, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"strings"
	"time"

	"gopkg.in/mgo.v2/bson"

	log "github.com/Sirupsen/logrus"
	"github.com/bmuller/arrow/lib"
	"github.com/brandfolder/gin-gorelic"
	"github.com/carlescere/scheduler"
	"github.com/dwarvesf/working-on/db"
	"github.com/gin-gonic/contrib/ginrus"
	"github.com/gin-gonic/gin"
	"github.com/nlopes/slack"
)

type Item struct {
	ID        bson.ObjectId `json:"id" bson:"_id"`
	UserID    string        `json:"user_id" bson:"user_id"`
	Name      string        `json:"user_name" bson:"user_name"`
	Text      string        `json:"text" bson:"text"`
	CreatedAt time.Time     `json:"created_at" bson:"created_at"`
}

func main() {

	// Read configuration from file and env
	port := os.Getenv("PORT")
	digestTime := os.Getenv("DIGEST_TIME")
	gorelic.InitNewrelicAgent(os.Getenv("NEW_RELIC_LICENSE_KEY"), "working", false)

	// Setup schedule jobs
	digestJob := postDigest
	scheduler.Every().Day().At(digestTime).Run(digestJob)

	// Prepare router
	router := gin.New()
	router.Use(gorelic.Handler)
	router.Use(ginrus.Ginrus(log.StandardLogger(), time.RFC3339, true))

	// router.LoadHTMLGlob("templates/*.tmpl.html")
	router.Static("/static", "static")
	router.POST("/on", addItem)

	// Start server
	router.Run(":" + port)
}

// Message will be passed to server with '-' prefix via various way
//	+ Direct message with the bots
//	+ Use slash command `/working <message>`
//	+ Use cli `working-on <message>`
//	+ Might use Chrome plugin
//	+ ...
// Token is secondary param to indicate the user
func addItem(c *gin.Context) {

	// Parse token and message
	var item Item

	text := c.PostForm("text")
	text = strings.TrimSpace(text)

	if text == "" {
		log.Fatalln("Message is nil")
		return
	}

	userID := c.PostForm("user_id")
	userName := c.PostForm("user_name")

	item.ID = bson.NewObjectId()
	item.CreatedAt = time.Now()
	item.Name = userName
	item.UserID = userID
	item.Text = text

	ctx, err := db.NewContext()
	if err != nil {
		panic(err)
	}

	defer ctx.Close()

	// Add Item to database
	err = ctx.C("items").Insert(item)
	if err != nil {
		log.Fatalln(err)
	}

	// Repost to the target channel
	channel := os.Getenv("WORKING_CHANNEL")
	botToken := os.Getenv("BOT_TOKEN")

	if botToken == "" {
		log.Fatal("No token provided")
	}

	// <@U024BE7LH|bob>
	title := "*" + userName + "* is working on: " + text

	postWorkingItem(botToken, channel, title)

	// Parse configuration
	var configuration Configuration
	bytes, err := ioutil.ReadFile("setting.json")
	if err != nil {
		log.Fatalln("Cannot read setting file")
	}

	err = json.Unmarshal(bytes, configuration)
	if err != nil {
		log.Fatalln("Cannot parse setting")
	}

	// Post item to project group
	for _, config := range configuration.Items {
		for _, tag := range config.Tags {
			if strings.Contains(text, tag) {

				log.Info("Hit" + tag)

				// Post to target channel
				postWorkingItem(config.Token, config.Channel, title)
			}
		}
	}
}

func postWorkingItem(token string, channel string, text string) {
	s := slack.New(token)
	params := slack.PostMessageParameters{}
	params.IconURL = "http://i.imgur.com/fLcxkel.png"
	params.Username = "oshin"
	s.PostMessage(channel, text, params)
}

type Configuration struct {
	Items []ConfigurationItem `json:"items"`
}

type ConfigurationItem struct {
	Channel string   `json:"channel"`
	Tags    []string `json:"tags"`
	Token   string   `json:"token"`
}

// Post summary to Slack channel
func postDigest() {

	channel := os.Getenv("DIGEST_CHANNEL")
	botToken := os.Getenv("BOT_TOKEN")

	if botToken == "" {
		log.Fatal("No token provided")
		os.Exit(1)
	}

	s := slack.New(botToken)
	users, err := s.GetUsers()

	if err != nil {
		log.Fatal("Cannot get users")
		os.Exit(1)
	}

	ctx, err := db.NewContext()
	if err != nil {
		panic(err)
	}

	defer ctx.Close()

	log.Info("Preparing data")
	// If count > 0, it means there is data to show
	count := 0
	title := " >> Yesterday I did: "
	params := slack.PostMessageParameters{}
	fields := []slack.AttachmentField{}

	yesterday := arrow.Yesterday().UTC()
	toDate := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, time.UTC)

	// Prepare attachment of done items
	for _, user := range users {

		if user.IsBot || user.Deleted {
			continue
		}

		// log.Info("Process user: " + user.Name + " - " + user.Id)

		// Query done items from Database
		var values string
		var items []Item

		err = ctx.C("items").Find(bson.M{"$and": []bson.M{
			bson.M{"user_id": user.Id},
			bson.M{"created_at": bson.M{"$gt": toDate}},
		},
		}).All(&items)

		if err != nil {
			log.Fatal("Cannot query done items.")
			os.Exit(1)
		}

		for _, item := range items {
			values = "\t" + values + " + " + item.Text + "\n"
		}

		// <@U024BE7LH|bob>
		if len(items) > 0 {

			count = count + 1
			field := slack.AttachmentField{
				Title: "@" + user.Name,
				Value: values,
			}

			fields = append(fields, field)
		}
	}

	params.Attachments = []slack.Attachment{
		slack.Attachment{
			Color:  "#7CD197",
			Fields: fields,
		},
	}

	params.IconURL = "http://i.imgur.com/fLcxkel.png"
	params.Username = "oshin"

	if count > 0 {
		s.PostMessage(channel, title, params)
	}
}
<commit_msg>new way to parse config<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"strings"
	"time"

	"gopkg.in/mgo.v2/bson"

	log "github.com/Sirupsen/logrus"
	"github.com/bmuller/arrow/lib"
	"github.com/brandfolder/gin-gorelic"
	"github.com/carlescere/scheduler"
	"github.com/dwarvesf/working-on/db"
	"github.com/gin-gonic/contrib/ginrus"
	"github.com/gin-gonic/gin"
	"github.com/nlopes/slack"
)

type Item struct {
	ID        bson.ObjectId `json:"id" bson:"_id"`
	UserID    string        `json:"user_id" bson:"user_id"`
	Name      string        `json:"user_name" bson:"user_name"`
	Text      string        `json:"text" bson:"text"`
	CreatedAt time.Time     `json:"created_at" bson:"created_at"`
}

func main() {

	// Read configuration from file and env
	port := os.Getenv("PORT")
	digestTime := os.Getenv("DIGEST_TIME")
	gorelic.InitNewrelicAgent(os.Getenv("NEW_RELIC_LICENSE_KEY"), "working", false)

	// Setup schedule jobs
	digestJob := postDigest
	scheduler.Every().Day().At(digestTime).Run(digestJob)

	// Prepare router
	router := gin.New()
	router.Use(gorelic.Handler)
	router.Use(ginrus.Ginrus(log.StandardLogger(), time.RFC3339, true))

	// router.LoadHTMLGlob("templates/*.tmpl.html")
	router.Static("/static", "static")
	router.POST("/on", addItem)

	// Start server
	router.Run(":" + port)
}

// Message will be passed to server with '-' prefix via various way
//	+ Direct message with the bots
//	+ Use slash command `/working <message>`
//	+ Use cli `working-on <message>`
//	+ Might use Chrome plugin
//	+ ...
// Token is secondary param to indicate the user
func addItem(c *gin.Context) {

	// Parse token and message
	var item Item

	text := c.PostForm("text")
	text = strings.TrimSpace(text)

	if text == "" {
		log.Fatalln("Message is nil")
		return
	}

	userID := c.PostForm("user_id")
	userName := c.PostForm("user_name")

	item.ID = bson.NewObjectId()
	item.CreatedAt = time.Now()
	item.Name = userName
	item.UserID = userID
	item.Text = text

	ctx, err := db.NewContext()
	if err != nil {
		panic(err)
	}

	defer ctx.Close()

	// Add Item to database
	err = ctx.C("items").Insert(item)
	if err != nil {
		log.Fatalln(err)
	}

	// Repost to the target channel
	channel := os.Getenv("WORKING_CHANNEL")
	botToken := os.Getenv("BOT_TOKEN")

	if botToken == "" {
		log.Fatal("No token provided")
	}

	// <@U024BE7LH|bob>
	title := "*" + userName + "* is working on: " + text

	postWorkingItem(botToken, channel, title)

	// Parse configuration
	var configuration Configuration
	bytes, err := ioutil.ReadFile("setting.json")
	if err != nil {
		log.Fatalln("Cannot read setting file")
	}

	err = json.Unmarshal(bytes, &configuration)
	if err != nil {
		log.Fatalln("Cannot parse setting")
	}

	// Post item to project group
	for _, config := range configuration.Items {
		for _, tag := range config.Tags {
			if strings.Contains(text, tag) {

				log.Info("Hit" + tag)

				// Post to target channel
				postWorkingItem(config.Token, config.Channel, title)
			}
		}
	}
}

func postWorkingItem(token string, channel string, text string) {
	s := slack.New(token)
	params := slack.PostMessageParameters{}
	params.IconURL = "http://i.imgur.com/fLcxkel.png"
	params.Username = "oshin"
	s.PostMessage(channel, text, params)
}

type Configuration struct {
	Items []ConfigurationItem `json:"items"`
}

type ConfigurationItem struct {
	Channel string   `json:"channel"`
	Tags    []string `json:"tags"`
	Token   string   `json:"token"`
}

// Post summary to Slack channel
func postDigest() {

	channel := os.Getenv("DIGEST_CHANNEL")
	botToken := os.Getenv("BOT_TOKEN")

	if botToken == "" {
		log.Fatal("No token provided")
		os.Exit(1)
	}

	s := slack.New(botToken)
	users, err := s.GetUsers()

	if err != nil {
		log.Fatal("Cannot get users")
		os.Exit(1)
	}

	ctx, err := db.NewContext()
	if err != nil {
		panic(err)
	}

	defer ctx.Close()

	log.Info("Preparing data")
	// If count > 0, it means there is data to show
	count := 0
	title := " >> Yesterday I did: "
	params := slack.PostMessageParameters{}
	fields := []slack.AttachmentField{}

	yesterday := arrow.Yesterday().UTC()
	toDate := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, time.UTC)

	// Prepare attachment of done items
	for _, user := range users {

		if user.IsBot || user.Deleted {
			continue
		}

		// log.Info("Process user: " + user.Name + " - " + user.Id)

		// Query done items from Database
		var values string
		var items []Item

		err = ctx.C("items").Find(bson.M{"$and": []bson.M{
			bson.M{"user_id": user.Id},
			bson.M{"created_at": bson.M{"$gt": toDate}},
		},
		}).All(&items)

		if err != nil {
			log.Fatal("Cannot query done items.")
			os.Exit(1)
		}

		for _, item := range items {
			values = "\t" + values + " + " + item.Text + "\n"
		}

		// <@U024BE7LH|bob>
		if len(items) > 0 {

			count = count + 1
			field := slack.AttachmentField{
				Title: "@" + user.Name,
				Value: values,
			}

			fields = append(fields, field)
		}
	}

	params.Attachments = []slack.Attachment{
		slack.Attachment{
			Color:  "#7CD197",
			Fields: fields,
		},
	}

	params.IconURL = "http://i.imgur.com/fLcxkel.png"
	params.Username = "oshin"

	if count > 0 {
		s.PostMessage(channel, title, params)
	}
}
<|endoftext|>


<commit_before>package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/TrilliumIT/docker-drouter/drouter"
	"github.com/codegangsta/cli"
	"os"
	"os/signal"
	"syscall"
)

const (
	version = "0.2"
)

func main() {

	var flagDebug = cli.BoolFlag{
		Name:  "debug, d",
		Usage: "Enable debugging.",
	}
	var flagIPOffset = cli.IntFlag{
		Name:  "ip-offset",
		Value: 0,
		Usage: "",
	}
	var flagNoAggressive = cli.BoolFlag{
		Name:  "no-aggressive",
		Usage: "Set false to make drouter only connect to docker networks with local containers.",
	}
	var flagLocalShortcut = cli.BoolFlag{
		Name:  "local-shortcut",
		Usage: "Set true to insert routes in the host destined for docker networks pointing to drouter over a host<->drouter p2p link.",
	}
	var flagLocalGateway = cli.BoolFlag{
		Name:  "local-gateway",
		Usage: "Set true to insert a default route on drouter pointing to the host over the host<->drouter p2p link. (implies --local-shortcut)",
	}
	var flagMasquerade = cli.BoolFlag{
		Name:  "masquerade",
		Usage: "Set true to masquerade container traffic to it's host's interface IP address.",
	}
	var flagP2PNet = cli.StringFlag{
		Name:  "p2p-net",
		Value: "172.29.255.252/30",
		Usage: "Use this option to customize the network used for the host<->drouter p2p link.",
	}
	var flagStaticRoutes = cli.StringSliceFlag{
		Name:  "static-route",
		Usage: "Specify one or many CIDR addresses that will be installed as routes via drouter to all containers.",
	}
	var flagTransitNet = cli.StringFlag{
		Name:  "transit-net",
		Usage: "Set a transit network for drouter to always connect to. Network must have 'drouter' option set. If network has a gateway, and --local-gateway=false, drouter's default gateway will be through this network's gateway. (this option is required with --no-aggressive)",
	}
	app := cli.NewApp()
	app.Name = "docker-drouter"
	app.Usage = "Docker Distributed Router"
	app.Version = version
	app.Flags = []cli.Flag{
		flagDebug,
		flagIPOffset,
		flagNoAggressive,
		flagLocalShortcut,
		flagLocalGateway,
		flagMasquerade,
		flagP2PNet,
		flagStaticRoutes,
		flagTransitNet,
	}
	app.Action = Run
	app.Run(os.Args)
}

// Run initializes the driver
func Run(ctx *cli.Context) error {
	log.SetFormatter(&log.TextFormatter{
		//ForceColors: false,
		//DisableColors: true,
		DisableTimestamp: false,
		FullTimestamp:    true,
	})

	if ctx.Bool("debug") {
		log.SetLevel(log.DebugLevel)
		log.Info("Debug logging enabled")
	}

	opts := &drouter.DistributedRouterOptions{
		IPOffset:      ctx.Int("ip-offset"),
		Aggressive:    !ctx.Bool("no-aggressive"),
		LocalShortcut: ctx.Bool("local-shortcut"),
		LocalGateway:  ctx.Bool("local-gateway"),
		Masquerade:    ctx.Bool("masquerade"),
		P2PAddr:       ctx.String("p2p-net"),
		StaticRoutes:  ctx.StringSlice("static-route"),
		TransitNet:    ctx.String("transit-net"),
	}

	quit := make(chan struct{})

	c := make(chan os.Signal)
	defer close(c)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)
	go func() {
		<-c
		close(quit)
	}()

	err := drouter.Run(opts, quit)
	if err != nil {
		log.Error(err)
		return err
	}

	return nil
}
<commit_msg>WIP: adding some options to main<commit_after>package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/TrilliumIT/docker-drouter/drouter"
	"github.com/codegangsta/cli"
	"os"
	"os/signal"
	"syscall"
)

const (
	version = "0.2"
)

func main() {

	var flagDebug = cli.BoolFlag{
		Name:  "debug, d",
		Usage: "Enable debugging.",
	}
	var flagIPOffset = cli.IntFlag{
		Name:  "ip-offset",
		Value: 0,
		Usage: "",
	}
	var flagDRouterInstance = cli.StringFlag{
		Name:  "drouter-instance",
		Value: "drouter",
		Usage: "String to tell this drouter instance which networks to connect to. (must match 'docker network create -o drouter=<instance>')",
	}
	var flagNoAggressive = cli.BoolFlag{
		Name:  "no-aggressive",
		Usage: "Set false to make drouter only connect to docker networks with local containers.",
	}
	var flagHostShortcut = cli.BoolFlag{
		Name:  "host-shortcut",
		Usage: "Set true to insert routes in the host destined for docker networks pointing to drouter over a host<->drouter p2p link.",
	}
	var flagContainerGateway = cli.BoolFlag{
		Name:  "container-gateway",
		Usage: "Set true to set the container gateway to drouter.",
	}
	var flagHostGateway = cli.BoolFlag{
		Name:  "host-gateway",
		Usage: "Set true to insert a default route on drouter pointing to the host over the host<->drouter p2p link. (implies --host-shortcut)",
	}
	var flagMasquerade = cli.BoolFlag{
		Name:  "masquerade",
		Usage: "Set true to masquerade container traffic to it's host's interface IP address.",
	}
	var flagP2PNet = cli.StringFlag{
		Name:  "p2p-net",
		Value: "172.29.255.252/30",
		Usage: "Use this option to customize the network used for the host<->drouter p2p link.",
	}
	var flagStaticRoutes = cli.StringSliceFlag{
		Name:  "static-route",
		Usage: "Specify one or many CIDR addresses that will be installed as routes via drouter to all containers.",
	}
	var flagTransitNet = cli.StringFlag{
		Name:  "transit-net",
		Usage: "Set a transit network for drouter to always connect to. Network should have 'drouter' option set. If network has a gateway, and --host-gateway=false, drouter's default gateway will be through this network's gateway. (this option is required with --no-aggressive)",
	}
	app := cli.NewApp()
	app.Name = "docker-drouter"
	app.Usage = "Docker Distributed Router"
	app.Version = version
	app.Flags = []cli.Flag{
		flagDebug,
		flagIPOffset,
		flagDRouterInstance,
		flagNoAggressive,
		flagHostShortcut,
		flagContainerGateway,
		flagHostGateway,
		flagMasquerade,
		flagP2PNet,
		flagStaticRoutes,
		flagTransitNet,
	}
	app.Action = Run
	app.Run(os.Args)
}

// Run initializes the driver
func Run(ctx *cli.Context) error {
	log.SetFormatter(&log.TextFormatter{
		//ForceColors: false,
		//DisableColors: true,
		DisableTimestamp: false,
		FullTimestamp:    true,
	})

	if ctx.Bool("debug") {
		log.SetLevel(log.DebugLevel)
		log.Info("Debug logging enabled")
	}

	opts := &drouter.DistributedRouterOptions{
		IPOffset:         ctx.Int("ip-offset"),
		Aggressive:       !ctx.Bool("no-aggressive"),
		HostShortcut:     ctx.Bool("host-shortcut"),
		ContainerGateway: ctx.Bool("container-gateway"),
		HostGateway:      ctx.Bool("host-gateway"),
		Masquerade:       ctx.Bool("masquerade"),
		P2PAddr:          ctx.String("p2p-net"),
		StaticRoutes:     ctx.StringSlice("static-route"),
		TransitNet:       ctx.String("transit-net"),
	}

	quit := make(chan struct{})

	c := make(chan os.Signal)
	defer close(c)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)
	go func() {
		<-c
		close(quit)
	}()

	err := drouter.Run(opts, quit)
	if err != nil {
		log.Error(err)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/csv"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"
)

type comparableHandler struct {
	http.Handler
}

var undefinedServer = &comparableHandler{
	http.NotFoundHandler(),
}

func main() {
	aliases := getAliases("alias.csv")

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		t1 := time.Now()

		host := aliases.Rewrite(r.Host)

		fs := fileServer(host)
		if fs == undefinedServer {
			// wildcard server rewrite
			fs = fileServer(aliases.Rewrite("*"))
		}

		fs.ServeHTTP(w, r)

		// log request
		hostRW := r.Host
		if host != r.Host {
			hostRW = fmt.Sprintf("%s -> %s", r.Host, host)
		}

		log.Printf("[%s] %s in %s", hostRW, r.URL.Path, time.Now().Sub(t1))
	})

	log.Fatal(http.ListenAndServe(":8000", nil))
}

func fileServer(host string) *comparableHandler {
	l := strings.Split(host, ".")
	parts := make([]string, len(l))
	for i := range parts {
		parts[i] = l[len(l)-i-1]
	}

	root := path.Join("./", strings.Join(parts, "/"))
	if f, err := os.Stat(root); os.IsNotExist(err) || !f.IsDir() {
		return undefinedServer
	}

	return &comparableHandler{http.FileServer(http.Dir(root))}
}

type aliasSet map[string]string

func getAliases(name string) aliasSet {
	f, err := os.Open(name)
	if err != nil {
		return make(aliasSet)
	}
	defer f.Close()

	records, err := csv.NewReader(f).ReadAll()
	if err != nil {
		log.Println(err)
		return make(aliasSet)
	}

	var set = make(aliasSet)
	for _, r := range records {
		from, to := strings.TrimSpace(r[0]), strings.TrimSpace(r[1])
		log.Println("adding alias", from, "->", to)
		set[from] = to
	}

	return set
}

func (a aliasSet) Rewrite(host string) string {
	if to, ok := a[host]; ok {
		host = to
	}
	return host
}
<commit_msg>add custom error pages<commit_after>package main

import (
	"encoding/csv"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"
)

func main() {
	aliases := getAliases("alias.csv")

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		t1 := time.Now()

		host := aliases.Rewrite(r.Host)

		defer func() {
			// log request
			hostRW := r.Host
			if host != r.Host {
				hostRW = fmt.Sprintf("%s -> %s", r.Host, host)
			}

			log.Printf("[%s] %s in %s", hostRW, r.URL.Path, time.Now().Sub(t1))
		}()

		dir := getRootDir(host)
		if dir == "" {
			// wildcard server rewrite
			defaultDomain := getRootDir(aliases.Rewrite("*"))
			if defaultDomain == "" {
				http.Error(w, "Undefined Server", 444)
			} else {
				http.FileServer(defaultDomain).ServeHTTP(w, r)
			}
			return
		}

		// custom error rewrites
		filePath := path.Join(string(dir), path.Clean("/"+r.URL.Path))
		stat, err := os.Stat(filePath)

		if os.IsNotExist(err) {
			aliases.Error(w, r, dir, http.StatusNotFound)
			return
		}

		if stat.IsDir() {
			if _, err := os.Stat(path.Join(filePath, "/index.html")); os.IsNotExist(err) {
				aliases.Error(w, r, dir, http.StatusForbidden)
				return
			}
		}

		http.FileServer(dir).ServeHTTP(w, r)
	})

	log.Fatal(http.ListenAndServe(":8000", nil))
}

func getRootDir(host string) http.Dir {
	l := strings.Split(host, ".")
	parts := make([]string, len(l))
	for i := range parts {
		parts[i] = l[len(l)-i-1]
	}

	root := path.Join("./", path.Clean("/"+strings.Join(parts, "/")))
	if f, err := os.Stat(root); os.IsNotExist(err) || !f.IsDir() {
		return ""
	}

	dir := http.Dir(root)
	return dir
}

type aliasSet map[string]string

func getAliases(name string) aliasSet {
	f, err := os.Open(name)
	if err != nil {
		return make(aliasSet)
	}
	defer f.Close()

	records, err := csv.NewReader(f).ReadAll()
	if err != nil {
		log.Println(err)
		return make(aliasSet)
	}

	var set = make(aliasSet)
	for _, r := range records {
		from, to := strings.TrimSpace(r[0]), strings.TrimSpace(r[1])
		log.Println("adding alias", from, "->", to)
		set[from] = to
	}

	return set
}

func (a aliasSet) Rewrite(host string) string {
	if to, ok := a[host]; ok {
		host = to
	}
	return host
}

func (a aliasSet) Error(w http.ResponseWriter, r *http.Request, dir http.Dir, code int) {
	var codeText string
	switch code {
	case http.StatusNotFound:
		codeText = "[404]"
	case http.StatusForbidden:
		codeText = "[403]"
	default:
		codeText = "[000]"
	}

	if fileName := a.Rewrite(codeText); fileName == codeText {
		http.Error(w, http.StatusText(code), code)
		return
	} else {
		// got custom error
		r.URL.Path = fileName

		f, err := dir.Open(fileName)
		if err != nil {
			http.Error(w, http.StatusText(code), code)
			return
		}
		defer f.Close()

		stat, err := f.Stat()
		if err != nil {
			http.Error(w, http.StatusText(code), code)
			return
		}

		http.ServeContent(w, r, fileName, stat.ModTime(), f)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"runtime"

	"golang.org/x/crypto/ssh/terminal"
)

const (
	keyCtrlC     = 3
	keyCtrlD     = 4
	keyCtrlN     = 14
	keyCtrlP     = 16
	keyCtrlU     = 21
	keyCtrlW     = 23
	keyEnter     = '\r'
	keyEscape    = 27
	keyUp        = 38
	keyDown      = 40
	keyBackspace = 127
)

var (
	SaveCursorPosition     = []byte{keyEscape, '[', 's'}
	RestoreCursorPosition  = []byte{keyEscape, '[', 'u'}
	EraseDisplayFromCursor = []byte{keyEscape, '[', 'J'}
	ReverseColor           = []byte{keyEscape, '[', '7', 'm'}
	ResetColor             = []byte{keyEscape, '[', '0', 'm'}
	ShowCursor             = []byte{keyEscape, '[', '?', '2', '5', 'h'}
)

type TTY struct {
	*os.File
}

func OpenTTY() (*TTY, error) {
	file, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
	tty := TTY{file}
	return &tty, err
}

func (tty *TTY) Fd() int {
	return int(tty.File.Fd())
}

type steps struct {
	Up    int
	Down  int
	Left  int
	Right int
}

func (tty *TTY) moveCursor(step steps) {
	count := step.Down + step.Up + step.Left + step.Right
	movement := make([]byte, 3*(count))
	m := movement
	for i := 0; i < step.Up; i++ {
		m[0] = keyEscape
		m[1] = '['
		m[2] = 'A'
		m = m[3:]
	}
	for i := 0; i < step.Down; i++ {
		m[0] = keyEscape
		m[1] = '['
		m[2] = 'B'
		m = m[3:]
	}
	for i := 0; i < step.Left; i++ {
		m[0] = keyEscape
		m[1] = '['
		m[2] = 'D'
		m = m[3:]
	}
	for i := 0; i < step.Right; i++ {
		m[0] = keyEscape
		m[1] = '['
		m[2] = 'C'
		m = m[3:]
	}

	tty.Write(movement)
}

func (tty *TTY) showCursor() {
	tty.Write(ShowCursor)
}

func (tty *TTY) saveCursorPosition() {
	tty.Write(SaveCursorPosition)
}

func (tty *TTY) restoreCursorPosition() {
	tty.Write(RestoreCursorPosition)
}

func (tty *TTY) eraseDisplayFromCursor() {
	tty.Write(EraseDisplayFromCursor)
}

type Renderer struct {
	tty     *TTY
	width   int
	height  int
	visible int
}

func (r *Renderer) PrepareForTerminalVim() {
	// start from the bottom of the screen
	r.tty.moveCursor(steps{
		Left: r.width,
		Down: r.height,
	})
	r.tty.showCursor()
}

func (r *Renderer) focusWritingPoint(picker *Picker) {
	r.tty.restoreCursorPosition()
	r.tty.moveCursor(steps{
		Right: len(picker.prompt) + len(picker.query),
	})
}

func (r *Renderer) renderFirstFrame(picker *Picker) {
	// write the first view
	r.tty.WriteString(picker.View())

	// going width time to the left is more than necessary but it works in all
	// situations and is simpler
	r.tty.moveCursor(steps{
		Up:   r.visible,
		Left: r.width,
	})

	// save the pos
	r.tty.saveCursorPosition()

	r.focusWritingPoint(picker)
}

func (r *Renderer) Start(channel chan *Picker) {
	// we special case the first picker to render since we have to save a
	// position for later uses
	r.renderFirstFrame(<-channel)

	for picker := range channel {
		r.tty.restoreCursorPosition()
		r.tty.eraseDisplayFromCursor()

		// write what we should see
		r.tty.WriteString(picker.View())

		r.focusWritingPoint(picker)
	}
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

var (
	visible int
	vim     bool
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	flag.IntVar(&visible, "v", 20, "Number of visible candidates")
	flag.BoolVar(&vim, "vim", false, "Print at bottom of screen")
	flag.Parse()

	tty, err := OpenTTY()
	if err != nil {
		panic(err)
	}
	originalState, err := terminal.MakeRaw(tty.Fd())
	if err != nil {
		panic(err)
	}
	defer terminal.Restore(tty.Fd(), originalState)

	width, height, err := terminal.GetSize(tty.Fd())
	if err != nil {
		panic(err)
	}

	if height < visible {
		visible = height
	}

	renderChan := make(chan *Picker)
	renderer := &Renderer{
		tty:     tty,
		height:  height,
		width:   width,
		visible: visible,
	}

	if vim {
		renderer.PrepareForTerminalVim()
	}
	go renderer.Start(renderChan)

	picker := NewPicker("> ", visible, width, os.Stdin, renderChan)

	input := make(chan rune)
	go func() {
		reader := bufio.NewReader(tty)
		for {
			r, _, err := reader.ReadRune()
			if err != nil || r == keyEscape || r == keyCtrlC {
				tty.restoreCursorPosition()
				os.Exit(1)
			}
			input <- r
		}
	}()

	for r := range input {
		switch r {
		case keyEnter:
			tty.restoreCursorPosition()
			fmt.Println(picker.Selected())
			return
		case keyCtrlU, keyCtrlW:
			picker.Clear()
		case keyCtrlN, keyDown:
			picker.Down()
		case keyCtrlP, keyUp:
			picker.Up()
		case keyBackspace:
			picker.Back()
		default:
			picker.More(r)
		}
	}
}
<commit_msg>Expose TTY methods. Add notes about the Renderer<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"runtime"

	"golang.org/x/crypto/ssh/terminal"
)

const (
	keyCtrlC     = 3
	keyCtrlD     = 4
	keyCtrlN     = 14
	keyCtrlP     = 16
	keyCtrlU     = 21
	keyCtrlW     = 23
	keyEnter     = '\r'
	keyEscape    = 27
	keyUp        = 38
	keyDown      = 40
	keyBackspace = 127
)

var (
	SaveCursorPosition     = []byte{keyEscape, '[', 's'}
	RestoreCursorPosition  = []byte{keyEscape, '[', 'u'}
	EraseDisplayFromCursor = []byte{keyEscape, '[', 'J'}
	ReverseColor           = []byte{keyEscape, '[', '7', 'm'}
	ResetColor             = []byte{keyEscape, '[', '0', 'm'}
	ShowCursor             = []byte{keyEscape, '[', '?', '2', '5', 'h'}
)

type TTY struct {
	*os.File
}

func OpenTTY() (*TTY, error) {
	file, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
	tty := TTY{file}
	return &tty, err
}

func (tty *TTY) Fd() int {
	return int(tty.File.Fd())
}

type steps struct {
	Up    int
	Down  int
	Left  int
	Right int
}

func (tty *TTY) moveCursor(step steps) {
	count := step.Down + step.Up + step.Left + step.Right
	movement := make([]byte, 3*(count))
	m := movement
	for i := 0; i < step.Up; i++ {
		m[0] = keyEscape
		m[1] = '['
		m[2] = 'A'
		m = m[3:]
	}
	for i := 0; i < step.Down; i++ {
		m[0] = keyEscape
		m[1] = '['
		m[2] = 'B'
		m = m[3:]
	}
	for i := 0; i < step.Left; i++ {
		m[0] = keyEscape
		m[1] = '['
		m[2] = 'D'
		m = m[3:]
	}
	for i := 0; i < step.Right; i++ {
		m[0] = keyEscape
		m[1] = '['
		m[2] = 'C'
		m = m[3:]
	}

	tty.Write(movement)
}

func (tty *TTY) ShowCursor() {
	tty.Write(ShowCursor)
}

func (tty *TTY) SaveCursorPosition() {
	tty.Write(SaveCursorPosition)
}

func (tty *TTY) RestoreCursorPosition() {
	tty.Write(RestoreCursorPosition)
}

func (tty *TTY) EraseDisplayFromCursor() {
	tty.Write(EraseDisplayFromCursor)
}

// XXX: The renderer work is still spread across the file.
// XXX: The renderer is still buggy when dealing with lots of candidates.
// Possibly a problem of sincronization.
type Renderer struct {
	tty     *TTY
	width   int
	height  int
	visible int
}

func (r *Renderer) PrepareForTerminalVim() {
	// start from the bottom of the screen
	r.tty.moveCursor(steps{
		Left: r.width,
		Down: r.height,
	})
	r.tty.ShowCursor()
}

func (r *Renderer) focusWritingPoint(picker *Picker) {
	r.tty.RestoreCursorPosition()
	r.tty.moveCursor(steps{
		Right: len(picker.prompt) + len(picker.query),
	})
}

func (r *Renderer) renderFirstFrame(picker *Picker) {
	// write the first view
	r.tty.WriteString(picker.View())

	// going width time to the left is more than necessary but it works in all
	// situations and is simpler
	r.tty.moveCursor(steps{
		Up:   r.visible,
		Left: r.width,
	})

	// save the pos
	r.tty.SaveCursorPosition()

	r.focusWritingPoint(picker)
}

func (r *Renderer) Start(channel chan *Picker) {
	// we special case the first picker to render since we have to save a
	// position for later uses
	r.renderFirstFrame(<-channel)

	for picker := range channel {
		r.tty.RestoreCursorPosition()
		r.tty.EraseDisplayFromCursor()

		// write what we should see
		r.tty.WriteString(picker.View())

		r.focusWritingPoint(picker)
	}
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

var (
	visible int
	vim     bool
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	flag.IntVar(&visible, "v", 20, "Number of visible candidates")
	flag.BoolVar(&vim, "vim", false, "Print at bottom of screen")
	flag.Parse()

	tty, err := OpenTTY()
	if err != nil {
		panic(err)
	}
	originalState, err := terminal.MakeRaw(tty.Fd())
	if err != nil {
		panic(err)
	}
	defer terminal.Restore(tty.Fd(), originalState)

	width, height, err := terminal.GetSize(tty.Fd())
	if err != nil {
		panic(err)
	}

	if height < visible {
		visible = height
	}

	renderChan := make(chan *Picker)
	renderer := &Renderer{
		tty:     tty,
		height:  height,
		width:   width,
		visible: visible,
	}

	if vim {
		renderer.PrepareForTerminalVim()
	}
	go renderer.Start(renderChan)

	picker := NewPicker("> ", visible, width, os.Stdin, renderChan)

	input := make(chan rune)
	go func() {
		reader := bufio.NewReader(tty)
		for {
			r, _, err := reader.ReadRune()
			if err != nil || r == keyEscape || r == keyCtrlC {
				tty.RestoreCursorPosition()
				os.Exit(1)
			}
			input <- r
		}
	}()

	for r := range input {
		switch r {
		case keyEnter:
			tty.RestoreCursorPosition()
			fmt.Println(picker.Selected())
			return
		case keyCtrlU, keyCtrlW:
			picker.Clear()
		case keyCtrlN, keyDown:
			picker.Down()
		case keyCtrlP, keyUp:
			picker.Up()
		case keyBackspace:
			picker.Back()
		default:
			picker.More(r)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/tls"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

// urlsInProgress is a wait group, for concurrency
var urlsInProgress sync.WaitGroup

// Responses is a channel to store the concurrent responses from the target
var responses chan *http.Response

// RedirectError is a custom error type for following redirects, and can be safely ignored
type RedirectError struct {
	RedirectRequest *http.Request
}

// Error method returns a string of the error
func (err *RedirectError) Error() string {
	return fmt.Sprintf("Redirect not followed to: %v", err.RedirectRequest.URL.String())
}

// Request body content
var body string

// Target URL value
var targetURL *url.URL

// Cookie jar value
var jar *cookiejar.Jar

// Number of requests
var numRequests int

// Verbose logging enabled
var verbose bool

// Command-line flags
var flagTargetURL = flag.String("url", "", "URL to send the request to.")
var flagBodyFile = flag.String("body", "", "The location (relative or absolute path) of a file containing the body of the request.")
var flagCookiesFile = flag.String("cookies", "", "The location (relative or absolute path) of a file containing newline-separate cookie values being sent along with the request. Cookie names and values are separated by a comma. For example: cookiename,cookieval")
var flagNumRequests = flag.Int("requests", 100, "The number of requests to send to the destination URL.")

//var flagVerbose = flag.Bool("v", false, "Enable verbose logging.")

func main() {
	// Change output location of logs
	log.SetOutput(os.Stdout)

	// Check the flags
	err := checkFlags()
	if err != nil {
		log.Println(err.Error())
		flag.Usage()
		os.Exit(1)
	}

	// Send the requests concurrently
	errChan := sendRequests()
	if len(errChan) != 0 {
		for err := range errChan {
			log.Printf("[ERROR] %s\n", err.Error())
		}
	}

	// Compare the responses for uniqueness
	uniqueResponses := compareResponses()

	// Output the responses
	log.Printf("Responses:\n")
	outputResponses(uniqueResponses)

	// Echo completion
	log.Println("Complete.")
}

// Function checkFlags checks that all necessary flags are entered, and parses them for contents.
// Returns a custom error if something went wrong.
func checkFlags() error {
	// Parse the flags
	flag.Parse()

	// set verbose logging explicitely
	verbose = *flagVerbose

	// Ensure that the destination URL is present
	if *flagTargetURL == "" {
		return fmt.Errorf("Destination URL required.")
	}

	// Parse the URL
	var err error
	targetURL, err = url.Parse(*flagTargetURL)
	if err != nil {
		return fmt.Errorf("Invalid URL provided: %s", *flagTargetURL)
	}

	// Get the request body content
	if *flagBodyFile != "" {
		file, err := os.Open(*flagBodyFile)
		if err != nil {
			// Error opening the file
			return fmt.Errorf("Unable to open the file: %s\n", *flagBodyFile)
		}

		// Ensure that the file is closed
		defer file.Close()

		// Initialize the file scanner
		scanner := bufio.NewScanner(file)

		// Iterate through the first line of the file
		line := 0
		for scanner.Scan() && line < 1 {
			body = scanner.Text()
			line++
		}
	} else {
		// Body file flag not present, exit.
		return fmt.Errorf("Request body contents required.")
	}

	// Check the number of requests used for testing
	numRequests = *flagNumRequests

	// Initialize the cookie jar
	jar, _ = cookiejar.New(nil)
	var cookies []*http.Cookie
	// Get the cookies to pass to the request
	if *flagCookiesFile != "" {
		file, err := os.Open(*flagCookiesFile)
		if err != nil {
			// Error opening the file
			return fmt.Errorf("Unable to open the file: %s", *flagCookiesFile)
		}

		// Ensure the file is closed
		defer file.Close()

		// Initialize the file scanner
		scanner := bufio.NewScanner(file)

		// Iterate through the file to get the cookies
		for scanner.Scan() {
			// Parse the line to separate the cookie names and values
			nextLine := scanner.Text()
			vals := strings.Split(nextLine, ",")
			cookieName := strings.TrimSpace(vals[0])
			cookieValue := strings.TrimSpace(vals[1])

			// Create the cookie
			cookie := &http.Cookie{
				Name:  cookieName,
				Value: cookieValue,
			}

			// Add the cookie to the existing slice of cookies
			cookies = append(cookies, cookie)
		}

		// Set the cookies to the appropriate URL
		jar.SetCookies(targetURL, cookies)

	}

	// Made it through with no errors, return
	return nil
}

// Function sendRequests takes care of sending the requests to the target concurrently.
// Errors are passed back in a channel of errors. If the length is zero, there were no errors.
func sendRequests() chan error {
	// Initialize the concurrency objects
	responses = make(chan *http.Response, numRequests)
	errorChannel := make(chan error, numRequests)
	urlsInProgress.Add(numRequests)

	// Start sending requests
	log.Println("Requests started.")
	for i := 0; i < numRequests; i++ {
		go func(index int) {
			// Ensure that the waitgroup element is returned
			defer urlsInProgress.Done()

			// Convert the request body to an io.Reader interface, to pass to the request.
			// This must be done in the loop, because any call to client.Do() will
			// read the body contents on the first time, but not any subsequent requests.
			requestBody := strings.NewReader(body)

			// Declare HTTP request method and URL
			req, err := http.NewRequest("POST", targetURL.String(), requestBody)
			if err != nil {
				errorChannel <- fmt.Errorf("Error in forming request: %v", err.Error())
				return
			}

			// Create the HTTP client
			// Using Cookie jar
			// Ignoring TLS errors
			// Ignoring redirects (more accurate output)
			// Implementing a connection timeouts, for slow clients & servers (especially important with race conditions on the server)
			client := http.Client{
				Jar: jar,
				Transport: &http.Transport{
					TLSClientConfig: &tls.Config{
						InsecureSkipVerify: true,
					},
				},
				CheckRedirect: func(req *http.Request, via []*http.Request) error {
					// Craft the custom error
					redirectError := RedirectError{req}
					return &redirectError
				},
				Timeout: 20 * time.Second,
			}

			// Make the request
			resp, err := client.Do(req)
			// Check the error type from the request
			if err != nil {
				if uErr, ok := err.(*url.Error); ok {
					if _, ok2 := uErr.Err.(*RedirectError); ok2 {
						// Redirect error
						//errorChannel <- fmt.Errorf("[VERBOSE] %v", rErr)
						// Add the response to the responses channel, because it is still valid
						responses <- resp
					} else {
						// URL Error, but not a redirect error
						errorChannel <- fmt.Errorf("Error in request #%v: %v\n", index, err)
					}
				} else {
					// Other type of error
					errorChannel <- fmt.Errorf("Error in request #%v: %v\n", index, err)
				}
			} else {
				// Add the response to the responses channel
				responses <- resp
			}
		}(i)
	}

	// Wait for the URLs to finish sending
	urlsInProgress.Wait()

	// Close the response and error chanels, so they don't block on the range read
	close(responses)
	close(errorChannel)

	return errorChannel
}

// Function compareResponses compares the responses returned from the requests,
// and adds them to a map, where the key is an *http.Response, and the value is
// the number of similar responses observed.
func compareResponses() (uniqueResponses map[*http.Response]int) {
	// Initialize the unique responses map
	uniqueResponses = make(map[*http.Response]int)

	// Compare the responses, one at a time
	for resp := range responses {
		// Add an entry, if the unique responses map is empty
		if len(uniqueResponses) == 0 {
			uniqueResponses[resp] = 0
		} else {
			// Add to the unique responses map, if no similar ones exist
			for uResp := range uniqueResponses {
				// Compare response status code & content length
				if resp.StatusCode == uResp.StatusCode && resp.ContentLength == uResp.ContentLength {
					// Similar, increase count
					uniqueResponses[uResp]++
					// Exit inner loop
					continue
				} else {
					// Unique, add to unique responses
					uniqueResponses[resp] = 0
					// Exit inner loop
					continue
				}
			}
		}
	}

	return
}

func outputResponses(uniqueResponses map[*http.Response]int) {
	// Display the responses
	log.Printf("Responses:\n")
	for resp, count := range uniqueResponses {
		fmt.Printf("Response:\n")
		fmt.Printf("[Status Code] %v\n", resp.StatusCode)
		fmt.Printf("[Protocol] %v\n", resp.Proto)
		if len(resp.Header) != 0 {
			fmt.Println("[Headers]")
			for header, value := range resp.Header {
				fmt.Printf("\t%v: %v\n", header, value)
			}
		}
		fmt.Printf("[Content-Length] %v\n", resp.ContentLength)
		location, err := resp.Location()
		if err != http.ErrNoLocation {
			fmt.Printf("[Location] %v\n", location.String())
		}
		respBody, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("[Body] ")
		} else {
			// Make sure the response body is closed
			defer resp.Body.Close()
			fmt.Printf("[Body]\n%s\n", respBody)
		}
		fmt.Printf("Similar: %v\n\n", count)
	}
}

// TODO: Add in verbosity (print response bodies, declare when sending a request)
// TODO: Add in option to do GET, HEAD, PUT, or POST (only ones supported by http.Client.Do)
// TODO: Add in user option to follow redirects (default: no)
<commit_msg>Added verbosity option.<commit_after>package main

import (
	"bufio"
	"crypto/tls"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

// urlsInProgress is a wait group, for concurrency
var urlsInProgress sync.WaitGroup

// Responses is a channel to store the concurrent responses from the target
var responses chan *http.Response

// RedirectError is a custom error type for following redirects, and can be safely ignored
type RedirectError struct {
	RedirectRequest *http.Request
}

// Error method returns a string of the error
func (err *RedirectError) Error() string {
	return fmt.Sprintf("Redirect not followed to: %v", err.RedirectRequest.URL.String())
}

// Request body content
var body string

// Target URL value
var targetURL *url.URL

// Cookie jar value
var jar *cookiejar.Jar

// Number of requests
var numRequests int

// Verbose logging enabled
var verbose bool

// Command-line flags
var flagTargetURL = flag.String("url", "", "URL to send the request to.")
var flagBodyFile = flag.String("body", "", "The location (relative or absolute path) of a file containing the body of the request.")
var flagCookiesFile = flag.String("cookies", "", "The location (relative or absolute path) of a file containing newline-separate cookie values being sent along with the request. Cookie names and values are separated by a comma. For example: cookiename,cookieval")
var flagNumRequests = flag.Int("requests", 100, "The number of requests to send to the destination URL.")
var flagVerbose = flag.Bool("v", false, "Enable verbose logging.")

func main() {
	// Change output location of logs
	log.SetOutput(os.Stdout)

	// Check the flags
	err := checkFlags()
	if err != nil {
		log.Println(err.Error())
		flag.Usage()
		os.Exit(1)
	}

	// Send the requests concurrently
	log.Println("Requests begin.")
	errChan := sendRequests()
	if len(errChan) != 0 {
		for err := range errChan {
			log.Printf("[ERROR] %s\n", err.Error())
		}
	}

	// Compare the responses for uniqueness
	uniqueResponses := compareResponses()

	// Output the responses
	outputResponses(uniqueResponses)

	// Echo completion
	log.Println("Complete.")
}

// Function checkFlags checks that all necessary flags are entered, and parses them for contents.
// Returns a custom error if something went wrong.
func checkFlags() error {
	// Parse the flags
	flag.Parse()

	// set verbose logging explicitely
	verbose = *flagVerbose

	// Ensure that the destination URL is present
	if *flagTargetURL == "" {
		return fmt.Errorf("Destination URL required.")
	}

	// Parse the URL
	var err error
	targetURL, err = url.Parse(*flagTargetURL)
	if err != nil {
		return fmt.Errorf("Invalid URL provided: %s", *flagTargetURL)
	}

	// Get the request body content
	if *flagBodyFile != "" {
		file, err := os.Open(*flagBodyFile)
		if err != nil {
			// Error opening the file
			return fmt.Errorf("Unable to open the file: %s\n", *flagBodyFile)
		}

		// Ensure that the file is closed
		defer file.Close()

		// Initialize the file scanner
		scanner := bufio.NewScanner(file)

		// Iterate through the first line of the file
		line := 0
		for scanner.Scan() && line < 1 {
			body = scanner.Text()
			line++
		}
	} else {
		// Body file flag not present, exit.
		return fmt.Errorf("Request body contents required.")
	}

	// Check the number of requests used for testing
	numRequests = *flagNumRequests

	// Initialize the cookie jar
	jar, _ = cookiejar.New(nil)
	var cookies []*http.Cookie
	// Get the cookies to pass to the request
	if *flagCookiesFile != "" {
		file, err := os.Open(*flagCookiesFile)
		if err != nil {
			// Error opening the file
			return fmt.Errorf("Unable to open the file: %s", *flagCookiesFile)
		}

		// Ensure the file is closed
		defer file.Close()

		// Initialize the file scanner
		scanner := bufio.NewScanner(file)

		// Iterate through the file to get the cookies
		for scanner.Scan() {
			// Parse the line to separate the cookie names and values
			nextLine := scanner.Text()
			vals := strings.Split(nextLine, ",")
			cookieName := strings.TrimSpace(vals[0])
			cookieValue := strings.TrimSpace(vals[1])

			// Create the cookie
			cookie := &http.Cookie{
				Name:  cookieName,
				Value: cookieValue,
			}

			// Add the cookie to the existing slice of cookies
			cookies = append(cookies, cookie)
		}

		// Set the cookies to the appropriate URL
		jar.SetCookies(targetURL, cookies)

	}

	// Made it through with no errors, return
	return nil
}

// Function sendRequests takes care of sending the requests to the target concurrently.
// Errors are passed back in a channel of errors. If the length is zero, there were no errors.
func sendRequests() chan error {
	// Initialize the concurrency objects
	responses = make(chan *http.Response, numRequests)
	errorChannel := make(chan error, numRequests)
	urlsInProgress.Add(numRequests)

	// VERBOSE
	if verbose {
		log.Printf("[VERBOSE] Sending %d %s requests to %s\n", numRequests, "POST", targetURL.String())
		if body != "" {
			log.Printf("[VERBOSE] Request body: %s", body)
		}
	}
	for i := 0; i < numRequests; i++ {
		go func(index int) {
			// Ensure that the waitgroup element is returned
			defer urlsInProgress.Done()

			// Convert the request body to an io.Reader interface, to pass to the request.
			// This must be done in the loop, because any call to client.Do() will
			// read the body contents on the first time, but not any subsequent requests.
			requestBody := strings.NewReader(body)

			// Declare HTTP request method and URL
			req, err := http.NewRequest("POST", targetURL.String(), requestBody)
			if err != nil {
				errorChannel <- fmt.Errorf("Error in forming request: %v", err.Error())
				return
			}

			// Create the HTTP client
			// Using Cookie jar
			// Ignoring TLS errors
			// Ignoring redirects (more accurate output)
			// Implementing a connection timeouts, for slow clients & servers (especially important with race conditions on the server)
			client := http.Client{
				Jar: jar,
				Transport: &http.Transport{
					TLSClientConfig: &tls.Config{
						InsecureSkipVerify: true,
					},
				},
				CheckRedirect: func(req *http.Request, via []*http.Request) error {
					// Craft the custom error
					redirectError := RedirectError{req}
					return &redirectError
				},
				Timeout: 20 * time.Second,
			}

			// Make the request
			resp, err := client.Do(req)
			// Check the error type from the request
			if err != nil {
				if uErr, ok := err.(*url.Error); ok {
					if rErr, ok2 := uErr.Err.(*RedirectError); ok2 {
						// Redirect error
						// VERBOSE
						if verbose {
							log.Printf("[VERBOSE] %v\n", rErr)
						}
						// Add the response to the responses channel, because it is still valid
						responses <- resp
					} else {
						// URL Error, but not a redirect error
						errorChannel <- fmt.Errorf("Error in request #%v: %v\n", index, err)
					}
				} else {
					// Other type of error
					errorChannel <- fmt.Errorf("Error in request #%v: %v\n", index, err)
				}
			} else {
				// Add the response to the responses channel
				responses <- resp
			}
		}(i)
	}

	// Wait for the URLs to finish sending
	urlsInProgress.Wait()

	// VERBOSE
	if verbose {
		log.Printf("[VERBOSE] Requests complete.")
	}

	// Close the response and error chanels, so they don't block on the range read
	close(responses)
	close(errorChannel)

	return errorChannel
}

// Function compareResponses compares the responses returned from the requests,
// and adds them to a map, where the key is an *http.Response, and the value is
// the number of similar responses observed.
func compareResponses() (uniqueResponses map[*http.Response]int) {
	// Initialize the unique responses map
	uniqueResponses = make(map[*http.Response]int)

	// VERBOSE
	if verbose {
		log.Printf("[VERBOSE] Unique response comparison begin.\n")
	}

	// Compare the responses, one at a time
	for resp := range responses {
		// Add an entry, if the unique responses map is empty
		if len(uniqueResponses) == 0 {
			uniqueResponses[resp] = 0
		} else {
			// Add to the unique responses map, if no similar ones exist
			for uResp := range uniqueResponses {
				// Compare response status code & content length
				if resp.StatusCode == uResp.StatusCode && resp.ContentLength == uResp.ContentLength {
					// Similar, increase count
					uniqueResponses[uResp]++
					// Exit inner loop
					continue
				} else {
					// Unique, add to unique responses
					uniqueResponses[resp] = 0
					// Exit inner loop
					continue
				}
			}
		}
	}

	// VERBOSE
	if verbose {
		log.Printf("[VERBOSE] Unique response comparision complete.\n")
	}

	return
}

func outputResponses(uniqueResponses map[*http.Response]int) {
	// Display the responses
	log.Printf("Responses:\n")
	for resp, count := range uniqueResponses {
		fmt.Printf("Response:\n")
		fmt.Printf("[Status Code] %v\n", resp.StatusCode)
		fmt.Printf("[Protocol] %v\n", resp.Proto)
		if len(resp.Header) != 0 {
			fmt.Println("[Headers]")
			for header, value := range resp.Header {
				fmt.Printf("\t%v: %v\n", header, value)
			}
		}
		location, err := resp.Location()
		if err != http.ErrNoLocation {
			fmt.Printf("[Location] %v\n", location.String())
		}
		respBody, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("[Body] ")
		} else {
			// Make sure the response body is closed
			defer resp.Body.Close()
			fmt.Printf("[Body]\n%s\n", respBody)
		}
		fmt.Printf("Similar: %v\n\n", count)
	}
}

// TODO: Add in option to do GET, HEAD, PUT, or POST (only ones supported by http.Client.Do)
// TODO: Add in user option to follow redirects (default: no)
// TODO: Compare response body as well (if content-length != 0)
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"github.com/vandosant/commandeer/models"
)

var commands models.Commands

func CommandHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/vnd.application+json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(commands); err != nil {
		panic(err)
	}
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	http.HandleFunc("/", CommandHandler)
	http.HandleFunc("/cmds", CommandHandler)

	fmt.Printf("Now running on port %s\n", port)
	http.ListenAndServe(":"+port, nil)

}
<commit_msg>Only index uses CommandHandler<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"github.com/vandosant/commandeer/models"
)

var commands models.Commands

func CommandHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/vnd.application+json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(commands); err != nil {
		panic(err)
	}
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	http.HandleFunc("/", CommandHandler)

	fmt.Printf("Now running on port %s\n", port)
	http.ListenAndServe(":"+port, nil)

}
<|endoftext|>


<commit_before>package main

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

	"github.com/lair-framework/go-lair"
	"github.com/tomsteele/blacksheepwall/bsw"
)

const (
	version = "2.0.0"
	tool    = "blacksheepwall"
	usage   = `
Usage:
  drone-blacksheepwall <id> <filename>
  export LAIR_ID=<id>; drone-blacksheepwall <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 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")
	}
	log.Println(lairPID, filename, *insecureSSL, *forcePorts, *tags)
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalf("Fatal: Could not open file. Error %s", err.Error())
	}
	hostTags := strings.Split(*tags, ",")
	tagSet := map[string]bool{}
	bResults := bsw.Results{}
	if err := json.Unmarshal(data, bResults); err != nil {
		log.Fatalf("Fatal: Could not parse JSON. Error %s", err.Error())
	}
	bNotFound := map[string]bool{}
	// Get this from API
	exproject := lair.Project{}
	project := lair.Project{}

	project.Tool = tool
	project.Commands = append(project.Commands, lair.Command{
		Tool: tool,
	})
	for _, result := range bResults {
		found := false
		for _, h := range exproject.Hosts {
			if result.IP == h.IPv4 {
				h.Hostnames = append(h.Hostnames, result.Hostname)
				h.LastModifiedBy = tool
				found = true
				if _, ok := tagSet[h.IPv4]; !ok {
					tagSet[h.IPv4] = true
					h.Tags = append(h.Tags, hostTags...)
				}
			}
			if !found {
				bNotFound[result.IP] = true
			}
		}
	}
	for _, h := range exproject.Hosts {
		project.Hosts = append(project.Hosts, lair.Host{
			IPv4:           h.IPv4,
			LongIPv4Addr:   h.LongIPv4Addr,
			IsFlagged:      h.IsFlagged,
			LastModifiedBy: h.LastModifiedBy,
			MAC:            h.MAC,
			OS:             h.OS,
			Status:         h.Status,
			StatusMessage:  h.StatusMessage,
			Tags:           h.Tags,
			Hostnames:      h.Hostnames,
		})
	}
	// upload project to api
	if len(bNotFound) > 0 {
		log.Println("Info: The following hosts had hostnames but could not be imported because they do not exist in lair")
	}
	for k := range bNotFound {
		fmt.Println(k)
	}
	log.Println("Success: Operation completed successfully")
}
<commit_msg>Updates usage and sets up import project<commit_after>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/tomsteele/blacksheepwall/bsw"
)

const (
	version = "2.0.0"
	tool    = "blacksheepwall"
	usage   = `
Parses a blacksheepwall JSON file into a lair project.

Usage:
  drone-blacksheepwall <id> <filename>
  export LAIR_ID=<id>; drone-blacksheepwall <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 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())
	}
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalf("Fatal: Could not open file. Error %s", err.Error())
	}
	hostTags := []string{}
	if *tags != "" {
		hostTags = strings.Split(*tags, ",")
	}
	tagSet := map[string]bool{}
	bResults := bsw.Results{}
	if err := json.Unmarshal(data, bResults); err != nil {
		log.Fatalf("Fatal: Could not parse JSON. Error %s", err.Error())
	}
	bNotFound := map[string]bool{}
	// Get this from API
	exproject := lair.Project{}
	project := &lair.Project{
		ID:   lairPID,
		Tool: tool,
		Commands: []lair.Command{lair.Command{
			Tool: tool,
		}},
	}

	for _, result := range bResults {
		found := false
		for _, h := range exproject.Hosts {
			if result.IP == h.IPv4 {
				h.Hostnames = append(h.Hostnames, result.Hostname)
				h.LastModifiedBy = tool
				found = true
				if _, ok := tagSet[h.IPv4]; !ok {
					tagSet[h.IPv4] = true
					h.Tags = append(h.Tags, hostTags...)
				}
			}
			if !found {
				bNotFound[result.IP] = true
			}
		}
	}

	for _, h := range exproject.Hosts {
		project.Hosts = append(project.Hosts, lair.Host{
			IPv4:           h.IPv4,
			LongIPv4Addr:   h.LongIPv4Addr,
			IsFlagged:      h.IsFlagged,
			LastModifiedBy: h.LastModifiedBy,
			MAC:            h.MAC,
			OS:             h.OS,
			Status:         h.Status,
			StatusMessage:  h.StatusMessage,
			Tags:           hostTags,
			Hostnames:      h.Hostnames,
		})
	}

	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.Println("Info: The following hosts had hostnames but could not be imported because they do not exist in lair")
	}
	for k := range bNotFound {
		fmt.Println(k)
	}
	log.Println("Success: Operation completed successfully")
}
<|endoftext|>


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

package main

import (
	"fmt"
	"log"
	"net/http"
	"math/rand"
	"time"
	"strconv"
	"strings"
	"os"
	"golang.org/x/net/context"
	"github.com/line/line-bot-sdk-go/linebot"
	"googlemaps.github.io/maps"
)

var bot *linebot.Client

func main() {
	var err error
	bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

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

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		switch event.Type {
		case linebot.EventTypeMessage:
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				if err := handleText(message, event.ReplyToken, event.Source); err != nil {
					log.Print(err)
				}
			case *linebot.ImageMessage:
				if err := handleImage(message, event.ReplyToken); err != nil {
					log.Print(err)
				}
			case *linebot.VideoMessage:
				if err := handleVideo(message, event.ReplyToken); err != nil {
					log.Print(err)
				}
			case *linebot.AudioMessage:
				if err := handleAudio(message, event.ReplyToken); err != nil {
					log.Print(err)
				}
			case *linebot.LocationMessage:
				if err := handleLocation(message, event.ReplyToken); err != nil {
					log.Print(err)
				}
			case *linebot.StickerMessage:
				if err := handleSticker(message, event.ReplyToken); err != nil {
					log.Print(err)
				}
			default:
				log.Printf("Unknown message: %v", message)
			}
		case linebot.EventTypeFollow:
			log.Print("Followed this bot: %v", event)
		case linebot.EventTypeUnfollow:
			log.Printf("Unfollowed this bot: %v", event)
		case linebot.EventTypeJoin:
			log.Print("Join: %v", event)
		case linebot.EventTypeLeave:
			log.Printf("Left: %v", event)
		case linebot.EventTypePostback:
			log.Print("Postback: %v", event)
		case linebot.EventTypeBeacon:
			log.Print("Beacon: %v", event)
		default:
			log.Printf("Unknown event: %v", event)
		}
	}

	/*
	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				action := strings.Contains(message.Text, "吃")
				target := strings.Contains(message.Text, "什麼")
				if !target {
					target = strings.Contains(message.Text, "啥")
				}

				if action && target {
					log.Print("SIVA: BINGO")

					i := random(1, 10)
					env := strconv.FormatInt(int64(i), 10)
					env = "SWFood"+env
					ans := os.Getenv(env)
					log.Print("SIVA: "+ans)

					if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(ans)).Do(); err != nil {
						log.Print(err)
					}
				}
				//if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(message.ID+":"+message.Text+" OK!")).Do(); err != nil {
				//	log.Print(err)
				//}
			}
		}
	}
	*/
}

func handleText(message *linebot.TextMessage, replyToken string, source *linebot.EventSource) error {

	action := strings.Contains(message.Text, "吃")
	target := strings.Contains(message.Text, "什麼")
	if !target {
		target = strings.Contains(message.Text, "啥")
	}

	if action && target {
		log.Print("SIVA: BINGO")

		i := random(1, 10)
		env := strconv.FormatInt(int64(i), 10)
		env = "SWFood"+env
		ans := os.Getenv(env)
		log.Print("SIVA: "+ans)

		if _, err := bot.ReplyMessage(replyToken, linebot.NewTextMessage(ans)).Do(); err != nil {
			log.Print(err)
		}
	}

	return nil
}

func handleImage(message *linebot.ImageMessage, replyToken string) error {
	return nil
}

func handleVideo(message *linebot.VideoMessage, replyToken string) error {
	return nil
}

func handleAudio(message *linebot.AudioMessage, replyToken string) error {
	return nil
}

func handleLocation(message *linebot.LocationMessage, replyToken string) error {
	log.Print("handleLocation")

	c, err := maps.NewClient(maps.WithAPIKey(os.Getenv("GMAP_KEY")))
    if err != nil {
        log.Fatalf("fatal error: %s", err)
    }

    ll := &maps.LatLng{
    	Lat: message.Latitude,
    	Lng: message.Longitude,
    }

    r := &maps.NearbySearchRequest{
        Location: ll,
        Type: "food",
        Language: "zh",
        //Region: "TW",
        OpenNow: true,
        RankBy: "distance",
    }

    resp, err := c.NearbySearch(context.Background(), r)
    if err != nil {
        log.Fatalf("fatal error: %s", err)
    } else {
    	len := len(resp.Results)
    	log.Print("length = %i", len)

    	i := random(0, len - 1)
    	log.Print("target = %i", i)
    	log.Print(resp.Results[i])
    }

	return nil
}

func handleSticker(message *linebot.StickerMessage, replyToken string) error {
	return nil
}<commit_msg>reply suggest place.<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"log"
	"net/http"
	"math/rand"
	"time"
	"strconv"
	"strings"
	"os"
	"golang.org/x/net/context"
	"github.com/line/line-bot-sdk-go/linebot"
	"googlemaps.github.io/maps"
)

var bot *linebot.Client

func main() {
	var err error
	bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

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

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		switch event.Type {
		case linebot.EventTypeMessage:
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				if err := handleText(message, event.ReplyToken, event.Source); err != nil {
					log.Print(err)
				}
			case *linebot.ImageMessage:
				if err := handleImage(message, event.ReplyToken); err != nil {
					log.Print(err)
				}
			case *linebot.VideoMessage:
				if err := handleVideo(message, event.ReplyToken); err != nil {
					log.Print(err)
				}
			case *linebot.AudioMessage:
				if err := handleAudio(message, event.ReplyToken); err != nil {
					log.Print(err)
				}
			case *linebot.LocationMessage:
				if err := handleLocation(message, event.ReplyToken); err != nil {
					log.Print(err)
				}
			case *linebot.StickerMessage:
				if err := handleSticker(message, event.ReplyToken); err != nil {
					log.Print(err)
				}
			default:
				log.Printf("Unknown message: %v", message)
			}
		case linebot.EventTypeFollow:
			log.Print("Followed this bot: %v", event)
		case linebot.EventTypeUnfollow:
			log.Printf("Unfollowed this bot: %v", event)
		case linebot.EventTypeJoin:
			log.Print("Join: %v", event)
		case linebot.EventTypeLeave:
			log.Printf("Left: %v", event)
		case linebot.EventTypePostback:
			log.Print("Postback: %v", event)
		case linebot.EventTypeBeacon:
			log.Print("Beacon: %v", event)
		default:
			log.Printf("Unknown event: %v", event)
		}
	}

	/*
	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				action := strings.Contains(message.Text, "吃")
				target := strings.Contains(message.Text, "什麼")
				if !target {
					target = strings.Contains(message.Text, "啥")
				}

				if action && target {
					log.Print("SIVA: BINGO")

					i := random(1, 10)
					env := strconv.FormatInt(int64(i), 10)
					env = "SWFood"+env
					ans := os.Getenv(env)
					log.Print("SIVA: "+ans)

					if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(ans)).Do(); err != nil {
						log.Print(err)
					}
				}
				//if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(message.ID+":"+message.Text+" OK!")).Do(); err != nil {
				//	log.Print(err)
				//}
			}
		}
	}
	*/
}

func handleText(message *linebot.TextMessage, replyToken string, source *linebot.EventSource) error {

	action := strings.Contains(message.Text, "吃")
	target := strings.Contains(message.Text, "什麼")
	if !target {
		target = strings.Contains(message.Text, "啥")
	}

	if action && target {
		log.Print("SIVA: BINGO")

		i := random(1, 10)
		env := strconv.FormatInt(int64(i), 10)
		env = "SWFood"+env
		ans := os.Getenv(env)
		log.Print("SIVA: "+ans)

		if _, err := bot.ReplyMessage(replyToken, linebot.NewTextMessage(ans)).Do(); err != nil {
			log.Print(err)
		}
	}

	return nil
}

func handleImage(message *linebot.ImageMessage, replyToken string) error {
	return nil
}

func handleVideo(message *linebot.VideoMessage, replyToken string) error {
	return nil
}

func handleAudio(message *linebot.AudioMessage, replyToken string) error {
	return nil
}

func handleLocation(message *linebot.LocationMessage, replyToken string) error {
	log.Print("handleLocation")

	c, err := maps.NewClient(maps.WithAPIKey(os.Getenv("GMAP_KEY")))
    if err != nil {
        log.Fatalf("fatal error: %s", err)
    }

    ll := &maps.LatLng{
    	Lat: message.Latitude,
    	Lng: message.Longitude,
    }

    r := &maps.NearbySearchRequest{
        Location: ll,
        Type: "food",
        Language: "zh",
        //Region: "TW",
        OpenNow: true,
        RankBy: "distance",
    }

    resp, err := c.NearbySearch(context.Background(), r)
    if err != nil {
        log.Fatalf("fatal error: %s", err)
    } else {
    	len := len(resp.Results)
    	log.Print("length = %d", len)

    	i := random(0, len - 1)
    	log.Print("target = %d", i)
    	log.Print("Name = %s", resp.Results[i].Name)
    	log.Print("FormattedAddress = %s", resp.Results[i].FormattedAddress)

    	messsage := resp.Results[i].Name+"\n"+resp.Results[i].FormattedAddress
    	if _, err := bot.ReplyMessage(replyToken, linebot.NewTextMessage(messsage)).Do(); err != nil {
			log.Print(err)
		}
    }

	return nil
}

func handleSticker(message *linebot.StickerMessage, replyToken string) error {
	return nil
}<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/orchardup/orchard/cli"
	"github.com/orchardup/orchard/proxy"
	"github.com/orchardup/orchard/tlsconfig"
	"github.com/orchardup/orchard/vendor/github.com/docopt/docopt.go"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"text/tabwriter"
)

func main() {
	usage := `Orchard.

Usage:
  orchard hosts
  orchard hosts create NAME
  orchard hosts rm NAME
  orchard [options] docker [COMMAND...]
  orchard [options] proxy

Options:
  -h --help               Show this screen.
  --version               Show version.
  -H NAME, --host=NAME    Name of host to connect to (instead of 'default')`

	args, err := docopt.Parse(usage, nil, true, "Orchard 2.0.0", true)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error parsing arguments: %s\n", err)
		return
	}

	var cmdErr error = nil

	if args["hosts"] == true {
		cmdErr = Hosts(args)
	} else if args["docker"] == true || args["proxy"] == true {
		cmdErr = Docker(args)
	}

	if cmdErr != nil {
		fmt.Fprintln(os.Stderr, cmdErr)
		os.Exit(1)
	}
}

func Docker(args map[string]interface{}) error {
	hostName := "default"
	if args["--host"] != nil {
		hostName = args["--host"].(string)
	}

	dirname, err := ioutil.TempDir("/tmp", "orchard-")
	if err != nil {
		return fmt.Errorf("Error creating temporary directory: %s\n", err)
	}
	defer os.RemoveAll(dirname)
	socketPath := path.Join(dirname, "orchard.sock")

	p, err := MakeProxy(socketPath, hostName)
	if err != nil {
		return fmt.Errorf("Error starting proxy: %v\n", err)
	}

	go p.Start()
	defer p.Stop()

	if err := <-p.ErrorChannel; err != nil {
		return fmt.Errorf("Error starting proxy: %v\n", err)
	}

	if args["docker"] == true {
		err := CallDocker(args["COMMAND"].([]string), []string{"DOCKER_HOST=unix://" + socketPath})
		if err != nil {
			return fmt.Errorf("Docker exited with error")
		}
	} else {
		fmt.Fprintln(os.Stderr, "Started proxy at unix://"+socketPath)

		c := make(chan os.Signal)
		signal.Notify(c, syscall.SIGINT, syscall.SIGKILL)
		<-c

		fmt.Fprintln(os.Stderr, "\nStopping proxy")
	}

	return nil
}

func MakeProxy(socketPath string, hostName string) (*proxy.Proxy, error) {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		return nil, err
	}

	host, err := httpClient.GetHost(hostName)
	if err != nil {
		return nil, err
	}
	destination := host.IPAddress + ":4243"

	certData := []byte(host.ClientCert)
	keyData := []byte(host.ClientKey)
	config, err := tlsconfig.GetTLSConfig(certData, keyData)
	if err != nil {
		return nil, err
	}

	return proxy.New(
		func() (net.Listener, error) { return net.Listen("unix", socketPath) },
		func() (net.Conn, error) { return tls.Dial("tcp", destination, config) },
	), nil
}

func CallDocker(args []string, env []string) error {
	dockerPath := GetDockerPath()
	if dockerPath == "" {
		return errors.New("Can't find `docker` executable in $PATH.\nYou might need to install it: http://docs.docker.io/en/latest/installation/#installation-list")
	}

	cmd := exec.Command(dockerPath, args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func GetDockerPath() string {
	for _, dir := range strings.Split(os.Getenv("PATH"), ":") {
		dockerPath := path.Join(dir, "docker")
		_, err := os.Stat(dockerPath)
		if err == nil {
			return dockerPath
		}
	}
	return ""
}

func Hosts(args map[string]interface{}) error {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		return err
	}

	if args["create"] == true {
		host, err := httpClient.CreateHost(args["NAME"].(string))
		if err != nil {
			return err
		}
		fmt.Fprintf(os.Stderr, "Created %s with IP address %s\n", host.Name, host.IPAddress)
	} else if args["rm"] == true {
		err := httpClient.DeleteHost(args["NAME"].(string))
		if err != nil {
			return err
		}
		fmt.Fprintf(os.Stderr, "Removed %s\n", args["NAME"].(string))
	} else {
		hosts, err := httpClient.GetHosts()
		if err != nil {
			return err
		}

		writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
		fmt.Fprintln(writer, "NAME\tIP")
		for _, host := range hosts {
			fmt.Fprintf(writer, "%s\t%s\n", host.Name, host.IPAddress)
		}
		writer.Flush()
	}

	return nil
}
<commit_msg>Move 'hosts create' and 'hosts rm' to 'start' and 'stop'<commit_after>package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/orchardup/orchard/cli"
	"github.com/orchardup/orchard/proxy"
	"github.com/orchardup/orchard/tlsconfig"
	"github.com/orchardup/orchard/vendor/github.com/docopt/docopt.go"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"text/tabwriter"
)

func main() {
	usage := `Orchard.

Usage:
  orchard hosts
  orchard start [NAME]
  orchard stop [NAME]
  orchard [options] docker [COMMAND...]
  orchard [options] proxy

Options:
  -h --help               Show this screen.
  --version               Show version.
  -H NAME, --host=NAME    Name of host to connect to (instead of 'default')`

	args, err := docopt.Parse(usage, nil, true, "Orchard 2.0.0", true)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error parsing arguments: %s\n", err)
		return
	}

	var cmdErr error = nil

	if args["hosts"] == true {
		cmdErr = Hosts(args)
	} else if args["start"] == true {
		cmdErr = Start(args)
	} else if args["stop"] == true {
		cmdErr = Stop(args)
	} else if args["docker"] == true || args["proxy"] == true {
		cmdErr = Docker(args)
	}

	if cmdErr != nil {
		fmt.Fprintln(os.Stderr, cmdErr)
		os.Exit(1)
	}
}

func Docker(args map[string]interface{}) error {
	hostName := "default"
	if args["--host"] != nil {
		hostName = args["--host"].(string)
	}

	dirname, err := ioutil.TempDir("/tmp", "orchard-")
	if err != nil {
		return fmt.Errorf("Error creating temporary directory: %s\n", err)
	}
	defer os.RemoveAll(dirname)
	socketPath := path.Join(dirname, "orchard.sock")

	p, err := MakeProxy(socketPath, hostName)
	if err != nil {
		return fmt.Errorf("Error starting proxy: %v\n", err)
	}

	go p.Start()
	defer p.Stop()

	if err := <-p.ErrorChannel; err != nil {
		return fmt.Errorf("Error starting proxy: %v\n", err)
	}

	if args["docker"] == true {
		err := CallDocker(args["COMMAND"].([]string), []string{"DOCKER_HOST=unix://" + socketPath})
		if err != nil {
			return fmt.Errorf("Docker exited with error")
		}
	} else {
		fmt.Fprintln(os.Stderr, "Started proxy at unix://"+socketPath)

		c := make(chan os.Signal)
		signal.Notify(c, syscall.SIGINT, syscall.SIGKILL)
		<-c

		fmt.Fprintln(os.Stderr, "\nStopping proxy")
	}

	return nil
}

func MakeProxy(socketPath string, hostName string) (*proxy.Proxy, error) {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		return nil, err
	}

	host, err := httpClient.GetHost(hostName)
	if err != nil {
		return nil, err
	}
	destination := host.IPAddress + ":4243"

	certData := []byte(host.ClientCert)
	keyData := []byte(host.ClientKey)
	config, err := tlsconfig.GetTLSConfig(certData, keyData)
	if err != nil {
		return nil, err
	}

	return proxy.New(
		func() (net.Listener, error) { return net.Listen("unix", socketPath) },
		func() (net.Conn, error) { return tls.Dial("tcp", destination, config) },
	), nil
}

func CallDocker(args []string, env []string) error {
	dockerPath := GetDockerPath()
	if dockerPath == "" {
		return errors.New("Can't find `docker` executable in $PATH.\nYou might need to install it: http://docs.docker.io/en/latest/installation/#installation-list")
	}

	cmd := exec.Command(dockerPath, args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func GetDockerPath() string {
	for _, dir := range strings.Split(os.Getenv("PATH"), ":") {
		dockerPath := path.Join(dir, "docker")
		_, err := os.Stat(dockerPath)
		if err == nil {
			return dockerPath
		}
	}
	return ""
}

func Start(args map[string]interface{}) error {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		return err
	}

	hostName, humanName := GetHostName(args)
	humanName = strings.ToUpper(humanName[0:1]) + humanName[1:]

	host, err := httpClient.CreateHost(hostName)
	if err != nil {
		return err
	}
	fmt.Fprintf(os.Stderr, "%s running at %s\n", humanName, host.IPAddress)

	return nil
}

func Stop(args map[string]interface{}) error {
	hostName, humanName := GetHostName(args)

	var confirm string
	fmt.Printf("Going to stop and delete %s. All data on it will be lost.\n", humanName)
	fmt.Print("Are you sure you're ready? [yN] ")
	fmt.Scanln(&confirm)

	if strings.ToLower(confirm) != "y" {
		return nil
	}

	httpClient, err := authenticator.Authenticate()
	if err != nil {
		return err
	}

	err = httpClient.DeleteHost(hostName)
	if err != nil {
		return err
	}
	fmt.Fprintf(os.Stderr, "Stopped %s\n", humanName)

	return nil
}

func GetHostName(args map[string]interface{}) (string, string) {
	hostName := "default"
	humanName := "default host"

	if args["NAME"] != nil {
		hostName = args["NAME"].(string)
		humanName = fmt.Sprintf("host '%s'", hostName)
	}

	return hostName, humanName
}

func Hosts(args map[string]interface{}) error {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		return err
	}

	hosts, err := httpClient.GetHosts()
	if err != nil {
		return err
	}

	writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
	fmt.Fprintln(writer, "NAME\tIP")
	for _, host := range hosts {
		fmt.Fprintf(writer, "%s\t%s\n", host.Name, host.IPAddress)
	}
	writer.Flush()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"strings"

	"github.com/chzyer/readline"
)

//var bracketStack = 0
var braceStack = 0

func main() {
	err := setUp()
	if err != nil {
		log.Fatal("setup error: %v", err)
		return
	}
	defer cleanUp()
	mainloop()
}

func mainloop() {
	// init current buffer
	curBuffer := increBuffer

	rl, err := readline.New(">>> ")
	if err != nil {
		panic(err)
	}
	defer rl.Close()
	for {
		//str, _ := reader.ReadString('\n')
		str, err := rl.Readline()
		if err != nil {
			log.Fatal("read line error: %v", err)
		}

		if strings.Trim(str, " \n") == "quit" {
			break
		}
		str += "\n"
		switch statementType(str) {
		case stImport: // import
			importBuffer.WriteString(str)
		/*
			case ST_TYPE_LEFT_BRACKET:
				curBuffer.WriteString(str)
				bracketStack += 1
			case ST_TYPE_RIGHT_BRACKET:
				curBuffer.WriteString(str)
				bracketStack -= 1
				if bracketStack <= 0 {
					bracketStack = 0
					run()
				}
		*/
		case stLeftBrace: // '{' found at the end
			curBuffer.WriteString(str)
			braceStack++
		case stRightBrace: // '}' found
			curBuffer.WriteString(str)
			braceStack--
			if braceStack <= 0 {
				braceStack = 0
				curBuffer = increBuffer
				execute()
			}
		case stAssign:
			curBuffer.WriteString(str)
		case stFunc:
			// begin func
			braceStack++
			curBuffer = funcBuffer
			curBuffer.WriteString(str)
		case stEmptyStr:
			// do nothing
		default:
			curBuffer.WriteString(str)
			if braceStack <= 0 {
				execute()
			}
		}
	}

}
<commit_msg>add version and startup hint<commit_after>package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/chzyer/readline"
)

//var bracketStack = 0
var braceStack = 0

func main() {
	err := setUp()
	if err != nil {
		log.Fatal("setup error: %v", err)
		return
	}
	defer cleanUp()
	mainloop()
}

func mainloop() {
	// init current buffer
	curBuffer := increBuffer

	fmt.Println("Goshell v1.0 (2016-9-1 17:28:02)")
	fmt.Println(`Type "quit" to exit`)
	rl, err := readline.New(">>> ")
	if err != nil {
		panic(err)
	}
	defer rl.Close()
	for {
		//str, _ := reader.ReadString('\n')
		str, err := rl.Readline()
		if err != nil {
			log.Fatal("read line error: %v", err)
		}

		if strings.Trim(str, " \n") == "quit" {
			break
		}
		str += "\n"
		switch statementType(str) {
		case stImport: // import
			importBuffer.WriteString(str)
		/*
			case ST_TYPE_LEFT_BRACKET:
				curBuffer.WriteString(str)
				bracketStack += 1
			case ST_TYPE_RIGHT_BRACKET:
				curBuffer.WriteString(str)
				bracketStack -= 1
				if bracketStack <= 0 {
					bracketStack = 0
					run()
				}
		*/
		case stLeftBrace: // '{' found at the end
			curBuffer.WriteString(str)
			braceStack++
		case stRightBrace: // '}' found
			curBuffer.WriteString(str)
			braceStack--
			if braceStack <= 0 {
				braceStack = 0
				curBuffer = increBuffer
				execute()
			}
		case stAssign:
			curBuffer.WriteString(str)
		case stFunc:
			// begin func
			braceStack++
			curBuffer = funcBuffer
			curBuffer.WriteString(str)
		case stEmptyStr:
			// do nothing
		default:
			curBuffer.WriteString(str)
			if braceStack <= 0 {
				execute()
			}
		}
	}

}
<|endoftext|>