<commit_before>package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"github.com/minotar/minecraft"
	"github.com/op/go-logging"
	"net/http"
	_ "net/http/pprof"
	"os"
	"runtime/debug"
	"strconv"
	"time"
)

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

	SkinCache

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

	MinotarVersion = "2.2"
)

var (
	config = &Configuration{}
	cache  = MakeCache()
)

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(*mcSkin) 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

		skin := fetchSkin(username)
		var err error

		timeFetch := time.Now()

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

		w.Header().Add("Content-Type", "image/png")
		w.Header().Add("X-Requested", "processed")
		w.Header().Add("X-Skin-Hash", skin.Hash)
		var timeout uint
		if ok {
			w.Header().Add("X-Result", "ok")
			timeout = TimeoutActualSkin
		} else {
			w.Header().Add("X-Result", "failed")
			timeout = TimeoutFailedFetch
		}

		timing := fmt.Sprintf("%d+%d+%d=%dms", timeBetween(timeReqStart, timeFetch), timeBetween(timeFetch, timeProcess), timeBetween(timeProcess, timeResize), timeBetween(timeReqStart, timeResize))

		w.Header().Add("X-Timing", timing)
		addCacheTimeoutHeader(w, timeout)
		skin.WritePNG(w)

		log.Info("Serving skin for " + username + " (" + timing + ") md5: " + skin.Hash)
	}
}
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")

	skin.WritePNG(w)
}
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) *mcSkin {
	if cache.has(username) {
		return &mcSkin{Processed: nil, Skin: cache.pull(username)}
	}

	skin, err := minecraft.FetchSkinFromUrl(username)
	if err != nil {
		log.Error("Failed to get skin for " + username + " from Mojang (" + err.Error() + ")")
		skin, _ = minecraft.FetchSkinForChar()
	}

	cache.add(username, skin)
	return &mcSkin{Processed: nil, Skin: 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
	*/
}

var log = logging.MustGetLogger("imgd")
var format = "[%{time:15:04:05.000000}] %{level:.4s} %{message}"

func main() {
	err := config.load()
	if err != nil {
		fmt.Printf("Error loading config: %s\n", err)
		return
	}

	logBackend := logging.NewLogBackend(os.Stdout, "", 0)
	logging.SetBackend(logBackend)
	logging.SetFormatter(logging.MustStringFormatter(format))

	debug.SetGCPercent(10)

	avatarPage := fetchImageProcessThen(func(skin *mcSkin) error {
		return skin.GetHead()
	})
	helmPage := fetchImageProcessThen(func(skin *mcSkin) error {
		return skin.GetHelm()
	})
	bodyPage := fetchImageProcessThen(func(skin *mcSkin) error {
		return skin.GetBody()
	})
	bustPage := fetchImageProcessThen(func(skin *mcSkin) error {
		return skin.GetBust()
	})

	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("/body/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", bodyPage)
	r.HandleFunc("/body/{username:"+minecraft.ValidUsernameRegex+"}/{size:[0-9]+}{extension:(.png)?}", bodyPage)

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

	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/")
		w.WriteHeader(302)
	})

	http.Handle("/", r)
	err = http.ListenAndServe(config.Address, nil)
	log.Critical(err.Error())
}
<commit_msg>Following convention<commit_after>package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"github.com/minotar/minecraft"
	"github.com/op/go-logging"
	"net/http"
	_ "net/http/pprof"
	"os"
	"runtime/debug"
	"strconv"
	"time"
)

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

	SkinCache

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

	MinotarVersion = "2.2"
)

var (
	config = &Configuration{}
	cache  = MakeCache()
)

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(*mcSkin) 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

		skin := fetchSkin(username)
		var err error

		timeFetch := time.Now()

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

		w.Header().Add("Content-Type", "image/png")
		w.Header().Add("X-Requested", "processed")
		w.Header().Add("X-Skin-Hash", skin.Hash)
		var timeout uint
		if ok {
			w.Header().Add("X-Result", "ok")
			timeout = TimeoutActualSkin
		} else {
			w.Header().Add("X-Result", "failed")
			timeout = TimeoutFailedFetch
		}

		timing := fmt.Sprintf("%d+%d+%d=%dms", timeBetween(timeReqStart, timeFetch), timeBetween(timeFetch, timeProcess), timeBetween(timeProcess, timeResize), timeBetween(timeReqStart, timeResize))

		w.Header().Add("X-Timing", timing)
		addCacheTimeoutHeader(w, timeout)
		skin.WritePNG(w)

		log.Info("Serving skin for " + username + " (" + timing + ") md5: " + skin.Hash)
	}
}
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")

	skin.WritePNG(w)
}
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) *mcSkin {
	if cache.has(username) {
		return &mcSkin{Processed: nil, Skin: cache.pull(username)}
	}

	skin, err := minecraft.FetchSkinFromUrl(username)
	if err != nil {
		log.Error("Failed to get skin for " + username + " from Mojang (" + err.Error() + ")")
		skin, _ = minecraft.FetchSkinForChar()
	}

	cache.add(username, skin)
	return &mcSkin{Processed: nil, Skin: 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
	*/
}

var log = logging.MustGetLogger("imgd")
var format = "[%{time:15:04:05.000000}] %{level:.4s} %{message}"

func main() {
	err := config.load()
	if err != nil {
		fmt.Printf("Error loading config: %s\n", err)
		return
	}

	logBackend := logging.NewLogBackend(os.Stdout, "", 0)
	logging.SetBackend(logBackend)
	logging.SetFormatter(logging.MustStringFormatter(format))

	debug.SetGCPercent(10)

	avatarPage := fetchImageProcessThen(func(skin *mcSkin) error {
		return skin.GetHead()
	})
	helmPage := fetchImageProcessThen(func(skin *mcSkin) error {
		return skin.GetHelm()
	})
	bodyPage := fetchImageProcessThen(func(skin *mcSkin) error {
		return skin.GetBody()
	})
	bustPage := fetchImageProcessThen(func(skin *mcSkin) error {
		return skin.GetBust()
	})

	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("/body/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", bodyPage)
	r.HandleFunc("/body/{username:"+minecraft.ValidUsernameRegex+"}/{size:[0-9]+}{extension:(.png)?}", bodyPage)

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

	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.WriteHeader(302)
		w.Header().Set("Location", "https://minotar.net/")
	})

	http.Handle("/", r)
	err = http.ListenAndServe(config.Address, nil)
	log.Critical(err.Error())
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strings"
	"syscall"
	"time"

	"github.com/cenkalti/yap/datetime"
	"github.com/cenkalti/yap/task"
	"github.com/codegangsta/cli"
	"github.com/olekukonko/tablewriter"
)

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

func main() {
	app := cli.NewApp()
	app.Name = "yap"
	app.Usage = "todo app"
	app.Authors = []cli.Author{
		{
			Name:  "Cenk Altı",
			Email: "cenkalti@gmail.com",
		},
	}
	app.Before = func(c *cli.Context) (err error) {
		fd, err := syscall.Open(task.Home, 0, 0)
		if err != nil {
			log.Fatal(err)
		}
		err = syscall.Flock(fd, syscall.LOCK_EX|syscall.LOCK_NB)
		if err != nil {
			log.Fatal(err)
		}
		return
	}
	app.Action = cmdAdd // Default subcommand is "add".
	app.Commands = []cli.Command{
		{
			Name:    "add",
			Aliases: []string{"a"},
			Usage:   "add new task",
			Action:  cmdAdd,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "due",
					Usage: "due date. complete task before this date",
				},
				cli.StringFlag{
					Name:  "wait",
					Usage: "wait date. task will be hidden until this date",
				},
			},
		},
		{
			Name:    "list",
			Aliases: []string{"l"},
			Usage:   "list tasks",
			Subcommands: []cli.Command{
				{
					Name:    "pending",
					Aliases: []string{"p"},
					Usage:   "list pending tasks",
					Action:  cmdListPending,
				},
				{
					Name:    "completed",
					Aliases: []string{"c"},
					Usage:   "list completed tasks",
					Action:  cmdListCompleted,
				},
				{
					Name:    "waiting",
					Aliases: []string{"w"},
					Usage:   "list waiting tasks",
					Action:  cmdListWaiting,
				},
			},
		},
		{
			Name:    "complete",
			Aliases: []string{"c"},
			Usage:   "complete pending task",
			Action:  cmdComplete,
		},
		{
			Name:    "continue",
			Aliases: []string{"con"},
			Usage:   "continue completed task",
			Action:  cmdContinue,
		},
	}
	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}
}

func cmdAdd(c *cli.Context) {
	// Since "add" is the default subcommand, we need to check if called with no args and show help.
	if len(c.Args()) == 0 {
		cli.ShowAppHelp(c)
		return
	}
	title := strings.Join(c.Args(), " ")
	dueDate := parseDateTime(c.String("due"))
	waitDate := parseDateTime(c.String("wait"))
	id, err := task.Add(title, dueDate, waitDate)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("id:", id)
}

func parseDateTime(s string) *datetime.DateTime {
	if s == "" {
		return nil
	}
	dt, err := datetime.Parse(s)
	if err != nil {
		log.Fatal(err)
	}
	return &dt
}

func formatDateTime(dt *datetime.DateTime) string {
	if dt == nil {
		return ""
	}
	return dt.String()
}

func formatDate(t *time.Time) string {
	if t == nil {
		return ""
	}
	return datetime.New(*t).String()
}

func cmdListPending(c *cli.Context) {
	tasks, err := task.ListPending()
	if err != nil {
		log.Fatal(err)
	}
	table := newTable("ID", "Title", "Due date")
	for _, t := range tasks {
		table.Append([]string{
			task.FormatID(t.ID),
			t.Title,
			formatDateTime(t.DueDate),
		})
	}
	table.Render()
}

func cmdListCompleted(c *cli.Context) {
	tasks, err := task.ListCompleted()
	if err != nil {
		log.Fatal(err)
	}
	table := newTable("ID", "Title", "Completed at")
	for _, t := range tasks {
		table.Append([]string{
			task.FormatID(t.ID),
			t.Title,
			formatDate(t.CompletedAt),
		})
	}
	table.Render()
}

func cmdListWaiting(c *cli.Context) {
	tasks, err := task.ListWaiting()
	if err != nil {
		log.Fatal(err)
	}
	table := newTable("ID", "Title", "Wait date", "Due date")
	for _, t := range tasks {
		table.Append([]string{
			task.FormatID(t.ID),
			t.Title,
			formatDateTime(t.WaitDate),
			formatDateTime(t.DueDate),
		})
	}
	table.Render()
}

func cmdComplete(c *cli.Context) {
	ids, err := parseIDs(c.Args())
	if err != nil {
		log.Fatal(err)
	}
	err = task.Complete(ids)
	if err != nil {
		log.Fatal(err)
	}
}

func cmdContinue(c *cli.Context) {
	ids, err := parseIDs(c.Args())
	if err != nil {
		log.Fatal(err)
	}
	err = task.Continue(ids)
	if err != nil {
		log.Fatal(err)
	}
}

func parseIDs(args []string) ([]uint16, error) {
	ids := make([]uint16, 0, len(args))
	for _, arg := range args {
		id, err := task.ParseID(arg)
		if err != nil {
			return nil, err
		}
		ids = append(ids, id)
	}
	return ids, nil
}

func newTable(fields ...string) *tablewriter.Table {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetBorder(false)
	table.SetColumnSeparator("")
	table.SetAutoFormatHeaders(false)
	table.SetHeader(fields)
	return table
}
<commit_msg>hide help command<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"strings"
	"syscall"
	"time"

	"github.com/cenkalti/yap/datetime"
	"github.com/cenkalti/yap/task"
	"github.com/codegangsta/cli"
	"github.com/olekukonko/tablewriter"
)

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

func main() {
	app := cli.NewApp()
	app.Name = "yap"
	app.Usage = "todo app"
	app.HideHelp = true
	app.Authors = []cli.Author{
		{
			Name:  "Cenk Altı",
			Email: "cenkalti@gmail.com",
		},
	}
	app.Before = func(c *cli.Context) (err error) {
		fd, err := syscall.Open(task.Home, 0, 0)
		if err != nil {
			log.Fatal(err)
		}
		err = syscall.Flock(fd, syscall.LOCK_EX|syscall.LOCK_NB)
		if err != nil {
			log.Fatal(err)
		}
		return
	}
	app.Action = cmdAdd // Default subcommand is "add".
	app.Commands = []cli.Command{
		{
			Name:    "add",
			Aliases: []string{"a"},
			Usage:   "add new task",
			Action:  cmdAdd,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "due",
					Usage: "due date. complete task before this date",
				},
				cli.StringFlag{
					Name:  "wait",
					Usage: "wait date. task will be hidden until this date",
				},
			},
		},
		{
			Name:    "list",
			Aliases: []string{"l"},
			Usage:   "list tasks",
			Subcommands: []cli.Command{
				{
					Name:    "pending",
					Aliases: []string{"p"},
					Usage:   "list pending tasks",
					Action:  cmdListPending,
				},
				{
					Name:    "completed",
					Aliases: []string{"c"},
					Usage:   "list completed tasks",
					Action:  cmdListCompleted,
				},
				{
					Name:    "waiting",
					Aliases: []string{"w"},
					Usage:   "list waiting tasks",
					Action:  cmdListWaiting,
				},
			},
		},
		{
			Name:    "complete",
			Aliases: []string{"c"},
			Usage:   "complete pending task",
			Action:  cmdComplete,
		},
		{
			Name:    "continue",
			Aliases: []string{"con"},
			Usage:   "continue completed task",
			Action:  cmdContinue,
		},
	}
	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}
}

func cmdAdd(c *cli.Context) {
	// Since "add" is the default subcommand, we need to check if called with no args and show help.
	if len(c.Args()) == 0 {
		cli.ShowAppHelp(c)
		return
	}
	title := strings.Join(c.Args(), " ")
	dueDate := parseDateTime(c.String("due"))
	waitDate := parseDateTime(c.String("wait"))
	id, err := task.Add(title, dueDate, waitDate)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("id:", id)
}

func parseDateTime(s string) *datetime.DateTime {
	if s == "" {
		return nil
	}
	dt, err := datetime.Parse(s)
	if err != nil {
		log.Fatal(err)
	}
	return &dt
}

func formatDateTime(dt *datetime.DateTime) string {
	if dt == nil {
		return ""
	}
	return dt.String()
}

func formatDate(t *time.Time) string {
	if t == nil {
		return ""
	}
	return datetime.New(*t).String()
}

func cmdListPending(c *cli.Context) {
	tasks, err := task.ListPending()
	if err != nil {
		log.Fatal(err)
	}
	table := newTable("ID", "Title", "Due date")
	for _, t := range tasks {
		table.Append([]string{
			task.FormatID(t.ID),
			t.Title,
			formatDateTime(t.DueDate),
		})
	}
	table.Render()
}

func cmdListCompleted(c *cli.Context) {
	tasks, err := task.ListCompleted()
	if err != nil {
		log.Fatal(err)
	}
	table := newTable("ID", "Title", "Completed at")
	for _, t := range tasks {
		table.Append([]string{
			task.FormatID(t.ID),
			t.Title,
			formatDate(t.CompletedAt),
		})
	}
	table.Render()
}

func cmdListWaiting(c *cli.Context) {
	tasks, err := task.ListWaiting()
	if err != nil {
		log.Fatal(err)
	}
	table := newTable("ID", "Title", "Wait date", "Due date")
	for _, t := range tasks {
		table.Append([]string{
			task.FormatID(t.ID),
			t.Title,
			formatDateTime(t.WaitDate),
			formatDateTime(t.DueDate),
		})
	}
	table.Render()
}

func cmdComplete(c *cli.Context) {
	ids, err := parseIDs(c.Args())
	if err != nil {
		log.Fatal(err)
	}
	err = task.Complete(ids)
	if err != nil {
		log.Fatal(err)
	}
}

func cmdContinue(c *cli.Context) {
	ids, err := parseIDs(c.Args())
	if err != nil {
		log.Fatal(err)
	}
	err = task.Continue(ids)
	if err != nil {
		log.Fatal(err)
	}
}

func parseIDs(args []string) ([]uint16, error) {
	ids := make([]uint16, 0, len(args))
	for _, arg := range args {
		id, err := task.ParseID(arg)
		if err != nil {
			return nil, err
		}
		ids = append(ids, id)
	}
	return ids, nil
}

func newTable(fields ...string) *tablewriter.Table {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetBorder(false)
	table.SetColumnSeparator("")
	table.SetAutoFormatHeaders(false)
	table.SetHeader(fields)
	return table
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
)

const version = "0.0.2"

// Metric holds the details of a metric
type Metric struct {
	Name      string            `json:"name"`
	Timestamp int               `json:"timestamp"`
	Value     float64           `json:"value"`
	Tags      map[string]string `json:"tags"`
}

func main() {
	host := flag.String("host", "localhost:4242", "The host:port to connect to. Defaults to 'localhost:4242'")
	ver := flag.Bool("version", false, "Print the version number and exit")
	flag.Parse()

	if *ver {
		fmt.Printf("%v version: %s\n", os.Args[0], version)
		os.Exit(0)
	}

	inputFilename := flag.Arg(0)

	var input io.Reader

	if len(inputFilename) > 0 {
		var err error

		input, err = os.Open(inputFilename)

		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	} else {
		input = os.Stdin
	}

	dec := json.NewDecoder(input)
	conn, err := net.Dial("tcp", *host)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer conn.Close()

	for {
		var m Metric
		if err := dec.Decode(&m); err == io.EOF {
			break
		} else if err != nil {
			fmt.Println(err)
			continue
		}

		o := fmt.Sprintf("put %s %d %f", m.Name, m.Timestamp, m.Value)

		for name, value := range m.Tags {
			//empty tags will generate an error on ingest
			if value != "" {
				o += fmt.Sprintf(" %s=%s", name, value)
			}
		}

		o += "\n"

		fmt.Fprint(conn, o)
	}
}
<commit_msg>Incremented version number<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
)

const version = "0.0.3"

// Metric holds the details of a metric
type Metric struct {
	Name      string            `json:"name"`
	Timestamp int               `json:"timestamp"`
	Value     float64           `json:"value"`
	Tags      map[string]string `json:"tags"`
}

func main() {
	host := flag.String("host", "localhost:4242", "The host:port to connect to. Defaults to 'localhost:4242'")
	ver := flag.Bool("version", false, "Print the version number and exit")
	flag.Parse()

	if *ver {
		fmt.Printf("%v version: %s\n", os.Args[0], version)
		os.Exit(0)
	}

	inputFilename := flag.Arg(0)

	var input io.Reader

	if len(inputFilename) > 0 {
		var err error

		input, err = os.Open(inputFilename)

		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	} else {
		input = os.Stdin
	}

	dec := json.NewDecoder(input)
	conn, err := net.Dial("tcp", *host)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer conn.Close()

	for {
		var m Metric
		if err := dec.Decode(&m); err == io.EOF {
			break
		} else if err != nil {
			fmt.Println(err)
			continue
		}

		o := fmt.Sprintf("put %s %d %f", m.Name, m.Timestamp, m.Value)

		for name, value := range m.Tags {
			//empty tags will generate an error on ingest
			if value != "" {
				o += fmt.Sprintf(" %s=%s", name, value)
			}
		}

		o += "\n"

		fmt.Fprint(conn, o)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/bahadley/esp/log"
	"github.com/bahadley/esp/operator"
	"github.com/bahadley/esp/stream"
	"github.com/bahadley/esp/sync"
)

const (
	envMaster = "ESP_MASTER"

	masterFlag = "NO"
)

var (
	master bool = true
)

func main() {
	log.Info.Println("Starting up ...")

	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)

	go func() {
		// Block waiting for signal.
		<-c
		log.Info.Println("Shutting down ...")
		os.Exit(0)
	}()

	if master {
		go stream.Egress()
		go sync.Ingress()
		go operator.Ingest()
		stream.Ingress(true)
	} else {
		go sync.Egress()
		stream.Ingress(false)
	}
}

func init() {
	m := os.Getenv(envMaster)
	if len(m) == 0 && strings.ToUpper(m) == masterFlag {
		master = false
	}
}
<commit_msg>Fixed logic in init()<commit_after>package main

import (
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/bahadley/esp/log"
	"github.com/bahadley/esp/operator"
	"github.com/bahadley/esp/stream"
	"github.com/bahadley/esp/sync"
)

const (
	envMaster = "ESP_MASTER"

	masterFlag = "NO"
)

var (
	master bool = true
)

func main() {
	log.Info.Println("Starting up ...")

	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)

	go func() {
		// Block waiting for signal.
		<-c
		log.Info.Println("Shutting down ...")
		os.Exit(0)
	}()

	if master {
		go stream.Egress()
		go sync.Ingress()
		go operator.Ingest()
		stream.Ingress(true)
	} else {
		go sync.Egress()
		stream.Ingress(false)
	}
}

func init() {
	m := os.Getenv(envMaster)
	if len(m) != 0 && strings.ToUpper(m) == masterFlag {
		master = false
	}
}
<|endoftext|>


<commit_before>// This is free and unencumbered software released into the public
// domain.  For more information, see <http://unlicense.org> or the
// accompanying UNLICENSE file.

package main

import (
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/nelsam/gxui"
	"github.com/nelsam/gxui/drivers/gl"
	"github.com/nelsam/gxui/math"
	"github.com/nelsam/gxui/themes/basic"
	"github.com/nelsam/gxui/themes/dark"
	"github.com/nelsam/vidar/commander"
	"github.com/nelsam/vidar/commands"
	"github.com/nelsam/vidar/settings"
	"github.com/tmc/fonts"

	"github.com/nelsam/vidar/controller"
	"github.com/nelsam/vidar/editor"
	"github.com/nelsam/vidar/navigator"
	"github.com/spf13/cobra"
)

var (
	background = gxui.Gray10

	workingDir string
	cmd        *cobra.Command
	files      []string
)

func init() {
	cmd = &cobra.Command{
		Use:   "vidar [files...]",
		Short: "An experimental Go editor",
		Long: "An editor for Go code, still in its infancy.  " +
			"Basic editing of Go code is mostly complete, but " +
			"panics still happen and can result in the loss of " +
			"unsaved work.",
		Run: func(cmd *cobra.Command, args []string) {
			files = args
			gl.StartDriver(uiMain)
		},
	}
}

func main() {
	cmd.Execute()
}

func font(driver gxui.Driver) gxui.Font {
	desiredFonts := settings.DesiredFonts()
	if len(desiredFonts) == 0 {
		return nil
	}
	fontReader, err := fonts.Load(desiredFonts...)
	if err != nil {
		log.Printf("Error searching for fonts %v: %s", desiredFonts, err)
		return nil
	}
	if closer, ok := fontReader.(io.Closer); ok {
		defer closer.Close()
	}
	fontBytes, err := ioutil.ReadAll(fontReader)
	if err != nil {
		log.Printf("Failed to read font file: %s", err)
		return nil
	}
	font, err := driver.CreateFont(fontBytes, 12)
	if err != nil {
		log.Printf("Could not parse font: %s", err)
		return nil
	}
	return font
}

func uiMain(driver gxui.Driver) {
	theme := dark.CreateTheme(driver).(*basic.Theme)
	font := font(driver)
	if font == nil {
		font = theme.DefaultMonospaceFont()
	}
	theme.SetDefaultMonospaceFont(font)
	theme.SetDefaultFont(font)
	theme.WindowBackground = background

	// TODO: figure out a better way to get this resolution
	window := theme.CreateWindow(1600, 800, "Vidar - GXUI Go Editor")
	controller := controller.New(driver, theme)

	nav := navigator.New(driver, theme, controller)
	controller.SetNavigator(nav)

	editor := editor.New(driver, window, theme, theme.DefaultMonospaceFont())
	controller.SetEditor(editor)

	projTree := navigator.NewProjectTree(driver, window, theme)
	projects := navigator.NewProjectsPane(driver, theme, projTree.Frame())

	nav.Add(projects)
	nav.Add(projTree)

	nav.Resize(window.Size().H)
	window.OnResize(func() {
		nav.Resize(window.Size().H)
	})

	commander := commander.New(driver, theme, controller)

	// TODO: Check the system's DPI settings for this value
	window.SetScale(1)

	window.AddChild(commander)

	for _, command := range commands.Commands(driver, theme, projTree.Frame()) {
		commander.Map(command, settings.Bindings(command.Name())...)
	}

	window.OnKeyDown(func(event gxui.KeyboardEvent) {
		if (event.Modifier.Control() || event.Modifier.Super()) && event.Key == gxui.KeyQ {
			os.Exit(0)
		}
		if event.Modifier == 0 && event.Key == gxui.KeyF11 {
			window.SetFullscreen(!window.Fullscreen())
		}
		if window.Focus() == nil {
			commander.KeyDown(event)
		}
	})
	window.OnKeyUp(func(event gxui.KeyboardEvent) {
		if window.Focus() == nil {
			commander.KeyPress(event)
		}
	})

	workingDir, err := os.Getwd()
	if err != nil {
		log.Printf("Failed to read working directory: %s", err)
		workingDir = os.Getenv("HOME")
	}
	for _, file := range files {
		filepath := filepath.Join(workingDir, file)
		commander.Controller().Editor().Open(filepath, token.Position{})
	}

	window.OnClose(driver.Terminate)
	window.SetPadding(math.Spacing{L: 10, T: 10, R: 10, B: 10})
}
<commit_msg>add open files with full path<commit_after>// This is free and unencumbered software released into the public
// domain.  For more information, see <http://unlicense.org> or the
// accompanying UNLICENSE file.

package main

import (
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/nelsam/gxui"
	"github.com/nelsam/gxui/drivers/gl"
	"github.com/nelsam/gxui/math"
	"github.com/nelsam/gxui/themes/basic"
	"github.com/nelsam/gxui/themes/dark"
	"github.com/nelsam/vidar/commander"
	"github.com/nelsam/vidar/commands"
	"github.com/nelsam/vidar/settings"
	"github.com/tmc/fonts"

	"github.com/nelsam/vidar/controller"
	"github.com/nelsam/vidar/editor"
	"github.com/nelsam/vidar/navigator"
	"github.com/spf13/cobra"
)

var (
	background = gxui.Gray10

	workingDir string
	cmd        *cobra.Command
	files      []string
)

func init() {
	cmd = &cobra.Command{
		Use:   "vidar [files...]",
		Short: "An experimental Go editor",
		Long: "An editor for Go code, still in its infancy.  " +
			"Basic editing of Go code is mostly complete, but " +
			"panics still happen and can result in the loss of " +
			"unsaved work.",
		Run: func(cmd *cobra.Command, args []string) {
			files = args
			gl.StartDriver(uiMain)
		},
	}
}

func main() {
	cmd.Execute()
}

func font(driver gxui.Driver) gxui.Font {
	desiredFonts := settings.DesiredFonts()
	if len(desiredFonts) == 0 {
		return nil
	}
	fontReader, err := fonts.Load(desiredFonts...)
	if err != nil {
		log.Printf("Error searching for fonts %v: %s", desiredFonts, err)
		return nil
	}
	if closer, ok := fontReader.(io.Closer); ok {
		defer closer.Close()
	}
	fontBytes, err := ioutil.ReadAll(fontReader)
	if err != nil {
		log.Printf("Failed to read font file: %s", err)
		return nil
	}
	font, err := driver.CreateFont(fontBytes, 12)
	if err != nil {
		log.Printf("Could not parse font: %s", err)
		return nil
	}
	return font
}

func uiMain(driver gxui.Driver) {
	theme := dark.CreateTheme(driver).(*basic.Theme)
	font := font(driver)
	if font == nil {
		font = theme.DefaultMonospaceFont()
	}
	theme.SetDefaultMonospaceFont(font)
	theme.SetDefaultFont(font)
	theme.WindowBackground = background

	// TODO: figure out a better way to get this resolution
	window := theme.CreateWindow(1600, 800, "Vidar - GXUI Go Editor")
	controller := controller.New(driver, theme)

	nav := navigator.New(driver, theme, controller)
	controller.SetNavigator(nav)

	editor := editor.New(driver, window, theme, theme.DefaultMonospaceFont())
	controller.SetEditor(editor)

	projTree := navigator.NewProjectTree(driver, window, theme)
	projects := navigator.NewProjectsPane(driver, theme, projTree.Frame())

	nav.Add(projects)
	nav.Add(projTree)

	nav.Resize(window.Size().H)
	window.OnResize(func() {
		nav.Resize(window.Size().H)
	})

	commander := commander.New(driver, theme, controller)

	// TODO: Check the system's DPI settings for this value
	window.SetScale(1)

	window.AddChild(commander)

	for _, command := range commands.Commands(driver, theme, projTree.Frame()) {
		commander.Map(command, settings.Bindings(command.Name())...)
	}

	window.OnKeyDown(func(event gxui.KeyboardEvent) {
		if (event.Modifier.Control() || event.Modifier.Super()) && event.Key == gxui.KeyQ {
			os.Exit(0)
		}
		if event.Modifier == 0 && event.Key == gxui.KeyF11 {
			window.SetFullscreen(!window.Fullscreen())
		}
		if window.Focus() == nil {
			commander.KeyDown(event)
		}
	})
	window.OnKeyUp(func(event gxui.KeyboardEvent) {
		if window.Focus() == nil {
			commander.KeyPress(event)
		}
	})

	for _, file := range files {
		filepath, err := filepath.Abs(file)
		if err != nil {
			log.Printf("Failed to get path: %s", err)
		}
		commander.Controller().Editor().Open(filepath, token.Position{})
	}

	window.OnClose(driver.Terminate)
	window.SetPadding(math.Spacing{L: 10, T: 10, R: 10, B: 10})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/facebookgo/grace/gracehttp"
	"github.com/gin-gonic/gin"
	"net/http"
	"runtime"

	"github.com/techjanitor/pram-post/config"
	c "github.com/techjanitor/pram-post/controllers"
	m "github.com/techjanitor/pram-post/middleware"
	u "github.com/techjanitor/pram-post/utils"
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Set up DB connection
	u.NewDb()

	// Set up Redis connection
	u.NewRedisCache()

	// Get limits and stuff from database
	u.GetDatabaseSettings()

	// Print out config
	config.Print()

}

func main() {
	r := gin.Default()

	// Adds CORS headers
	r.Use(m.CORS())
	// Checks for antispam cookie
	r.Use(m.GetAntiSpamCookie())

	r.POST("/thread/new", c.ThreadController)
	r.POST("/thread/reply", c.ReplyController)
	r.POST("/tag/new", c.NewTagController)
	r.POST("/tag/add", c.AddTagController)
	r.NoRoute(c.ErrorController)

	s := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", config.Settings.General.Address, config.Settings.General.Port),
		Handler: r,
	}

	gracehttp.Serve(s)

}
<commit_msg>disable antispam cookie<commit_after>package main

import (
	"fmt"
	"github.com/facebookgo/grace/gracehttp"
	"github.com/gin-gonic/gin"
	"net/http"
	"runtime"

	"github.com/techjanitor/pram-post/config"
	c "github.com/techjanitor/pram-post/controllers"
	m "github.com/techjanitor/pram-post/middleware"
	u "github.com/techjanitor/pram-post/utils"
)

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Set up DB connection
	u.NewDb()

	// Set up Redis connection
	u.NewRedisCache()

	// Get limits and stuff from database
	u.GetDatabaseSettings()

	// Print out config
	config.Print()

}

func main() {
	r := gin.Default()

	// Adds CORS headers
	r.Use(m.CORS())
	// Checks for antispam cookie
	//r.Use(m.GetAntiSpamCookie())

	r.POST("/thread/new", c.ThreadController)
	r.POST("/thread/reply", c.ReplyController)
	r.POST("/tag/new", c.NewTagController)
	r.POST("/tag/add", c.AddTagController)
	r.NoRoute(c.ErrorController)

	s := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", config.Settings.General.Address, config.Settings.General.Port),
		Handler: r,
	}

	gracehttp.Serve(s)

}
<|endoftext|>


<commit_before>package main

import (
  "os"
  "fmt"
  "time"
  "strings"
  "net/http"
  "io/ioutil"
  "regexp"
  "encoding/json"
  "gopkg.in/mgo.v2"
  "gopkg.in/mgo.v2/bson"
  "github.com/gorilla/mux"
)

/**
 *  Shuttle Tracker
 *   Auto Updater - send request to iTrak API,
 *                  get updated shuttle info,
 *                  store updated records in db
 */
type VehicleUpdate struct {
  Id          bson.ObjectId                 `bson:"_id,omitempty"`
  VehicleId   string     `json:"vehicleId"   bson:"vehicleId,omitempty"`
  Lat         string     `json:"lat"         bson:"lat"`
  Lng         string     `json:"lng"         bson:"lng"`
  Heading     string     `json:"heading"     bson:"heading"`
  Speed       string     `json:"speed"       bson:"speed"`
  Lock        string     `json:"lock"        bson:"lock"`
  Time        string     `json:"time"        bson:"time"`
  Date        string     `json:"date"        bson:"date"`
  Status      string     `json:"status"      bson:"status"`
  Created     time.Time  `json:"created"     bson:"created"`
}

func (App *App) UpdateShuttles(dataFeed string, updateInterval int) {
  for {
    // Make request to our tracking data feed
    resp, err := http.Get(dataFeed)
    if err != nil {
      continue;
    }
    defer resp.Body.Close()

    // Read response body content
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
      continue;
    }

    delim := "eof"
    // Iterate through all vehicles returned by data feed
    vehicles_data := strings.Split(string(body), delim)
    for i := 1; i < len(vehicles_data)-1; i++ {

      // Match eatch API field with any number (+)
      //   of the previous expressions (\d digit, \. escaped period, - negative number)
      //   Specify named capturing groups to store each field from data feed
      re := regexp.MustCompile(`(?P<id>Vehicle ID:([\d\.]+)) (?P<lat>lat:([\d\.-]+)) (?P<lng>lon:([\d\.-]+)) (?P<heading>dir:([\d\.-]+)) (?P<speed>spd:([\d\.-]+)) (?P<lock>lck:([\d\.-]+)) (?P<time>time:([\d]+)) (?P<date>date:([\d]+)) (?P<status>trig:([\d]+))`)
      n := re.SubexpNames()
      match := re.FindAllStringSubmatch(vehicles_data[i], -1)[0]

      // Store named capturing group and matching expression as a key value pair
      result := map[string]string{}
      for i, item := range match {
        result[n[i]] = item
      }

      // Create new vehicle update & insert update into database
      update := VehicleUpdate {
        Id:        bson.NewObjectId(),
        VehicleId: strings.Replace(result["id"], "Vehicle ID:", "", -1),
        Lat:       strings.Replace(result["lat"], "lat:", "", -1),
        Lng:       strings.Replace(result["lng"], "lon:", "", -1),
        Heading:   strings.Replace(result["heading"], "dir:", "", -1),
        Speed:     strings.Replace(result["speed"], "spd:", "", -1),
        Lock:      strings.Replace(result["lock"], "lck:", "", -1),
        Time:      strings.Replace(result["time"], "time:", "", -1),
        Date:      strings.Replace(result["date"], "date:", "", -1),
        Status:    strings.Replace(result["status"], "trig:", "", -1),
        Created:   time.Now()}

      err := App.Updates.Insert(&update)

      if err != nil {
        fmt.Println(err.Error())
      }
    }

    // Sleep for n seconds before updating again
    time.Sleep(time.Duration(updateInterval) * time.Second)
  }
}

/**
 *  Route handlers - API requests,
 *                   serve view files
 */

type Vehicle struct {
  Id          bson.ObjectId                 `bson:"_id,omitempty"`
  VehicleId   string     `json:"vehicleId"   bson:"vehicleId,omitempty"`
  VehicleName string     `json:"vehicleName" bson:"vehicleName"`
  Created     time.Time  `json:"created"     bson:"created"`
}

/**
 * Find all vehicles in the database
 *
 */
func (App *App) VehiclesHandler(w http.ResponseWriter, r *http.Request) {
  // Find all vehicles in database
  var vehicles []Vehicle
  err := App.Vehicles.Find(bson.M{}).All(&vehicles)
  // Handle query errors
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  // Send each vehicle to client as JSON
  vehiclesJSON, err := json.MarshalIndent(vehicles, "", " ")
  fmt.Fprintf(w, string(vehiclesJSON))
}

/**
 * Add a new vehicle to the database
 *
 */
func (App *App) VehiclesCreateHandler(w http.ResponseWriter, r *http.Request) {
  // Create new vehicle object using request fields
  vehicle := Vehicle{}
  vehicleData := json.NewDecoder(r.Body)
  err := vehicleData.Decode(&vehicle)
  // Error handling
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  // Store new vehicle under vehicles collection
  err = App.Vehicles.Insert(&vehicle)
  // Error handling
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}

/**
 * Vehicle Updates - Get most recent update for each
 *                   vehicle in the vehicles collection
 */
func (App *App) UpdatesHandler(w http.ResponseWriter, r *http.Request) {
  // Store updates for each vehicle
  var vehicles []Vehicle
  var updates []VehicleUpdate
  var update VehicleUpdate
  // Query all Vehicles
  err := App.Vehicles.Find(bson.M{}).All(&vehicles)
  // Handle errors
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  // Find recent updates for each vehicle
  for _,vehicle := range vehicles {
    err := App.Updates.Find(bson.M{"vehicleId": vehicle.VehicleId}).Sort("-created").Limit(1).One(&update)

    if err == nil {
      updates = append(updates, update)
    }
  }
  // Convert updates to JSON
  u, err := json.MarshalIndent(updates, "", " ")
  // Handle JSON parsing errors
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  // Send updates to client
  fmt.Fprint(w, string(u))
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
  http.ServeFile(w, r, "index.html")
}

func AdminHandler(w http.ResponseWriter, r *http.Request) {
  http.ServeFile(w, r, "admin.html")
}

/**
 *  Main - connect to database,
 *         handle routing,
 *         start tracker go routine,
 *         serve requests
 */

type Configuration struct {
  DataFeed        string
  UpdateInterval  int
  MongoUrl        string
  MongoPort       string
}

type App struct {
  Session    *mgo.Session
  Updates    *mgo.Collection
  Vehicles   *mgo.Collection
}

func ReadConfiguration(fileName string) Configuration {
  // Open config file and decode JSON to Configuration struct
  file, _ := os.Open(fileName)
  decoder := json.NewDecoder(file)
  config := Configuration{}
  err := decoder.Decode(&config)
  if err != nil {
    fmt.Println("Unable to read config file: ")
    os.Exit(1)
  }
  return config
}

func main() {
  // Read app configuration file
  config := ReadConfiguration("conf.json")

  // Connect to MongoDB
  session, err := mgo.Dial(config.MongoUrl + ":" + config.MongoPort)
  if err != nil {
    fmt.Println("MongoDB connection failed")
    os.Exit(1)
  }
  // close Mongo session when server terminates
  defer session.Close()

  // Create Shuttles object to store database session and collections
  App := &App{
    session,
    session.DB("shuttle_tracking").C("updates"),
    session.DB("shuttle_tracking").C("vehicles"),
  }

  // Start auto updater
  go App.UpdateShuttles(config.DataFeed, config.UpdateInterval)

  // Routing
  r := mux.NewRouter()
  r.HandleFunc("/", IndexHandler).Methods("GET")
  r.HandleFunc("/admin", AdminHandler).Methods("GET")
  r.HandleFunc("/admin/vehicles", AdminHandler).Methods("GET")
  r.HandleFunc("/admin/tracking", AdminHandler).Methods("GET")
  r.HandleFunc("/admin/stops", AdminHandler).Methods("GET")
  r.HandleFunc("/vehicles", App.VehiclesHandler).Methods("GET")
  r.HandleFunc("/vehicles/create", App.VehiclesCreateHandler).Methods("POST")
  r.HandleFunc("/updates", App.UpdatesHandler).Methods("GET")
  // Static files
  r.PathPrefix("/bower_components/").Handler(http.StripPrefix("/bower_components/", http.FileServer(http.Dir("bower_components/"))))
  r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static/"))))
  // Serve requests
  http.Handle("/", r)
  http.ListenAndServe(":8080", r)
}<commit_msg>Improve error handling loading config.<commit_after>package main

import (
  "log"
  "os"
  "fmt"
  "time"
  "strings"
  "net/http"
  "io/ioutil"
  "regexp"
  "encoding/json"
  "gopkg.in/mgo.v2"
  "gopkg.in/mgo.v2/bson"
  "github.com/gorilla/mux"
)

/**
 *  Shuttle Tracker
 *   Auto Updater - send request to iTrak API,
 *                  get updated shuttle info,
 *                  store updated records in db
 */
type VehicleUpdate struct {
  Id          bson.ObjectId                 `bson:"_id,omitempty"`
  VehicleId   string     `json:"vehicleId"   bson:"vehicleId,omitempty"`
  Lat         string     `json:"lat"         bson:"lat"`
  Lng         string     `json:"lng"         bson:"lng"`
  Heading     string     `json:"heading"     bson:"heading"`
  Speed       string     `json:"speed"       bson:"speed"`
  Lock        string     `json:"lock"        bson:"lock"`
  Time        string     `json:"time"        bson:"time"`
  Date        string     `json:"date"        bson:"date"`
  Status      string     `json:"status"      bson:"status"`
  Created     time.Time  `json:"created"     bson:"created"`
}

func (App *App) UpdateShuttles(dataFeed string, updateInterval int) {
  for {
    // Make request to our tracking data feed
    resp, err := http.Get(dataFeed)
    if err != nil {
      continue;
    }
    defer resp.Body.Close()

    // Read response body content
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
      continue;
    }

    delim := "eof"
    // Iterate through all vehicles returned by data feed
    vehicles_data := strings.Split(string(body), delim)
    for i := 1; i < len(vehicles_data)-1; i++ {

      // Match eatch API field with any number (+)
      //   of the previous expressions (\d digit, \. escaped period, - negative number)
      //   Specify named capturing groups to store each field from data feed
      re := regexp.MustCompile(`(?P<id>Vehicle ID:([\d\.]+)) (?P<lat>lat:([\d\.-]+)) (?P<lng>lon:([\d\.-]+)) (?P<heading>dir:([\d\.-]+)) (?P<speed>spd:([\d\.-]+)) (?P<lock>lck:([\d\.-]+)) (?P<time>time:([\d]+)) (?P<date>date:([\d]+)) (?P<status>trig:([\d]+))`)
      n := re.SubexpNames()
      match := re.FindAllStringSubmatch(vehicles_data[i], -1)[0]

      // Store named capturing group and matching expression as a key value pair
      result := map[string]string{}
      for i, item := range match {
        result[n[i]] = item
      }

      // Create new vehicle update & insert update into database
      update := VehicleUpdate {
        Id:        bson.NewObjectId(),
        VehicleId: strings.Replace(result["id"], "Vehicle ID:", "", -1),
        Lat:       strings.Replace(result["lat"], "lat:", "", -1),
        Lng:       strings.Replace(result["lng"], "lon:", "", -1),
        Heading:   strings.Replace(result["heading"], "dir:", "", -1),
        Speed:     strings.Replace(result["speed"], "spd:", "", -1),
        Lock:      strings.Replace(result["lock"], "lck:", "", -1),
        Time:      strings.Replace(result["time"], "time:", "", -1),
        Date:      strings.Replace(result["date"], "date:", "", -1),
        Status:    strings.Replace(result["status"], "trig:", "", -1),
        Created:   time.Now()}

      err := App.Updates.Insert(&update)

      if err != nil {
        fmt.Println(err.Error())
      }
    }

    // Sleep for n seconds before updating again
    time.Sleep(time.Duration(updateInterval) * time.Second)
  }
}

/**
 *  Route handlers - API requests,
 *                   serve view files
 */

type Vehicle struct {
  Id          bson.ObjectId                 `bson:"_id,omitempty"`
  VehicleId   string     `json:"vehicleId"   bson:"vehicleId,omitempty"`
  VehicleName string     `json:"vehicleName" bson:"vehicleName"`
  Created     time.Time  `json:"created"     bson:"created"`
}

/**
 * Find all vehicles in the database
 *
 */
func (App *App) VehiclesHandler(w http.ResponseWriter, r *http.Request) {
  // Find all vehicles in database
  var vehicles []Vehicle
  err := App.Vehicles.Find(bson.M{}).All(&vehicles)
  // Handle query errors
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  // Send each vehicle to client as JSON
  vehiclesJSON, err := json.MarshalIndent(vehicles, "", " ")
  fmt.Fprintf(w, string(vehiclesJSON))
}

/**
 * Add a new vehicle to the database
 *
 */
func (App *App) VehiclesCreateHandler(w http.ResponseWriter, r *http.Request) {
  // Create new vehicle object using request fields
  vehicle := Vehicle{}
  vehicleData := json.NewDecoder(r.Body)
  err := vehicleData.Decode(&vehicle)
  // Error handling
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  // Store new vehicle under vehicles collection
  err = App.Vehicles.Insert(&vehicle)
  // Error handling
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
}

/**
 * Vehicle Updates - Get most recent update for each
 *                   vehicle in the vehicles collection
 */
func (App *App) UpdatesHandler(w http.ResponseWriter, r *http.Request) {
  // Store updates for each vehicle
  var vehicles []Vehicle
  var updates []VehicleUpdate
  var update VehicleUpdate
  // Query all Vehicles
  err := App.Vehicles.Find(bson.M{}).All(&vehicles)
  // Handle errors
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  // Find recent updates for each vehicle
  for _,vehicle := range vehicles {
    err := App.Updates.Find(bson.M{"vehicleId": vehicle.VehicleId}).Sort("-created").Limit(1).One(&update)

    if err == nil {
      updates = append(updates, update)
    }
  }
  // Convert updates to JSON
  u, err := json.MarshalIndent(updates, "", " ")
  // Handle JSON parsing errors
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }
  // Send updates to client
  fmt.Fprint(w, string(u))
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
  http.ServeFile(w, r, "index.html")
}

func AdminHandler(w http.ResponseWriter, r *http.Request) {
  http.ServeFile(w, r, "admin.html")
}

/**
 *  Main - connect to database,
 *         handle routing,
 *         start tracker go routine,
 *         serve requests
 */

type Configuration struct {
  DataFeed        string
  UpdateInterval  int
  MongoUrl        string
  MongoPort       string
}

type App struct {
  Session    *mgo.Session
  Updates    *mgo.Collection
  Vehicles   *mgo.Collection
}

func ReadConfiguration(fileName string) (*Configuration, error) {
  // Open config file and decode JSON to Configuration struct
  file, err := os.Open(fileName)
  if err != nil {
    return nil, err
  }
  decoder := json.NewDecoder(file)
  config := Configuration{}
  if err := decoder.Decode(&config); err != nil {
    return nil, err
  }
  return &config, nil
}

func main() {
  // Read app configuration file
  config, err := ReadConfiguration("conf.json")
  if err != nil {
    log.Fatalf("error reading configuration file: %v", err)
  }

  // Connect to MongoDB
  session, err := mgo.Dial(config.MongoUrl + ":" + config.MongoPort)
  if err != nil {
    fmt.Println("MongoDB connection failed")
    os.Exit(1)
  }
  // close Mongo session when server terminates
  defer session.Close()

  // Create Shuttles object to store database session and collections
  App := &App{
    session,
    session.DB("shuttle_tracking").C("updates"),
    session.DB("shuttle_tracking").C("vehicles"),
  }

  // Start auto updater
  go App.UpdateShuttles(config.DataFeed, config.UpdateInterval)

  // Routing
  r := mux.NewRouter()
  r.HandleFunc("/", IndexHandler).Methods("GET")
  r.HandleFunc("/admin", AdminHandler).Methods("GET")
  r.HandleFunc("/admin/vehicles", AdminHandler).Methods("GET")
  r.HandleFunc("/admin/tracking", AdminHandler).Methods("GET")
  r.HandleFunc("/admin/stops", AdminHandler).Methods("GET")
  r.HandleFunc("/vehicles", App.VehiclesHandler).Methods("GET")
  r.HandleFunc("/vehicles/create", App.VehiclesCreateHandler).Methods("POST")
  r.HandleFunc("/updates", App.UpdatesHandler).Methods("GET")
  // Static files
  r.PathPrefix("/bower_components/").Handler(http.StripPrefix("/bower_components/", http.FileServer(http.Dir("bower_components/"))))
  r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static/"))))
  // Serve requests
  http.Handle("/", r)
  http.ListenAndServe(":8080", r)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"regexp"
	"strings"

	"github.com/GeertJohan/yubigo"
	"github.com/kisom/whitelist"
	"github.com/qbit/puber/backend"
)

type puberReq struct {
	User   string `json:"user"`
	PubKey string `json:"pubKey"`
	YKey   string `json:"yKey"`
}

var listen string
var redisServer string
var prefix string
var yubiServer string
var yubiSKey string
var yubiCID string
var err error
var store = backend.MemStore{}
var be backend.Backend

func auth(ykey string) (*yubigo.YubiResponse, bool, error) {
	yubiAuth, err := yubigo.NewYubiAuth(yubiCID, yubiSKey)
	if yubiServer != "" {
		log.Printf("using '%s'", yubiServer)
		yubiAuth.SetApiServerList(yubiServer)
	}
	if err != nil {
		log.Println(err)
	}

	return yubiAuth.Verify(ykey)
}

func handleIdx(w http.ResponseWriter, r *http.Request) {
	data, err := be.GetCount()
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "I currently have %d key(s)", data)
}

func handleAll(w http.ResponseWriter, r *http.Request) {
	keys, err := be.GetAll()
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var data string
	for i, un := range keys {
		fmt.Println(un)
		for j := range keys[i] {
			data += keys[i][j] + "\n"
		}
	}

	fmt.Fprintf(w, data)
}

func handleUser(w http.ResponseWriter, r *http.Request) {
	u, err := url.Parse(r.URL.Path)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
	}

	parts := strings.Split(u.String(), "/")
	user := parts[len(parts)-1]

	match, err := regexp.Match(",", []byte(user))
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
	}

	var data string

	if match {
		users := strings.Split(user, ",")
		var d []string
		for i := range users {
			d, err = be.Get(users[i])
			if err != nil {

				log.Println(err)
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}

			for j := range d {
				data += d[j] + "\n"
			}

		}
	} else {
		var d []string
		d, err = be.Get(user)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		data += strings.Join(d, "\n")

	}
	fmt.Fprintf(w, data)
}

func readPost(r *http.Request) (*puberReq, error) {
	var data puberReq

	if r.Body == nil {
		return nil, errors.New("empty request")
	}

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}

	defer r.Body.Close()

	err = json.Unmarshal(body, &data)
	if err != nil {
		return nil, err
	}

	return &data, nil
}

func handleAdd(w http.ResponseWriter, r *http.Request) {
	data, err := readPost(r)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	result, ok, err := auth(data.YKey)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	log.Println(result.GetRequestQuery())

	if ok {
		be.Add(data.User, data.PubKey)
		fmt.Fprintf(w, "Added\n")
	} else {
		log.Println("Auth Failed!")
		http.Error(w, "Auth Failed!", http.StatusUnauthorized)
	}
}

func handleRm(w http.ResponseWriter, r *http.Request) {
	data, err := readPost(r)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	_, ok, err := auth(data.YKey)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if ok {
		_, err := be.RMAll(data.User)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		fmt.Fprintf(w, "Removed\n")
	} else {
		log.Println("Auth Failed!")
		http.Error(w, "Auth Failed!", http.StatusUnauthorized)
	}
}

func init() {
	store.Init()
	be = backend.Backend(&store)
}

func main() {
	addWL := whitelist.NewBasic()
	rmWL := whitelist.NewBasic()
	wlAddHandler, err := whitelist.NewHandlerFunc(handleAdd, nil, addWL)
	if err != nil {
		log.Fatalf("%v", err)
	}

	wlRMHandler, err := whitelist.NewHandlerFunc(handleRm, nil, rmWL)
	if err != nil {
		log.Fatalf("%v", err)
	}

	flag.StringVar(&listen, "listen", ":8081", "listen string")
	whiteList := flag.String("wl", "localhost", "comma seperated list of hosts to allow access to /add")
	flag.StringVar(&redisServer, "redishost", "localhost:6379", "redis server")
	flag.StringVar(&prefix, "prefix", "puber", "prefix to store redis keys with")
	flag.StringVar(&yubiServer, "yserver", "api.yubico.com/wsapi/2.0/verify", "YubiAuth server to authenticate against")
	flag.StringVar(&yubiSKey, "yskey", "", "Yubi API Key")
	flag.StringVar(&yubiCID, "ycid", "", "Yubi Client ID")

	flag.Parse()

	for _, host := range strings.Split(*whiteList, ",") {
		ip, err := net.LookupHost(host)
		if err != nil {
			log.Fatal(err)
		}
		for i := range ip {
			log.Printf("whitelisting: %s (%s)\n", host, ip[i])

			// TODO: It's ghetto to have two whitelists.
			// Reduce this down to use one.
			addWL.Add(net.ParseIP(ip[i]))
			rmWL.Add(net.ParseIP(ip[i]))
		}
	}

	http.HandleFunc("/", handleIdx)
	http.HandleFunc("/all", handleAll)
	http.HandleFunc("/user/", handleUser)
	http.Handle("/add", wlAddHandler)
	http.Handle("/rm", wlRMHandler)

	log.Printf("Listening on '%s'\n", listen)
	log.Fatal(http.ListenAndServe(listen, nil))
}
<commit_msg>add debug data on startup with -debug flag<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"regexp"
	"strings"

	"github.com/GeertJohan/yubigo"
	"github.com/kisom/whitelist"
	"github.com/qbit/puber/backend"
)

type puberReq struct {
	User   string `json:"user"`
	PubKey string `json:"pubKey"`
	YKey   string `json:"yKey"`
}

var listen string
var redisServer string
var prefix string
var yubiServer string
var yubiSKey string
var yubiCID string
var err error
var debug bool
var store = backend.MemStore{}
var be backend.Backend

func auth(ykey string) (*yubigo.YubiResponse, bool, error) {
	yubiAuth, err := yubigo.NewYubiAuth(yubiCID, yubiSKey)
	if yubiServer != "" {
		log.Printf("using '%s'", yubiServer)
		yubiAuth.SetApiServerList(yubiServer)
	}
	if err != nil {
		log.Println(err)
	}

	return yubiAuth.Verify(ykey)
}

func handleIdx(w http.ResponseWriter, r *http.Request) {
	data, err := be.GetCount()
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "I currently have %d key(s)", data)
}

func handleAll(w http.ResponseWriter, r *http.Request) {
	keys, err := be.GetAll()
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var data string
	for i := range keys {
		for j := range keys[i] {
			data += keys[i][j] + "\n"
		}
	}

	fmt.Fprintf(w, data)
}

func handleUser(w http.ResponseWriter, r *http.Request) {
	u, err := url.Parse(r.URL.Path)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
	}

	parts := strings.Split(u.String(), "/")
	user := parts[len(parts)-1]

	match, err := regexp.Match(",", []byte(user))
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
	}

	var data string

	if match {
		users := strings.Split(user, ",")
		var d []string
		for i := range users {
			d, err = be.Get(users[i])
			if err != nil {
				log.Println(err)
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}

			for j := range d {
				data += d[j] + "\n"
			}

		}
	} else {
		var d []string
		d, err = be.Get(user)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		data += strings.Join(d, "\n")

	}
	fmt.Fprintf(w, data)
}

func readPost(r *http.Request) (*puberReq, error) {
	var data puberReq

	if r.Body == nil {
		return nil, errors.New("empty request")
	}

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}

	defer r.Body.Close()

	err = json.Unmarshal(body, &data)
	if err != nil {
		return nil, err
	}

	return &data, nil
}

func handleAdd(w http.ResponseWriter, r *http.Request) {
	data, err := readPost(r)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	result, ok, err := auth(data.YKey)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if debug {
		log.Println(result.GetRequestQuery())
	}

	if ok {
		be.Add(data.User, data.PubKey)
		fmt.Fprintf(w, "Added\n")
	} else {
		log.Println("Auth Failed!")
		http.Error(w, "Auth Failed!", http.StatusUnauthorized)
	}
}

func handleRm(w http.ResponseWriter, r *http.Request) {
	data, err := readPost(r)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	_, ok, err := auth(data.YKey)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if ok {
		if data.PubKey != "" {
			_, err := be.RM(data.User, data.PubKey)
			if err != nil {
				log.Println(err)
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
			fmt.Fprintf(w, fmt.Sprintf("Removed '%s' from '%s'\n", data.PubKey, data.User))
		} else {
			_, err := be.RMAll(data.User)
			if err != nil {
				log.Println(err)
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
			fmt.Fprintf(w, "Removed\n")
		}
	} else {
		log.Println("Auth Failed!")
		http.Error(w, "Auth Failed!", http.StatusUnauthorized)
	}
}

func init() {
	store.Init()
	be = backend.Backend(&store)
}

func main() {
	addWL := whitelist.NewBasic()
	rmWL := whitelist.NewBasic()
	wlAddHandler, err := whitelist.NewHandlerFunc(handleAdd, nil, addWL)
	if err != nil {
		log.Fatalf("%v", err)
	}

	wlRMHandler, err := whitelist.NewHandlerFunc(handleRm, nil, rmWL)
	if err != nil {
		log.Fatalf("%v", err)
	}

	flag.StringVar(&listen, "listen", ":8081", "listen string")
	whiteList := flag.String("wl", "localhost", "comma seperated list of hosts to allow access to /add")
	flag.StringVar(&redisServer, "redishost", "localhost:6379", "redis server")
	flag.StringVar(&prefix, "prefix", "puber", "prefix to store redis keys with")
	flag.StringVar(&yubiServer, "yserver", "api.yubico.com/wsapi/2.0/verify", "YubiAuth server to authenticate against")
	flag.StringVar(&yubiSKey, "yskey", "", "Yubi API Key")
	flag.StringVar(&yubiCID, "ycid", "", "Yubi Client ID")

	flag.BoolVar(&debug, "debug", false, "Enable debugging")

	flag.Parse()

	if debug {
		be.Add("debug", "debug key")
		be.Add("debug", "debug key two")
		be.Add("debug", "debug key three")
	}

	for _, host := range strings.Split(*whiteList, ",") {
		ip, err := net.LookupHost(host)
		if err != nil {
			log.Fatal(err)
		}
		for i := range ip {
			log.Printf("whitelisting: %s (%s)\n", host, ip[i])

			// TODO: It's ghetto to have two whitelists.
			// Reduce this down to use one.
			addWL.Add(net.ParseIP(ip[i]))
			rmWL.Add(net.ParseIP(ip[i]))
		}
	}

	http.HandleFunc("/", handleIdx)
	http.HandleFunc("/all", handleAll)
	http.HandleFunc("/user/", handleUser)
	http.Handle("/add", wlAddHandler)
	http.Handle("/rm", wlRMHandler)

	log.Printf("Listening on '%s'\n", listen)
	log.Fatal(http.ListenAndServe(listen, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"strings"
	"time"

	"github.com/julienschmidt/httprouter"
)

// Kudo main kudos
type Kudo struct {
	ID         int64
	Text       string
	Original   string
	MemberFrom Member
	Recipients []Member
	Value      int
	Color      string
	Date       time.Time
}

type pageView struct {
	Kudos  []kudoView
	Events []event
}

type kudoView struct {
	Item Kudo
	Text []string
}

var kudos []Kudo

func main() {
	readConfig()
	loadUsers()
	loadDatabase()

	router := httprouter.New()
	router.GET("/", index)
	router.POST("/kudo", handleKudoCmd)
	router.POST("/boo", handleKudoCmd)

	router.ServeFiles("/asset/*filepath", http.Dir(config.AssetPath))

	log.Println("Listening on port", config.Port, "...")
	log.Fatal(http.ListenAndServe(fmt.Sprint(":", config.Port), router))
}

func handleKudoCmd(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

	memberFrom, err := getMemberFrom(r)
	if err != nil {
		fmt.Fprint(w, "Invalid user provided")
		return
	}

	command, text, err := getCommandParams(r)
	if err != nil {
		printKudoUsage(w)
		return
	}

	switch command {
	case "to":
		value := 1
		if r.PostFormValue("command") == "/boo" {
			value = -1
		}
		handleNewKudoCommand(w, memberFrom, text, value)
	default:
		printKudoUsage(w)
	}
}

func printKudoUsage(w http.ResponseWriter) {
	fmt.Fprint(w, "New kudo: `/kudos to @user1 [@user2, ...] message`")
}

func index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {

	r.Header.Set("Content-Type", "text/html")

	var page = `
	<!DOCTYPE html>

  	<meta charset="utf-8">
		<meta http-equiv="refresh" content="300">
	  <meta http-equiv="X-UA-Compatible" content="IE=edge">
	  <meta name="viewport" content="width=1920, initial-scale=1">
	  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
	  <link rel="stylesheet" href="/asset/c.css">
	  <link href='https://fonts.googleapis.com/css?family=Patrick+Hand|Droid+Sans' rel='stylesheet' type='text/css'>

	{{range .Kudos}}

			{{if eq .Item.Value -1}}

			{{end}}

{{range .Item.Recipients}}@{{.Name}}, {{end}}
{{range .Text}}{{.}}
{{end}}

			@{{.Item.MemberFrom.Name}}

	{{end}}

Happening today

				{{range .Events}}
			  	{{if .Today}}

{{.Date}} {{.Event.Summary}}

			  	{{end}}
				{{end}}

Upcoming events

				{{range .Events}}
			  	{{if not .Today}}

{{.Date}} {{.Event.Summary}}

			  	{{end}}
				{{end}}

	`
	tmpl := template.New("page")
	var err error
	if tmpl, err = tmpl.Parse(page); err != nil {
		fmt.Println(err)
	}

	var viewKudos []kudoView
	for i := 0; i < 9 && i < len(kudos); i++ {
		view := kudoView{Item: kudos[i], Text: strings.Split(kudos[i].Text, "\n")}
		viewKudos = append(viewKudos, view)
	}
	pageData := pageView{Kudos: viewKudos, Events: getEvents()}

	tmpl.Execute(w, pageData)
}

func getMemberFrom(r *http.Request) (Member, error) {
	memberFromTag := r.PostFormValue("user_name")

	var memberFrom, err = findMemberByTag(memberFromTag)
	if err != nil {
		loadUsers()
		memberFrom, err = findMemberByTag(memberFromTag)

		if err != nil {
			return Member{}, fmt.Errorf("Could not find member %s", memberFromTag)
		}
	}

	return memberFrom, nil
}

func getCommandParams(r *http.Request) (string, string, error) {
	var text = r.PostFormValue("text")

	textParts := strings.SplitN(text, " ", 2)
	if len(textParts) < 1 {
		return "", "", fmt.Errorf("Invalid number of parameters.")
	}

	command, text := textParts[0], ""
	if len(textParts) == 2 {
		text = textParts[1]
	}

	return command, text, nil
}

func randomColor() string {
	colors := []string{"yellow", "pink", "green", "red", "orange", ""}
	return colors[rand.Intn(len(colors))]
}
<commit_msg>fs on android<commit_after>package main

import (
	"fmt"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"strings"
	"time"

	"github.com/julienschmidt/httprouter"
)

// Kudo main kudos
type Kudo struct {
	ID         int64
	Text       string
	Original   string
	MemberFrom Member
	Recipients []Member
	Value      int
	Color      string
	Date       time.Time
}

type pageView struct {
	Kudos  []kudoView
	Events []event
}

type kudoView struct {
	Item Kudo
	Text []string
}

var kudos []Kudo

func main() {
	readConfig()
	loadUsers()
	loadDatabase()

	router := httprouter.New()
	router.GET("/", index)
	router.POST("/kudo", handleKudoCmd)
	router.POST("/boo", handleKudoCmd)

	router.ServeFiles("/asset/*filepath", http.Dir(config.AssetPath))

	log.Println("Listening on port", config.Port, "...")
	log.Fatal(http.ListenAndServe(fmt.Sprint(":", config.Port), router))
}

func handleKudoCmd(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

	memberFrom, err := getMemberFrom(r)
	if err != nil {
		fmt.Fprint(w, "Invalid user provided")
		return
	}

	command, text, err := getCommandParams(r)
	if err != nil {
		printKudoUsage(w)
		return
	}

	switch command {
	case "to":
		value := 1
		if r.PostFormValue("command") == "/boo" {
			value = -1
		}
		handleNewKudoCommand(w, memberFrom, text, value)
	default:
		printKudoUsage(w)
	}
}

func printKudoUsage(w http.ResponseWriter) {
	fmt.Fprint(w, "New kudo: `/kudos to @user1 [@user2, ...] message`")
}

func index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {

	r.Header.Set("Content-Type", "text/html")

	var page = `
	<!DOCTYPE html>

  	<meta charset="utf-8">
		<meta http-equiv="refresh" content="300">
	  <meta http-equiv="X-UA-Compatible" content="IE=edge">
	  <meta name="viewport" content="width=1920, initial-scale=1">
		<meta name="mobile-web-app-capable" content="yes">
	  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
	  <link rel="stylesheet" href="/asset/c.css">
	  <link href='https://fonts.googleapis.com/css?family=Patrick+Hand|Droid+Sans' rel='stylesheet' type='text/css'>

	{{range .Kudos}}

			{{if eq .Item.Value -1}}

			{{end}}

{{range .Item.Recipients}}@{{.Name}}, {{end}}
{{range .Text}}{{.}}
{{end}}

			@{{.Item.MemberFrom.Name}}

	{{end}}

Happening today

				{{range .Events}}
			  	{{if .Today}}

{{.Date}} {{.Event.Summary}}

			  	{{end}}
				{{end}}

Upcoming events

				{{range .Events}}
			  	{{if not .Today}}

{{.Date}} {{.Event.Summary}}

			  	{{end}}
				{{end}}

	`
	tmpl := template.New("page")
	var err error
	if tmpl, err = tmpl.Parse(page); err != nil {
		fmt.Println(err)
	}

	var viewKudos []kudoView
	for i := 0; i < 9 && i < len(kudos); i++ {
		view := kudoView{Item: kudos[i], Text: strings.Split(kudos[i].Text, "\n")}
		viewKudos = append(viewKudos, view)
	}
	pageData := pageView{Kudos: viewKudos, Events: getEvents()}

	tmpl.Execute(w, pageData)
}

func getMemberFrom(r *http.Request) (Member, error) {
	memberFromTag := r.PostFormValue("user_name")

	var memberFrom, err = findMemberByTag(memberFromTag)
	if err != nil {
		loadUsers()
		memberFrom, err = findMemberByTag(memberFromTag)

		if err != nil {
			return Member{}, fmt.Errorf("Could not find member %s", memberFromTag)
		}
	}

	return memberFrom, nil
}

func getCommandParams(r *http.Request) (string, string, error) {
	var text = r.PostFormValue("text")

	textParts := strings.SplitN(text, " ", 2)
	if len(textParts) < 1 {
		return "", "", fmt.Errorf("Invalid number of parameters.")
	}

	command, text := textParts[0], ""
	if len(textParts) == 2 {
		text = textParts[1]
	}

	return command, text, nil
}

func randomColor() string {
	colors := []string{"yellow", "pink", "green", "red", "orange", ""}
	return colors[rand.Intn(len(colors))]
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/user"
	"time"

	"golang.org/x/net/context"

	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"

	"github.com/almighty/almighty-core/app"
	"github.com/almighty/almighty-core/configuration"
	"github.com/almighty/almighty-core/migration"
	"github.com/almighty/almighty-core/models"
	"github.com/almighty/almighty-core/remoteworkitem"
	"github.com/almighty/almighty-core/transaction"
	token "github.com/dgrijalva/jwt-go"
	"github.com/goadesign/goa"
	"github.com/goadesign/goa/middleware"
	"github.com/goadesign/goa/middleware/security/jwt"
)

var (
	// Commit current build commit set by build script
	Commit = "0"
	// BuildTime set by build script in ISO 8601 (UTC) format: YYYY-MM-DDThh:mm:ssTZD (see https://www.w3.org/TR/NOTE-datetime for details)
	BuildTime = "0"
	// StartTime in ISO 8601 (UTC) format
	StartTime = time.Now().UTC().Format("2006-01-02T15:04:05Z")
)

func main() {
	// --------------------------------------------------------------------
	// Parse flags
	// --------------------------------------------------------------------
	var configFilePath string
	var printConfig bool
	var migrateDB bool
	var scheduler *remoteworkitem.Scheduler
	flag.StringVar(&configFilePath, "config", "", "Path to the config file to read")
	flag.BoolVar(&printConfig, "printConfig", false, "Prints the config (including merged environment variables) and exits")
	flag.BoolVar(&migrateDB, "migrateDatabase", false, "Migrates the database to the newest version and exits.")
	flag.Parse()

	// Override default -config switch with environment variable only if -config switch was
	// not explicitly given via the command line.
	configSwitchIsSet := false
	flag.Visit(func(f *flag.Flag) {
		if f.Name == "config" {
			configSwitchIsSet = true
		}
	})
	if !configSwitchIsSet {
		if envConfigPath, ok := os.LookupEnv("ALMIGHTY_CONFIG_FILE_PATH"); ok {
			configFilePath = envConfigPath
		}
	}

	var err error
	if err = configuration.Setup(configFilePath); err != nil {
		panic(fmt.Errorf("Failed to setup the configuration: %s", err.Error()))
	}

	if printConfig {
		fmt.Printf("%s\n", configuration.String())
		os.Exit(0)
	}

	printUserInfo()

	var db *gorm.DB
	for i := 1; i <= configuration.GetPostgresConnectionMaxRetries(); i++ {
		log.Printf("Opening DB connection attempt %d of %d\n", i, configuration.GetPostgresConnectionMaxRetries())
		db, err = gorm.Open("postgres", configuration.GetPostgresConfigString())
		if err != nil {
			time.Sleep(configuration.GetPostgresConnectionRetrySleep())
		} else {
			defer db.Close()
			break
		}
	}
	if err != nil {
		panic("Could not open connection to database")
	}

	// Migrate the schema
	err = migration.Migrate(db.DB())
	if err != nil {
		panic(err.Error())
	}

	// Nothing to here except exit, since the migration is already performed.
	if migrateDB {
		os.Exit(0)
	}

	ts := models.NewGormTransactionSupport(db)
	witRepo := models.NewWorkItemTypeRepository(ts)
	wiRepo := models.NewWorkItemRepository(ts, witRepo)

	// Make sure the database is populated with the correct types (e.g. system.bug etc.)
	if configuration.GetPopulateCommonTypes() {
		if err := transaction.Do(ts, func() error {
			return migration.PopulateCommonTypes(context.Background(), ts.TX(), witRepo)
		}); err != nil {
			panic(err.Error())
		}
	}

	// Scheduler to fetch and import remote tracker items
	scheduler = remoteworkitem.NewScheduler(db)
	defer scheduler.Stop()
	scheduler.ScheduleAllQueries()

	// Create service
	service := goa.New("alm")

	// Mount middleware
	service.Use(middleware.RequestID())
	service.Use(middleware.LogRequest(true))
	service.Use(middleware.ErrorHandler(service, true))
	service.Use(middleware.Recover())

	publicKey, err := token.ParseRSAPublicKeyFromPEM([]byte(RSAPublicKey))
	if err != nil {
		panic(err)
	}
	app.UseJWTMiddleware(service, jwt.New(publicKey, nil, app.NewJWTSecurity()))

	// Mount "login" controller
	loginCtrl := NewLoginController(service)
	app.MountLoginController(service, loginCtrl)
	// Mount "status" controller
	statusCtrl := NewStatusController(service, db)
	app.MountStatusController(service, statusCtrl)

	// Mount "workitem" controller
	workitemCtrl := NewWorkitemController(service, wiRepo, ts)
	app.MountWorkitemController(service, workitemCtrl)

	// Mount "workitemtype" controller
	workitemtypeCtrl := NewWorkitemtypeController(service, witRepo, ts)
	app.MountWorkitemtypeController(service, workitemtypeCtrl)

	ts2 := models.NewGormTransactionSupport(db)

	// Mount "tracker" controller
	repo2 := remoteworkitem.NewTrackerRepository(ts2)
	c5 := NewTrackerController(service, repo2, ts2, scheduler)
	app.MountTrackerController(service, c5)

	// Mount "trackerquery" controller
	repo3 := remoteworkitem.NewTrackerQueryRepository(ts2)
	c6 := NewTrackerqueryController(service, repo3, ts2, scheduler)
	app.MountTrackerqueryController(service, c6)

	fmt.Println("Git Commit SHA: ", Commit)
	fmt.Println("UTC Build Time: ", BuildTime)
	fmt.Println("UTC Start Time: ", StartTime)
	fmt.Println("Dev mode:       ", configuration.IsPostgresDeveloperModeEnabled())

	http.Handle("/api/", service.Mux)
	http.Handle("/", http.FileServer(assetFS()))
	http.Handle("/favicon.ico", http.NotFoundHandler())

	// Start http
	if err := http.ListenAndServe(configuration.GetHTTPAddress(), nil); err != nil {
		service.LogError("startup", "err", err)
	}

}

func printUserInfo() {
	u, err := user.Current()
	if err != nil {
		log.Printf("Failed to get current user: %s", err.Error())
	} else {
		log.Printf("Running as user name \"%s\" with UID %s.\n", u.Username, u.Uid)
		/*
			g, err := user.LookupGroupId(u.Gid)
			if err != nil {
				fmt.Printf("Failed to lookup group: %", err.Error())
			} else {
				fmt.Printf("Running with group \"%s\" with GID %s.\n", g.Name, g.Gid)
			}
		*/
	}
}
<commit_msg>Add gzip compression for REST response (#347)<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/user"
	"time"

	"golang.org/x/net/context"

	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"

	"github.com/almighty/almighty-core/app"
	"github.com/almighty/almighty-core/configuration"
	"github.com/almighty/almighty-core/migration"
	"github.com/almighty/almighty-core/models"
	"github.com/almighty/almighty-core/remoteworkitem"
	"github.com/almighty/almighty-core/transaction"
	token "github.com/dgrijalva/jwt-go"
	"github.com/goadesign/goa"
	"github.com/goadesign/goa/middleware"
	"github.com/goadesign/goa/middleware/gzip"
	"github.com/goadesign/goa/middleware/security/jwt"
)

var (
	// Commit current build commit set by build script
	Commit = "0"
	// BuildTime set by build script in ISO 8601 (UTC) format: YYYY-MM-DDThh:mm:ssTZD (see https://www.w3.org/TR/NOTE-datetime for details)
	BuildTime = "0"
	// StartTime in ISO 8601 (UTC) format
	StartTime = time.Now().UTC().Format("2006-01-02T15:04:05Z")
)

func main() {
	// --------------------------------------------------------------------
	// Parse flags
	// --------------------------------------------------------------------
	var configFilePath string
	var printConfig bool
	var migrateDB bool
	var scheduler *remoteworkitem.Scheduler
	flag.StringVar(&configFilePath, "config", "", "Path to the config file to read")
	flag.BoolVar(&printConfig, "printConfig", false, "Prints the config (including merged environment variables) and exits")
	flag.BoolVar(&migrateDB, "migrateDatabase", false, "Migrates the database to the newest version and exits.")
	flag.Parse()

	// Override default -config switch with environment variable only if -config switch was
	// not explicitly given via the command line.
	configSwitchIsSet := false
	flag.Visit(func(f *flag.Flag) {
		if f.Name == "config" {
			configSwitchIsSet = true
		}
	})
	if !configSwitchIsSet {
		if envConfigPath, ok := os.LookupEnv("ALMIGHTY_CONFIG_FILE_PATH"); ok {
			configFilePath = envConfigPath
		}
	}

	var err error
	if err = configuration.Setup(configFilePath); err != nil {
		panic(fmt.Errorf("Failed to setup the configuration: %s", err.Error()))
	}

	if printConfig {
		fmt.Printf("%s\n", configuration.String())
		os.Exit(0)
	}

	printUserInfo()

	var db *gorm.DB
	for i := 1; i <= configuration.GetPostgresConnectionMaxRetries(); i++ {
		log.Printf("Opening DB connection attempt %d of %d\n", i, configuration.GetPostgresConnectionMaxRetries())
		db, err = gorm.Open("postgres", configuration.GetPostgresConfigString())
		if err != nil {
			time.Sleep(configuration.GetPostgresConnectionRetrySleep())
		} else {
			defer db.Close()
			break
		}
	}
	if err != nil {
		panic("Could not open connection to database")
	}

	// Migrate the schema
	err = migration.Migrate(db.DB())
	if err != nil {
		panic(err.Error())
	}

	// Nothing to here except exit, since the migration is already performed.
	if migrateDB {
		os.Exit(0)
	}

	ts := models.NewGormTransactionSupport(db)
	witRepo := models.NewWorkItemTypeRepository(ts)
	wiRepo := models.NewWorkItemRepository(ts, witRepo)

	// Make sure the database is populated with the correct types (e.g. system.bug etc.)
	if configuration.GetPopulateCommonTypes() {
		if err := transaction.Do(ts, func() error {
			return migration.PopulateCommonTypes(context.Background(), ts.TX(), witRepo)
		}); err != nil {
			panic(err.Error())
		}
	}

	// Scheduler to fetch and import remote tracker items
	scheduler = remoteworkitem.NewScheduler(db)
	defer scheduler.Stop()
	scheduler.ScheduleAllQueries()

	// Create service
	service := goa.New("alm")

	// Mount middleware
	service.Use(middleware.RequestID())
	service.Use(middleware.LogRequest(true))
	service.Use(gzip.Middleware(9))
	service.Use(middleware.ErrorHandler(service, true))
	service.Use(middleware.Recover())

	publicKey, err := token.ParseRSAPublicKeyFromPEM([]byte(RSAPublicKey))
	if err != nil {
		panic(err)
	}
	app.UseJWTMiddleware(service, jwt.New(publicKey, nil, app.NewJWTSecurity()))

	// Mount "login" controller
	loginCtrl := NewLoginController(service)
	app.MountLoginController(service, loginCtrl)
	// Mount "status" controller
	statusCtrl := NewStatusController(service, db)
	app.MountStatusController(service, statusCtrl)

	// Mount "workitem" controller
	workitemCtrl := NewWorkitemController(service, wiRepo, ts)
	app.MountWorkitemController(service, workitemCtrl)

	// Mount "workitemtype" controller
	workitemtypeCtrl := NewWorkitemtypeController(service, witRepo, ts)
	app.MountWorkitemtypeController(service, workitemtypeCtrl)

	ts2 := models.NewGormTransactionSupport(db)

	// Mount "tracker" controller
	repo2 := remoteworkitem.NewTrackerRepository(ts2)
	c5 := NewTrackerController(service, repo2, ts2, scheduler)
	app.MountTrackerController(service, c5)

	// Mount "trackerquery" controller
	repo3 := remoteworkitem.NewTrackerQueryRepository(ts2)
	c6 := NewTrackerqueryController(service, repo3, ts2, scheduler)
	app.MountTrackerqueryController(service, c6)

	fmt.Println("Git Commit SHA: ", Commit)
	fmt.Println("UTC Build Time: ", BuildTime)
	fmt.Println("UTC Start Time: ", StartTime)
	fmt.Println("Dev mode:       ", configuration.IsPostgresDeveloperModeEnabled())

	http.Handle("/api/", service.Mux)
	http.Handle("/", http.FileServer(assetFS()))
	http.Handle("/favicon.ico", http.NotFoundHandler())

	// Start http
	if err := http.ListenAndServe(configuration.GetHTTPAddress(), nil); err != nil {
		service.LogError("startup", "err", err)
	}

}

func printUserInfo() {
	u, err := user.Current()
	if err != nil {
		log.Printf("Failed to get current user: %s", err.Error())
	} else {
		log.Printf("Running as user name \"%s\" with UID %s.\n", u.Username, u.Uid)
		/*
			g, err := user.LookupGroupId(u.Gid)
			if err != nil {
				fmt.Printf("Failed to lookup group: %", err.Error())
			} else {
				fmt.Printf("Running with group \"%s\" with GID %s.\n", g.Name, g.Gid)
			}
		*/
	}
}
<|endoftext|>


<commit_before>package gohome

import (
	"os"
	"path/filepath"
	"runtime"
)

// Cache returns the correct folder to store your apps cache, according to the
// spec of each operating system.
func Cache(appName string) string {
	return cacheFor(appName, runtime.GOOS)
}

// Config returns the correct folder to store your apps configuration,
// according to the spec of each operating system.
func Config(appName string) string {
	return configFor(appName, runtime.GOOS)
}

func cacheFor(appName, os string) string {
	switch os {
	case "darwin":
		return filepath.Join(home(), "Library", "Caches", appName)
	case "windows":
		return appData(appName)
	default:
		return filepath.Join(xdgCache(), appName)
	}
}

func configFor(appName, os string) string {
	switch os {
	case "darwin":
		return filepath.Join(home(), "Library", "Application Support", appName)
	case "windows":
		return appData(appName)
	default:
		return filepath.Join(xdgConfig(), appName)
	}
}

func xdgConfig() string {
	xdg := os.Getenv("XDG_CONFIG_HOME")
	if xdg == "" {
		return filepath.Join(home(), ".config")
	}
	return xdg
}

func xdgCache() string {
	xdg := os.Getenv("XDG_CACHE_HOME")
	if xdg == "" {
		return filepath.Join(home(), ".cache")
	}
	return xdg
}

func appData(appName string) string {
	return filepath.Join(os.Getenv("APPDATA"), appName)
}

func home() string {
	return os.Getenv("HOME")
}
<commit_msg>using os/user instead of Getenv(HOME)<commit_after>package gohome

import (
	"os"
	"os/user"
	"path/filepath"
	"runtime"
)

// Cache returns the correct folder to store your apps cache, according to the
// spec of each operating system.
func Cache(appName string) string {
	return cacheFor(appName, runtime.GOOS)
}

// Config returns the correct folder to store your apps configuration,
// according to the spec of each operating system.
func Config(appName string) string {
	return configFor(appName, runtime.GOOS)
}

func cacheFor(appName, os string) string {
	switch os {
	case "darwin":
		return filepath.Join(home(), "Library", "Caches", appName)
	case "windows":
		return appData(appName)
	default:
		return filepath.Join(xdgCache(), appName)
	}
}

func configFor(appName, os string) string {
	switch os {
	case "darwin":
		return filepath.Join(home(), "Library", "Application Support", appName)
	case "windows":
		return appData(appName)
	default:
		return filepath.Join(xdgConfig(), appName)
	}
}

func xdgConfig() string {
	xdg := os.Getenv("XDG_CONFIG_HOME")
	if xdg == "" {
		return filepath.Join(home(), ".config")
	}
	return xdg
}

func xdgCache() string {
	xdg := os.Getenv("XDG_CACHE_HOME")
	if xdg == "" {
		return filepath.Join(home(), ".cache")
	}
	return xdg
}

func appData(appName string) string {
	return filepath.Join(os.Getenv("APPDATA"), appName)
}

func home() string {
	u, err := user.Current()
	if err != nil {
		panic(err.Error())
	}
	return u.HomeDir
}
<|endoftext|>