<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

func main() {
	if err := newRootCommand().Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v", err)
		os.Exit(1)
	}
}

func newRootCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "sdb [command]",
		Short: "SDB is collection of utilities for Apache Jackrabbit Oak's Segment Store",
	}
	cmd.AddCommand(newTarsCommand())
	cmd.AddCommand(newEntriesCommand())
	cmd.AddCommand(newSegmentsCommand())
	cmd.AddCommand(newSegmentCommand())
	cmd.AddCommand(newIndexCommand())
	cmd.AddCommand(newGraphCommand())
	cmd.AddCommand(newBinariesCommand())
	return cmd
}

func newTarsCommand() *cobra.Command {
	var all bool
	cmd := &cobra.Command{
		Use:   "tars [dir]",
		Short: "Prints the TAR files at the provided path.",
		Run: func(cmd *cobra.Command, args []string) {
			directory, err := os.Getwd()
			if err != nil {
				fmt.Fprintf(os.Stderr, "Unable to determine the working directory: %v.\n", err)
				os.Exit(1)
			}
			if len(args) > 1 {
				fmt.Fprintf(os.Stderr, "Too many arguments.\n")
				os.Exit(1)
			}
			if len(args) == 1 {
				directory = args[0]
			}
			if err := forEachTarFile(directory, all, doPrintTo(os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print TAR files: %v.\n", err)
				os.Exit(1)
			}
		},
	}
	cmd.Flags().BoolVar(&all, "all", false, "List both active and non-active TAR files")
	return cmd
}

func newEntriesCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "entries file",
		Short: "Prints the entries from the specified TAR file.",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) > 1 {
				fmt.Fprintf(os.Stderr, "Too many arguments.\n")
				os.Exit(1)
			}
			if len(args) < 1 {
				fmt.Fprintf(os.Stderr, "Too few arguments.\n")
				os.Exit(1)
			}
			if err := forEachEntry(args[0], doPrintNameTo(os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print TAR entries: %v.\n", err)
				os.Exit(1)
			}
		},
	}
}

func newSegmentsCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "segments file",
		Short: "Prints the identifiers of the segments from the specified TAR file.",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) > 1 {
				fmt.Fprintf(os.Stderr, "Too many arguments.\n")
				os.Exit(1)
			}
			if len(args) < 1 {
				fmt.Fprintln(os.Stderr, "Too few arguments.\n")
				os.Exit(1)
			}
			if err := forEachMatchingEntry(args[0], isAnySegment, doPrintSegmentNameTo(os.Stdout)); err != nil {
				fmt.Fprintln(os.Stderr, "Unable to print segment IDs: %v.\n", err)
				os.Exit(1)
			}
		},
	}
}

func newSegmentCommand() *cobra.Command {
	f := formatText
	cmd := &cobra.Command{
		Use:   "segment file id",
		Short: "Prints the identifiers of the segments from the specified TAR file.",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) < 2 {
				fmt.Fprintf(os.Stderr, "Too few arguments.\n")
				os.Exit(1)
			}
			if len(args) > 2 {
				fmt.Fprintf(os.Stderr, "Too many arguments.\n")
				os.Exit(1)
			}
			if err := onMatchingEntry(args[0], isSegment(args[1]), doPrintSegment(f, os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print segment: %v.\n", err)
				os.Exit(1)
			}
		},
	}
	cmd.Flags().Var(&f, "format", "Output format (text, hex)")
	return cmd
}

func newIndexCommand() *cobra.Command {
	f := formatText
	cmd := &cobra.Command{
		Use:   "index",
		Short: "Prints the index from the specified TAR file",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) > 1 {
				fmt.Fprintf(os.Stderr, "Too many arguments.\n")
				os.Exit(1)
			}
			if len(args) < 1 {
				fmt.Fprintln(os.Stderr, "Too few arguments.\n")
				os.Exit(1)
			}
			if err := onMatchingEntry(args[0], isIndex, doPrintIndex(f, os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print the index: %v.\n", err)
				os.Exit(1)
			}
		},
	}
	cmd.Flags().Var(&f, "format", "Output format (text, hex)")
	return cmd
}

func newGraphCommand() *cobra.Command {
	f := formatText
	cmd := &cobra.Command{
		Use:   "graph",
		Short: "Prints the graph from the specified TAR file",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) > 1 {
				fmt.Fprintf(os.Stderr, "Too many arguments.\n")
				os.Exit(1)
			}
			if len(args) < 1 {
				fmt.Fprintln(os.Stderr, "Too few arguments.\n")
				os.Exit(1)
			}
			if err := onMatchingEntry(args[0], isGraph, doPrintGraph(f, os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print the graph: %v.\n", err)
				os.Exit(1)
			}
		},
	}
	cmd.Flags().Var(&f, "format", "Output format (text, hex)")
	return cmd
}

func newBinariesCommand() *cobra.Command {
	f := formatText
	cmd := &cobra.Command{
		Use:   "binaries",
		Short: "Prints the index of binary references from the specified TAR file",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) > 1 {
				fmt.Fprintf(os.Stderr, "Too many arguments.\n")
				os.Exit(1)
			}
			if len(args) < 1 {
				fmt.Fprintln(os.Stderr, "Too few arguments.\n")
				os.Exit(1)
			}
			if err := onMatchingEntry(args[0], isBinary, doPrintBinaries(f, os.Stdout)); err != nil {
				fmt.Fprintln(os.Stderr, "Unable to print the index of binary references: %v.\n", err)
				os.Exit(1)
			}
		},
	}
	cmd.Flags().Var(&f, "format", "Output format (text, hex)")
	return cmd
}

type format string

const (
	formatText format = "text"
	formatHex  format = "hex"
)

func (f *format) String() string {
	return string(*f)
}

func (f *format) Set(s string) error {
	switch format(s) {
	case formatHex:
		*f = formatHex
	case formatText:
		*f = formatText
	default:
		return fmt.Errorf("Invalid format '%s'", s)
	}
	return nil
}

func (f *format) Type() string {
	return "format"
}
<commit_msg>Fix formatting of output messages<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

func main() {
	if err := newRootCommand().Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v", err)
		os.Exit(1)
	}
}

func newRootCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "sdb [command]",
		Short: "SDB is collection of utilities for Apache Jackrabbit Oak's Segment Store",
	}
	cmd.AddCommand(newTarsCommand())
	cmd.AddCommand(newEntriesCommand())
	cmd.AddCommand(newSegmentsCommand())
	cmd.AddCommand(newSegmentCommand())
	cmd.AddCommand(newIndexCommand())
	cmd.AddCommand(newGraphCommand())
	cmd.AddCommand(newBinariesCommand())
	return cmd
}

func newTarsCommand() *cobra.Command {
	var all bool
	cmd := &cobra.Command{
		Use:   "tars [dir]",
		Short: "Prints the TAR files at the provided path.",
		Run: func(cmd *cobra.Command, args []string) {
			directory, err := os.Getwd()
			if err != nil {
				fmt.Fprintf(os.Stderr, "Unable to determine the working directory: %v.\n", err)
				os.Exit(1)
			}
			if len(args) > 1 {
				fmt.Fprintf(os.Stderr, "Too many arguments.\n")
				os.Exit(1)
			}
			if len(args) == 1 {
				directory = args[0]
			}
			if err := forEachTarFile(directory, all, doPrintTo(os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print TAR files: %v.\n", err)
				os.Exit(1)
			}
		},
	}
	cmd.Flags().BoolVar(&all, "all", false, "List both active and non-active TAR files")
	return cmd
}

func newEntriesCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "entries file",
		Short: "Prints the entries from the specified TAR file.",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) > 1 {
				fmt.Fprintf(os.Stderr, "Too many arguments.\n")
				os.Exit(1)
			}
			if len(args) < 1 {
				fmt.Fprintf(os.Stderr, "Too few arguments.\n")
				os.Exit(1)
			}
			if err := forEachEntry(args[0], doPrintNameTo(os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print TAR entries: %v.\n", err)
				os.Exit(1)
			}
		},
	}
}

func newSegmentsCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "segments file",
		Short: "Prints the identifiers of the segments from the specified TAR file.",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) > 1 {
				fmt.Fprintln(os.Stderr, "Too many arguments.")
				os.Exit(1)
			}
			if len(args) < 1 {
				fmt.Fprintln(os.Stderr, "Too few arguments.")
				os.Exit(1)
			}
			if err := forEachMatchingEntry(args[0], isAnySegment, doPrintSegmentNameTo(os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print segment IDs: %v.\n", err)
				os.Exit(1)
			}
		},
	}
}

func newSegmentCommand() *cobra.Command {
	f := formatText
	cmd := &cobra.Command{
		Use:   "segment file id",
		Short: "Prints the identifiers of the segments from the specified TAR file.",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) < 2 {
				fmt.Fprintf(os.Stderr, "Too few arguments.\n")
				os.Exit(1)
			}
			if len(args) > 2 {
				fmt.Fprintf(os.Stderr, "Too many arguments.\n")
				os.Exit(1)
			}
			if err := onMatchingEntry(args[0], isSegment(args[1]), doPrintSegment(f, os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print segment: %v.\n", err)
				os.Exit(1)
			}
		},
	}
	cmd.Flags().Var(&f, "format", "Output format (text, hex)")
	return cmd
}

func newIndexCommand() *cobra.Command {
	f := formatText
	cmd := &cobra.Command{
		Use:   "index",
		Short: "Prints the index from the specified TAR file",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) > 1 {
				fmt.Fprintln(os.Stderr, "Too many arguments.")
				os.Exit(1)
			}
			if len(args) < 1 {
				fmt.Fprintln(os.Stderr, "Too few arguments.")
				os.Exit(1)
			}
			if err := onMatchingEntry(args[0], isIndex, doPrintIndex(f, os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print the index: %v.\n", err)
				os.Exit(1)
			}
		},
	}
	cmd.Flags().Var(&f, "format", "Output format (text, hex)")
	return cmd
}

func newGraphCommand() *cobra.Command {
	f := formatText
	cmd := &cobra.Command{
		Use:   "graph",
		Short: "Prints the graph from the specified TAR file",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) > 1 {
				fmt.Fprintln(os.Stderr, "Too many arguments.")
				os.Exit(1)
			}
			if len(args) < 1 {
				fmt.Fprintln(os.Stderr, "Too few arguments.")
				os.Exit(1)
			}
			if err := onMatchingEntry(args[0], isGraph, doPrintGraph(f, os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print the graph: %v.\n", err)
				os.Exit(1)
			}
		},
	}
	cmd.Flags().Var(&f, "format", "Output format (text, hex)")
	return cmd
}

func newBinariesCommand() *cobra.Command {
	f := formatText
	cmd := &cobra.Command{
		Use:   "binaries",
		Short: "Prints the index of binary references from the specified TAR file",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) > 1 {
				fmt.Fprintln(os.Stderr, "Too many arguments.")
				os.Exit(1)
			}
			if len(args) < 1 {
				fmt.Fprintln(os.Stderr, "Too few arguments.")
				os.Exit(1)
			}
			if err := onMatchingEntry(args[0], isBinary, doPrintBinaries(f, os.Stdout)); err != nil {
				fmt.Fprintf(os.Stderr, "Unable to print the index of binary references: %v.\n", err)
				os.Exit(1)
			}
		},
	}
	cmd.Flags().Var(&f, "format", "Output format (text, hex)")
	return cmd
}

type format string

const (
	formatText format = "text"
	formatHex  format = "hex"
)

func (f *format) String() string {
	return string(*f)
}

func (f *format) Set(s string) error {
	switch format(s) {
	case formatHex:
		*f = formatHex
	case formatText:
		*f = formatText
	default:
		return fmt.Errorf("Invalid format '%s'", s)
	}
	return nil
}

func (f *format) Type() string {
	return "format"
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"net/http"
	"time"

	"github.com/centralmarinsoccer/teams/geocode"
	"github.com/centralmarinsoccer/teams/handler"
	"github.com/centralmarinsoccer/teams/teamsnap"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/gorilla/mux"
)

const defaultPort = 8080
const urlPath = "/teams/"
const defaultInterval = 60

// Environment contains all of the required and optional environment variables
type Environment struct {
	Token           string
	Division        int
	URL             string
	RefreshInterval time.Duration
	GoogleAPIKey	string
}

func main() {
	// Make sure we have the appropriate environment variables
	var env Environment
	var ok bool
	if env, ok = ValidateEnvs(); !ok {
		os.Exit(-2)
	}

	// Create a geocoder
	geocoder := geocode.New(env.GoogleAPIKey)

	ts, err := teamsnap.New(&teamsnap.Configuration{
		Division:      env.Division,
		Token:         env.Token,
		Geocoder:  geocoder,
	})
	if err != nil {
		log.Printf("Failed to create a new TeamSnap. Error: %v\n", err)
		os.Exit(-1)
	}

	// create a channel to update TeamSnap data
	update := make(chan bool)

	// Setup our HTTP Server
	h, err := handler.New(ts, update)
	if err != nil {
		log.Printf("Handlers error: %v\n", err)
		os.Exit(-2)
	}

	//mux.Handle(urlPath, h)
	//mux.Handle("/metrics", prometheus.Handler()) // Add Metrics Handler

	r := mux.NewRouter()
	r.HandleFunc("/teams/", h)
	r.HandleFunc("/teams/{ID}", h)
	r.Handle("/metrics", prometheus.Handler()) // Add Metrics Handler

	log.Printf("Starting up server at %s%s with data refresh interval of %d for TeamSnap division %d\n", env.URL, urlPath, env.RefreshInterval, env.Division)

	ticker := time.NewTicker(env.RefreshInterval * time.Minute)
	go func() {
		for {
			select {
			case <- ticker.C:
				log.Println("Updating data")
				if ok := ts.Update(); ok {
					update <- true
					log.Println("Complete")
				} else {
					log.Println("Failed to update data")
				}
			}
		}
	}()

//	http.ListenAndServe(env.URL, mux)
	http.ListenAndServe(env.URL, r)

}

// ValidateEnvs checks to make sure the necessary environment variables are set
func ValidateEnvs() (Environment, bool) {

	var env Environment
	state := true

	// Required Parameters
	var ok bool
	if env.Division, ok = getEnvInt("DIVISION", 0, true); !ok {
		state = false
	}
	if env.Token, ok = getEnvString("TOKEN", "", true); !ok {
		state = false
	}

	var port int
	var domain string
	if port, ok = getEnvInt("PORT", defaultPort, false); !ok {
		state = false
	}
	if domain, ok = getEnvString("DOMAIN", "", false); !ok {
		state = false
	}

	if len(domain) != 0 {
		domain = "http://" + domain
	}
	env.URL = fmt.Sprintf("%s:%d", domain, port)

	var interval int
	if interval, ok = getEnvInt("REFRESHINTERVAL", defaultInterval, false); ok {
		env.RefreshInterval = time.Duration(interval)
	} else {
		state = false
	}

	if env.GoogleAPIKey, ok = getEnvString("GOOGLE_API_KEY", "", true); !ok {
		state = false
	}

	return env, state
}

func getEnvString(name string, defaultVal string, required bool) (string, bool) {
	val := os.Getenv(name)
	if val == "" {
		if required {
			log.Printf("Missing required environment variable '%s'\n", name)
			return "", false
		}
		return defaultVal, true
	}

	return val, true
}

func getEnvInt(name string, defaultVal int, required bool) (int, bool) {

	if val, ok := getEnvString(name, "", required); ok {
		if val == "" {
			return defaultVal, true
		}

		i, err := strconv.Atoi(val)
		if err != nil {
			log.Printf("%s's value %s is not an integer.\n", name, val)
			return -1, false
		}
		return i, true
	}

	return -1, false
}
<commit_msg>Now serves static files<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"net/http"
	"time"

	"github.com/centralmarinsoccer/teams/geocode"
	"github.com/centralmarinsoccer/teams/handler"
	"github.com/centralmarinsoccer/teams/teamsnap"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/gorilla/mux"
)

const defaultPort = 8080
const urlPath = "/teams/"
const defaultInterval = 60

// Environment contains all of the required and optional environment variables
type Environment struct {
	Token           string
	Division        int
	URL             string
	RefreshInterval time.Duration
	GoogleAPIKey	string
}

func main() {
	// Make sure we have the appropriate environment variables
	var env Environment
	var ok bool
	if env, ok = ValidateEnvs(); !ok {
		os.Exit(-2)
	}

	// Create a geocoder
	geocoder := geocode.New(env.GoogleAPIKey)

	ts, err := teamsnap.New(&teamsnap.Configuration{
		Division:      env.Division,
		Token:         env.Token,
		Geocoder:  geocoder,
	})
	if err != nil {
		log.Printf("Failed to create a new TeamSnap. Error: %v\n", err)
		os.Exit(-1)
	}

	// create a channel to update TeamSnap data
	update := make(chan bool)

	// Setup our HTTP Server
	h, err := handler.New(ts, update)
	if err != nil {
		log.Printf("Handlers error: %v\n", err)
		os.Exit(-2)
	}

	r := mux.NewRouter()
	r.HandleFunc("/teams/", h)
	r.HandleFunc("/teams/{ID}", h)
	r.Handle("/metrics", prometheus.Handler()) // Add Metrics Handler
	r.PathPrefix("/teams/static/").Handler(http.StripPrefix("/teams/static/", http.FileServer(http.Dir("static"))))

	// Setup timer to refresh TeamSnap data
	ticker := time.NewTicker(env.RefreshInterval * time.Minute)
	go func() {
		for {
			select {
			case <- ticker.C:
				log.Println("Updating team data")
				if ok := ts.Update(); ok {
					update <- true
					log.Println("Complete")
				} else {
					log.Println("Failed to update data")
				}
			}
		}
	}()

	log.Printf("Starting up server at %s%s with data refresh interval of %d for TeamSnap division %d\n", env.URL, urlPath, env.RefreshInterval, env.Division)
	srv := &http.Server{
		Handler:      r,
		Addr:         env.URL,
		// Good practice: enforce timeouts for servers you create!
		WriteTimeout: 5 * time.Second,
		ReadTimeout:  5 * time.Second,
	}

	log.Fatal(srv.ListenAndServe())
}

// ValidateEnvs checks to make sure the necessary environment variables are set
func ValidateEnvs() (Environment, bool) {

	var env Environment
	state := true

	// Required Parameters
	var ok bool
	if env.Division, ok = getEnvInt("DIVISION", 0, true); !ok {
		state = false
	}
	if env.Token, ok = getEnvString("TOKEN", "", true); !ok {
		state = false
	}

	var port int
	var domain string
	if port, ok = getEnvInt("PORT", defaultPort, false); !ok {
		state = false
	}
	if domain, ok = getEnvString("DOMAIN", "", false); !ok {
		state = false
	}

	if len(domain) != 0 {
		domain = "http://" + domain
	}
	env.URL = fmt.Sprintf("%s:%d", domain, port)

	var interval int
	if interval, ok = getEnvInt("REFRESHINTERVAL", defaultInterval, false); ok {
		env.RefreshInterval = time.Duration(interval)
	} else {
		state = false
	}

	if env.GoogleAPIKey, ok = getEnvString("GOOGLE_API_KEY", "", true); !ok {
		state = false
	}

	return env, state
}

func getEnvString(name string, defaultVal string, required bool) (string, bool) {
	val := os.Getenv(name)
	if val == "" {
		if required {
			log.Printf("Missing required environment variable '%s'\n", name)
			return "", false
		}
		return defaultVal, true
	}

	return val, true
}

func getEnvInt(name string, defaultVal int, required bool) (int, bool) {

	if val, ok := getEnvString(name, "", required); ok {
		if val == "" {
			return defaultVal, true
		}

		i, err := strconv.Atoi(val)
		if err != nil {
			log.Printf("%s's value %s is not an integer.\n", name, val)
			return -1, false
		}
		return i, true
	}

	return -1, false
}
<|endoftext|>


<commit_before>package tvdb

// SeriesRequest is used to request additional series information
type SeriesRequest struct {
	SeriesID int `json:"id"`
}

// Series represents a reponse for series information from the TVDB service
type Series struct {
	Added           string   `json:"added"`
	AirsDayOfWeek   string   `json:"airsDayOfWeek"`
	AirsTime        string   `json:"airsTime"`
	Aliases         []string `json:"aliases"`
	Banner          string   `json:"banner"`
	FirstAired      string   `json:"firstAired"`
	Genre           []string `json:"genre"`
	ID              int      `json:"id"`
	ImdbID          string   `json:"imdbId"`
	LastUpdated     int      `json:"lastUpdated"`
	Network         string   `json:"network"`
	NetworkID       string   `json:"networkId"`
	Overview        string   `json:"overview"`
	Rating          string   `json:"rating"`
	Runtime         string   `json:"runtime"`
	SeriesID        string   `json:"seriesId"`
	SeriesName      string   `json:"seriesName"`
	SiteRating      float64  `json:"siteRating"`
	SiteRatingCount int      `json:"siteRatingCount"`
	Status          string   `json:"status"`
	Zap2itID        string   `json:"zap2itId"`
}

// SeriesData represents the list of responses to get series information
type SeriesData struct {
	Data   Series     `json:"data"`
	Errors JSONErrors `json:"errors,omitempty"`
}

// SeriesEpisodesRequest represents a request to get episode information for a series
type SeriesEpisodesRequest struct {
	SeriesID int

	/* Query parameters */
	AiredEpisode int `json:"airedEpisode"`
	AiredSeason  int `json:"airedSeason"`
	DVDEpisode   int `json:"dvdEpisode"`
	DVDSeason    int `json:"dvdSeason"`
	IMDBId       int `json:"imdbId"`
}

// BasicEpisode represents a reponse for episode information from the TVDB service
type BasicEpisode struct {
	AbsoluteNumber     int    `json:"absoluteNumber"`
	AiredEpisodeNumber int    `json:"airedEpisodeNumber"`
	AiredSeason        int    `json:"airedSeason"`
	AiredSeasonID      int    `json:"airedSeasonID"`
	DVDEpisodeNumber   int    `json:"dvdEpisodeNumber"`
	DVDSeason          int    `json:"dvdSeason"`
	EpisodeName        string `json:"episodeName"`
	FirstAired         string `json:"firstAired"`
	ID                 int    `json:"id"`
	LastUpdated        int    `json:"lastUpdated"`
	Overview           string `json:"overview"`
}

// Links represents the paging information for multiple episode responses
type Links struct {
	FirstPage    int `json:"first"`
	LastPage     int `json:"last"`
	NextPage     int `json:"next"`
	PreviousPage int `json:"prev"`
}

// SeriesEpisodes represents the list of responses to get episode information
type SeriesEpisodes struct {
	Links  Links          `json:"links"`
	Data   []BasicEpisode `json:"data"`
	Errors JSONErrors     `json:"errors,omitempty"`
}

// SeriesEpisodesSummary Returns a summary of the episodes and seasons available for the series.
type SeriesEpisodesSummary struct {
	// Number of all aired episodes for this series
	AiredEpisodes string   `json:"airedEpisodes,omitempty"`
	AiredSeasons  []string `json:"airedSeasons,omitempty"`
	// Number of all dvd episodes for this series
	DvdEpisodes string   `json:"dvdEpisodes,omitempty"`
	DvdSeasons  []string `json:"dvdSeasons,omitempty"`
}

// SeriesActorsData contains information about a single actor
type SeriesActorsData struct {
	ID          int    `json:"id"`
	Image       string `json:"image"`
	ImageAdded  string `json:"imageAdded"`
	ImageAuthor int    `json:"imageAuthor"`
	LastUpdated string `json:"lastUpdated"`
	Name        string `json:"name"`
	Role        string `json:"role"`
	SeriesID    int    `json:"seriesId"`
	SortOrder   int    `json:"sortOrder"`
}

// SeriesActors is the response of the api when asking for authors
type SeriesActors struct {
	Data []SeriesActorsData `json:"data"`
}

// SeriesImageQueryRequest used to query images for a given series and type
type SeriesImageQueryRequest struct {
	SeriesID   int    `json:"id"`
	KeyType    string `json:"keyType"`
	Resulution string `json:"resulution"`
	SubKey     string `json:"subKey"`
}

// SeriesImagesCount one single image response
type SeriesImagesCount struct {
	FileName    string             `json:"fileName"`
	ID          int                `json:"id"`
	KeyType     string             `json:"keyType"`
	LanguageID  int                `json:"languageId"`
	RatingsInfo map[string]float64 `json:"ratingsInfo"`
	Resolution  string             `json:"resulution"`
	SubKey      string             `json:"subKey"`
	Thumbnail   string             `json:"thumbnail"`
}

// SeriesImagesCounts is the response of the api when asking for images
type SeriesImagesCounts struct {
	Data []SeriesImagesCount `json:"data"`
}
<commit_msg>Update series.go<commit_after>package tvdb

// SeriesRequest is used to request additional series information
type SeriesRequest struct {
	SeriesID int `json:"id"`
}

// Series represents a response for series information from the TVDB service
type Series struct {
	Added           string   `json:"added"`
	AirsDayOfWeek   string   `json:"airsDayOfWeek"`
	AirsTime        string   `json:"airsTime"`
	Aliases         []string `json:"aliases"`
	Banner          string   `json:"banner"`
	FirstAired      string   `json:"firstAired"`
	Genre           []string `json:"genre"`
	ID              int      `json:"id"`
	ImdbID          string   `json:"imdbId"`
	LastUpdated     int      `json:"lastUpdated"`
	Network         string   `json:"network"`
	NetworkID       string   `json:"networkId"`
	Overview        string   `json:"overview"`
	Rating          string   `json:"rating"`
	Runtime         string   `json:"runtime"`
	SeriesID        string   `json:"seriesId"`
	SeriesName      string   `json:"seriesName"`
	SiteRating      float64  `json:"siteRating"`
	SiteRatingCount int      `json:"siteRatingCount"`
	Status          string   `json:"status"`
	Zap2itID        string   `json:"zap2itId"`
}

// SeriesData represents the list of responses to get series information
type SeriesData struct {
	Data   Series     `json:"data"`
	Errors JSONErrors `json:"errors,omitempty"`
}

// SeriesEpisodesRequest represents a request to get episode information for a series
type SeriesEpisodesRequest struct {
	SeriesID int

	/* Query parameters */
	AiredEpisode int `json:"airedEpisode"`
	AiredSeason  int `json:"airedSeason"`
	DVDEpisode   int `json:"dvdEpisode"`
	DVDSeason    int `json:"dvdSeason"`
	IMDBId       int `json:"imdbId"`
}

// BasicEpisode represents a response for episode information from the TVDB service
type BasicEpisode struct {
	AbsoluteNumber     int    `json:"absoluteNumber"`
	AiredEpisodeNumber int    `json:"airedEpisodeNumber"`
	AiredSeason        int    `json:"airedSeason"`
	AiredSeasonID      int    `json:"airedSeasonID"`
	DVDEpisodeNumber   int    `json:"dvdEpisodeNumber"`
	DVDSeason          int    `json:"dvdSeason"`
	EpisodeName        string `json:"episodeName"`
	FirstAired         string `json:"firstAired"`
	ID                 int    `json:"id"`
	LastUpdated        int    `json:"lastUpdated"`
	Overview           string `json:"overview"`
}

// Links represents the paging information for multiple episode responses
type Links struct {
	FirstPage    int `json:"first"`
	LastPage     int `json:"last"`
	NextPage     int `json:"next"`
	PreviousPage int `json:"prev"`
}

// SeriesEpisodes represents the list of responses to get episode information
type SeriesEpisodes struct {
	Links  Links          `json:"links"`
	Data   []BasicEpisode `json:"data"`
	Errors JSONErrors     `json:"errors,omitempty"`
}

// SeriesEpisodesSummary Returns a summary of the episodes and seasons available for the series.
type SeriesEpisodesSummary struct {
	// Number of all aired episodes for this series
	AiredEpisodes string   `json:"airedEpisodes,omitempty"`
	AiredSeasons  []string `json:"airedSeasons,omitempty"`
	// Number of all dvd episodes for this series
	DvdEpisodes string   `json:"dvdEpisodes,omitempty"`
	DvdSeasons  []string `json:"dvdSeasons,omitempty"`
}

// SeriesActorsData contains information about a single actor
type SeriesActorsData struct {
	ID          int    `json:"id"`
	Image       string `json:"image"`
	ImageAdded  string `json:"imageAdded"`
	ImageAuthor int    `json:"imageAuthor"`
	LastUpdated string `json:"lastUpdated"`
	Name        string `json:"name"`
	Role        string `json:"role"`
	SeriesID    int    `json:"seriesId"`
	SortOrder   int    `json:"sortOrder"`
}

// SeriesActors is the response of the api when asking for authors
type SeriesActors struct {
	Data []SeriesActorsData `json:"data"`
}

// SeriesImageQueryRequest used to query images for a given series and type
type SeriesImageQueryRequest struct {
	SeriesID   int    `json:"id"`
	KeyType    string `json:"keyType"`
	Resulution string `json:"resulution"`
	SubKey     string `json:"subKey"`
}

// SeriesImagesCount one single image response
type SeriesImagesCount struct {
	FileName    string             `json:"fileName"`
	ID          int                `json:"id"`
	KeyType     string             `json:"keyType"`
	LanguageID  int                `json:"languageId"`
	RatingsInfo map[string]float64 `json:"ratingsInfo"`
	Resolution  string             `json:"resulution"`
	SubKey      string             `json:"subKey"`
	Thumbnail   string             `json:"thumbnail"`
}

// SeriesImagesCounts is the response of the api when asking for images
type SeriesImagesCounts struct {
	Data []SeriesImagesCount `json:"data"`
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/robvanmieghem/go-opencl/cl"
	"github.com/robvanmieghem/gominer/clients"
)

//Version is the released version string of gominer
var Version = "0.5-Dev"

var intensity = 28
var devicesTypesForMining = cl.DeviceTypeGPU

const maxUint32 = int64(^uint32(0))

func createWork(siaclient clients.SiaClient, miningWorkChannel chan *MiningWork, nrOfMiningDevices int, globalItemSize int) {
	siaclient.Start()
	for {
		target, header, jobStillValid, job, err := siaclient.GetHeaderForWork()

		if err != nil {
			log.Println("ERROR fetching work -", err)
			time.Sleep(1000 * time.Millisecond)
			continue
		}
		//copy target to header
		for i := 0; i < 8; i++ {
			header[i+32] = target[7-i]
		}
		//Fill the workchannel with work
		// Only generate nonces for a 32 bit space (since gpu's are mostly 32 bit)
		for i := int64(0); i*int64(globalItemSize) < (maxUint32 - int64(globalItemSize)); i++ {
			//Do not continue mining the 32 bit nonce space if the current job is deprecated
			select {
			case <-jobStillValid:
			default:
			}

			miningWorkChannel <- &MiningWork{header, int(i) * globalItemSize, job}
		}
	}
}

func main() {
	printVersion := flag.Bool("v", false, "Show version and exit")
	useCPU := flag.Bool("cpu", false, "If set, also use the CPU for mining, only GPU's are used by default")
	flag.IntVar(&intensity, "I", intensity, "Intensity")
	siadHost := flag.String("url", "localhost:9980", "siad host and port, for stratum servers, use `stratum+tcp://<host>:<port>`")
	pooluser := flag.String("user", "payoutaddress.rigname", "username, most stratum servers take this in the form [payoutaddress].[rigname]")
	excludedGPUs := flag.String("E", "", "Exclude GPU's: comma separated list of devicenumbers")
	flag.Parse()

	if *printVersion {
		fmt.Println("gominer version", Version)
		os.Exit(0)
	}

	siaclient := clients.NewSiaClient(*siadHost, *pooluser)

	if *useCPU {
		devicesTypesForMining = cl.DeviceTypeAll
	}
	globalItemSize := int(math.Exp2(float64(intensity)))

	platforms, err := cl.GetPlatforms()
	if err != nil {
		log.Panic(err)
	}

	clDevices := make([]*cl.Device, 0, 4)
	for _, platform := range platforms {
		log.Println("Platform", platform.Name())
		platormDevices, err := cl.GetDevices(platform, devicesTypesForMining)
		if err != nil {
			log.Println(err)
		}
		log.Println(len(platormDevices), "device(s) found:")
		for i, device := range platormDevices {
			log.Println(i, "-", device.Type(), "-", device.Name())
			clDevices = append(clDevices, device)
		}
	}

	nrOfMiningDevices := len(clDevices)

	if nrOfMiningDevices == 0 {
		log.Println("No suitable opencl devices found")
		os.Exit(1)
	}

	//Start fetching work
	workChannel := make(chan *MiningWork, nrOfMiningDevices)
	go createWork(siaclient, workChannel, nrOfMiningDevices, globalItemSize)

	//Start mining routines
	var hashRateReportsChannel = make(chan *HashRateReport, nrOfMiningDevices*10)
	for i, device := range clDevices {
		if deviceExcludedForMining(i, *excludedGPUs) {
			continue
		}
		miner := &Miner{
			clDevice:          device,
			minerID:           i,
			hashRateReports:   hashRateReportsChannel,
			miningWorkChannel: workChannel,
			GlobalItemSize:    globalItemSize,
			siad:              siaclient,
		}
		go miner.mine()
	}

	//Start printing out the hashrates of the different gpu's
	hashRateReports := make([]float64, nrOfMiningDevices)
	for {
		//No need to print at every hashreport, we have time
		for i := 0; i < nrOfMiningDevices; i++ {
			report := <-hashRateReportsChannel
			hashRateReports[report.MinerID] = report.HashRate
		}
		fmt.Print("\r")
		var totalHashRate float64
		for minerID, hashrate := range hashRateReports {
			fmt.Printf("%d-%.1f ", minerID, hashrate)
			totalHashRate += hashrate
		}
		fmt.Printf("Total: %.1f MH/s  ", totalHashRate)

	}
}

//deviceExcludedForMining checks if the device is in the exclusion list
func deviceExcludedForMining(deviceID int, excludedGPUs string) bool {
	excludedGPUList := strings.Split(excludedGPUs, ",")
	for _, excludedGPU := range excludedGPUList {
		if strconv.Itoa(deviceID) == excludedGPU {
			return true
		}
	}
	return false
}
<commit_msg>Forgot to break out<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/robvanmieghem/go-opencl/cl"
	"github.com/robvanmieghem/gominer/clients"
)

//Version is the released version string of gominer
var Version = "0.5-Dev"

var intensity = 28
var devicesTypesForMining = cl.DeviceTypeGPU

const maxUint32 = int64(^uint32(0))

func createWork(siaclient clients.SiaClient, miningWorkChannel chan *MiningWork, nrOfMiningDevices int, globalItemSize int) {
	siaclient.Start()
	for {
		target, header, deprecationChannel, job, err := siaclient.GetHeaderForWork()

		if err != nil {
			log.Println("ERROR fetching work -", err)
			time.Sleep(1000 * time.Millisecond)
			continue
		}
		//copy target to header
		for i := 0; i < 8; i++ {
			header[i+32] = target[7-i]
		}
		//Fill the workchannel with work
		// Only generate nonces for a 32 bit space (since gpu's are mostly 32 bit)
		for i := int64(0); i*int64(globalItemSize) < (maxUint32 - int64(globalItemSize)); i++ {
			//Do not continue mining the 32 bit nonce space if the current job is deprecated
			select {
			case <-deprecationChannel:
				break
			default:
			}

			miningWorkChannel <- &MiningWork{header, int(i) * globalItemSize, job}
		}
	}
}

func main() {
	printVersion := flag.Bool("v", false, "Show version and exit")
	useCPU := flag.Bool("cpu", false, "If set, also use the CPU for mining, only GPU's are used by default")
	flag.IntVar(&intensity, "I", intensity, "Intensity")
	siadHost := flag.String("url", "localhost:9980", "siad host and port, for stratum servers, use `stratum+tcp://<host>:<port>`")
	pooluser := flag.String("user", "payoutaddress.rigname", "username, most stratum servers take this in the form [payoutaddress].[rigname]")
	excludedGPUs := flag.String("E", "", "Exclude GPU's: comma separated list of devicenumbers")
	flag.Parse()

	if *printVersion {
		fmt.Println("gominer version", Version)
		os.Exit(0)
	}

	siaclient := clients.NewSiaClient(*siadHost, *pooluser)

	if *useCPU {
		devicesTypesForMining = cl.DeviceTypeAll
	}
	globalItemSize := int(math.Exp2(float64(intensity)))

	platforms, err := cl.GetPlatforms()
	if err != nil {
		log.Panic(err)
	}

	clDevices := make([]*cl.Device, 0, 4)
	for _, platform := range platforms {
		log.Println("Platform", platform.Name())
		platormDevices, err := cl.GetDevices(platform, devicesTypesForMining)
		if err != nil {
			log.Println(err)
		}
		log.Println(len(platormDevices), "device(s) found:")
		for i, device := range platormDevices {
			log.Println(i, "-", device.Type(), "-", device.Name())
			clDevices = append(clDevices, device)
		}
	}

	nrOfMiningDevices := len(clDevices)

	if nrOfMiningDevices == 0 {
		log.Println("No suitable opencl devices found")
		os.Exit(1)
	}

	//Start fetching work
	workChannel := make(chan *MiningWork, nrOfMiningDevices)
	go createWork(siaclient, workChannel, nrOfMiningDevices, globalItemSize)

	//Start mining routines
	var hashRateReportsChannel = make(chan *HashRateReport, nrOfMiningDevices*10)
	for i, device := range clDevices {
		if deviceExcludedForMining(i, *excludedGPUs) {
			continue
		}
		miner := &Miner{
			clDevice:          device,
			minerID:           i,
			hashRateReports:   hashRateReportsChannel,
			miningWorkChannel: workChannel,
			GlobalItemSize:    globalItemSize,
			siad:              siaclient,
		}
		go miner.mine()
	}

	//Start printing out the hashrates of the different gpu's
	hashRateReports := make([]float64, nrOfMiningDevices)
	for {
		//No need to print at every hashreport, we have time
		for i := 0; i < nrOfMiningDevices; i++ {
			report := <-hashRateReportsChannel
			hashRateReports[report.MinerID] = report.HashRate
		}
		fmt.Print("\r")
		var totalHashRate float64
		for minerID, hashrate := range hashRateReports {
			fmt.Printf("%d-%.1f ", minerID, hashrate)
			totalHashRate += hashrate
		}
		fmt.Printf("Total: %.1f MH/s  ", totalHashRate)

	}
}

//deviceExcludedForMining checks if the device is in the exclusion list
func deviceExcludedForMining(deviceID int, excludedGPUs string) bool {
	excludedGPUList := strings.Split(excludedGPUs, ",")
	for _, excludedGPU := range excludedGPUList {
		if strconv.Itoa(deviceID) == excludedGPU {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2014 Rana Ian. All rights reserved.
// Use of this source code is governed by The MIT License
// found in the accompanying LICENSE file.

package main

import (
	"fmt"
	"github.com/ranaian/ora"
)

func main() {
	// example usage of the ora package driver
	// connect to a server and open a session
	env, _ := ora.GetDrv().OpenEnv()
	defer env.Close()
	srv, err := env.OpenSrv("orcl")
	defer srv.Close()
	if err != nil {
		panic(err)
	}
	ses, err := srv.OpenSes("test", "test")
	defer ses.Close()
	if err != nil {
		panic(err)
	}

	// create table
	tableName := "t1"
	stmtTbl, err := ses.Prep(fmt.Sprintf("CREATE TABLE %v "+
		"(C1 NUMBER(19,0) GENERATED ALWAYS AS IDENTITY "+
		"(START WITH 1 INCREMENT BY 1), C2 VARCHAR2(48 CHAR))", tableName))
	defer stmtTbl.Close()
	if err != nil {
		panic(err)
	}
	rowsAffected, err := stmtTbl.Exe()
	if err != nil {
		panic(err)
	}
	fmt.Println(rowsAffected)

	// begin first transaction
	tx1, err := ses.StartTx()
	if err != nil {
		panic(err)
	}

	// insert record
	var id uint64
	str := "Go is expressive, concise, clean, and efficient."
	stmtIns, err := ses.Prep(fmt.Sprintf(
		"INSERT INTO %v (C2) VALUES (:C2) RETURNING C1 INTO :C1", tableName))
	defer stmtIns.Close()
	rowsAffected, err = stmtIns.Exe(str, &id)
	if err != nil {
		panic(err)
	}
	fmt.Println(rowsAffected)

	// insert nullable String slice
	a := make([]ora.String, 4)
	a[0] = ora.String{Value: "Its concurrency mechanisms make it easy to"}
	a[1] = ora.String{IsNull: true}
	a[2] = ora.String{Value: "It's a fast, statically typed, compiled"}
	a[3] = ora.String{Value: "One of Go's key design goals is code"}
	stmtSliceIns, err := ses.Prep(fmt.Sprintf(
		"INSERT INTO %v (C2) VALUES (:C2)", tableName))
	defer stmtSliceIns.Close()
	if err != nil {
		panic(err)
	}
	rowsAffected, err = stmtSliceIns.Exe(a)
	if err != nil {
		panic(err)
	}
	fmt.Println(rowsAffected)

	// fetch records
	stmtQry, err := ses.Prep(fmt.Sprintf(
		"SELECT C1, C2 FROM %v", tableName))
	defer stmtQry.Close()
	if err != nil {
		panic(err)
	}
	rset, err := stmtQry.Qry()
	if err != nil {
		panic(err)
	}
	for rset.Next() {
		fmt.Println(rset.Row[0], rset.Row[1])
	}
	if rset.Err != nil {
		panic(rset.Err)
	}

	// commit first transaction
	err = tx1.Commit()
	if err != nil {
		panic(err)
	}

	// begin second transaction
	tx2, err := ses.StartTx()
	if err != nil {
		panic(err)
	}
	// insert null String
	nullableStr := ora.String{IsNull: true}
	stmtTrans, err := ses.Prep(fmt.Sprintf(
		"INSERT INTO %v (C2) VALUES (:C2)", tableName))
	defer stmtTrans.Close()
	if err != nil {
		panic(err)
	}
	rowsAffected, err = stmtTrans.Exe(nullableStr)
	if err != nil {
		panic(err)
	}
	fmt.Println(rowsAffected)
	// rollback second transaction
	err = tx2.Rollback()
	if err != nil {
		panic(err)
	}

	// fetch and specify return type
	stmtCount, err := ses.Prep(fmt.Sprintf(
		"SELECT COUNT(C1) FROM %v WHERE C2 IS NULL", tableName), ora.U8)
	defer stmtCount.Close()
	if err != nil {
		panic(err)
	}
	rset, err = stmtCount.Qry()
	if err != nil {
		panic(err)
	}
	row := rset.NextRow()
	if row != nil {
		fmt.Println(row[0])
	}
	if rset.Err != nil {
		panic(rset.Err)
	}

	// create stored procedure with sys_refcursor
	stmtProcCreate, err := ses.Prep(fmt.Sprintf(
		"CREATE OR REPLACE PROCEDURE PROC1(P1 OUT SYS_REFCURSOR) AS BEGIN "+
			"OPEN P1 FOR SELECT C1, C2 FROM %v WHERE C1 > 2 ORDER BY C1; "+
			"END PROC1;",
		tableName))
	defer stmtProcCreate.Close()
	rowsAffected, err = stmtProcCreate.Exe()
	if err != nil {
		panic(err)
	}

	// call stored procedure
	// pass *Rset to Exe to receive the results of a sys_refcursor
	stmtProcCall, err := ses.Prep("CALL PROC1(:1)")
	defer stmtProcCall.Close()
	if err != nil {
		panic(err)
	}
	procRset := &ora.Rset{}
	rowsAffected, err = stmtProcCall.Exe(procRset)
	if err != nil {
		panic(err)
	}
	if procRset.IsOpen() {
		for procRset.Next() {
			fmt.Println(procRset.Row[0], procRset.Row[1])
		}
		if procRset.Err != nil {
			panic(procRset.Err)
		}
		fmt.Println(procRset.Len())
	}

	// Output:
	// 0
	// 1
	// 4
	// 1 Go is expressive, concise, clean, and efficient.
	// 2 Its concurrency mechanisms make it easy to
	// 3 <empty>
	// 4 It's a fast, statically typed, compiled
	// 5 One of Go's key design goals is code
	// 1
	// 1
	// 3 <empty>
	// 4 It's a fast, statically typed, compiled
	// 5 One of Go's key design goals is code
	// 3
}
<commit_msg>revised sample code<commit_after>// Copyright 2014 Rana Ian. All rights reserved.
// Use of this source code is governed by The MIT License
// found in the accompanying LICENSE file.

package main

import (
	"fmt"
	"github.com/ranaian/ora"
)

func main() {
	// example usage of the ora package driver
	// connect to a server and open a session
	env, err := ora.GetDrv().OpenEnv()
	defer env.Close()
	if err != nil {
		panic(err)
	}
	srv, err := env.OpenSrv("orcl")
	defer srv.Close()
	if err != nil {
		panic(err)
	}
	ses, err := srv.OpenSes("test", "test")
	defer ses.Close()
	if err != nil {
		panic(err)
	}

	// create table
	tableName := "t1"
	stmtTbl, err := ses.Prep(fmt.Sprintf("CREATE TABLE %v "+
		"(C1 NUMBER(19,0) GENERATED ALWAYS AS IDENTITY "+
		"(START WITH 1 INCREMENT BY 1), C2 VARCHAR2(48 CHAR))", tableName))
	defer stmtTbl.Close()
	if err != nil {
		panic(err)
	}
	rowsAffected, err := stmtTbl.Exe()
	if err != nil {
		panic(err)
	}
	fmt.Println(rowsAffected)

	// begin first transaction
	tx1, err := ses.StartTx()
	if err != nil {
		panic(err)
	}

	// insert record
	var id uint64
	str := "Go is expressive, concise, clean, and efficient."
	stmtIns, err := ses.Prep(fmt.Sprintf(
		"INSERT INTO %v (C2) VALUES (:C2) RETURNING C1 INTO :C1", tableName))
	defer stmtIns.Close()
	rowsAffected, err = stmtIns.Exe(str, &id)
	if err != nil {
		panic(err)
	}
	fmt.Println(rowsAffected)

	// insert nullable String slice
	a := make([]ora.String, 4)
	a[0] = ora.String{Value: "Its concurrency mechanisms make it easy to"}
	a[1] = ora.String{IsNull: true}
	a[2] = ora.String{Value: "It's a fast, statically typed, compiled"}
	a[3] = ora.String{Value: "One of Go's key design goals is code"}
	stmtSliceIns, err := ses.Prep(fmt.Sprintf(
		"INSERT INTO %v (C2) VALUES (:C2)", tableName))
	defer stmtSliceIns.Close()
	if err != nil {
		panic(err)
	}
	rowsAffected, err = stmtSliceIns.Exe(a)
	if err != nil {
		panic(err)
	}
	fmt.Println(rowsAffected)

	// fetch records
	stmtQry, err := ses.Prep(fmt.Sprintf(
		"SELECT C1, C2 FROM %v", tableName))
	defer stmtQry.Close()
	if err != nil {
		panic(err)
	}
	rset, err := stmtQry.Qry()
	if err != nil {
		panic(err)
	}
	for rset.Next() {
		fmt.Println(rset.Row[0], rset.Row[1])
	}
	if rset.Err != nil {
		panic(rset.Err)
	}

	// commit first transaction
	err = tx1.Commit()
	if err != nil {
		panic(err)
	}

	// begin second transaction
	tx2, err := ses.StartTx()
	if err != nil {
		panic(err)
	}
	// insert null String
	nullableStr := ora.String{IsNull: true}
	stmtTrans, err := ses.Prep(fmt.Sprintf(
		"INSERT INTO %v (C2) VALUES (:C2)", tableName))
	defer stmtTrans.Close()
	if err != nil {
		panic(err)
	}
	rowsAffected, err = stmtTrans.Exe(nullableStr)
	if err != nil {
		panic(err)
	}
	fmt.Println(rowsAffected)
	// rollback second transaction
	err = tx2.Rollback()
	if err != nil {
		panic(err)
	}

	// fetch and specify return type
	stmtCount, err := ses.Prep(fmt.Sprintf(
		"SELECT COUNT(C1) FROM %v WHERE C2 IS NULL", tableName), ora.U8)
	defer stmtCount.Close()
	if err != nil {
		panic(err)
	}
	rset, err = stmtCount.Qry()
	if err != nil {
		panic(err)
	}
	row := rset.NextRow()
	if row != nil {
		fmt.Println(row[0])
	}
	if rset.Err != nil {
		panic(rset.Err)
	}

	// create stored procedure with sys_refcursor
	stmtProcCreate, err := ses.Prep(fmt.Sprintf(
		"CREATE OR REPLACE PROCEDURE PROC1(P1 OUT SYS_REFCURSOR) AS BEGIN "+
			"OPEN P1 FOR SELECT C1, C2 FROM %v WHERE C1 > 2 ORDER BY C1; "+
			"END PROC1;",
		tableName))
	defer stmtProcCreate.Close()
	rowsAffected, err = stmtProcCreate.Exe()
	if err != nil {
		panic(err)
	}

	// call stored procedure
	// pass *Rset to Exe to receive the results of a sys_refcursor
	stmtProcCall, err := ses.Prep("CALL PROC1(:1)")
	defer stmtProcCall.Close()
	if err != nil {
		panic(err)
	}
	procRset := &ora.Rset{}
	rowsAffected, err = stmtProcCall.Exe(procRset)
	if err != nil {
		panic(err)
	}
	if procRset.IsOpen() {
		for procRset.Next() {
			fmt.Println(procRset.Row[0], procRset.Row[1])
		}
		if procRset.Err != nil {
			panic(procRset.Err)
		}
		fmt.Println(procRset.Len())
	}

	// Output:
	// 0
	// 1
	// 4
	// 1 Go is expressive, concise, clean, and efficient.
	// 2 Its concurrency mechanisms make it easy to
	// 3 <empty>
	// 4 It's a fast, statically typed, compiled
	// 5 One of Go's key design goals is code
	// 1
	// 1
	// 3 <empty>
	// 4 It's a fast, statically typed, compiled
	// 5 One of Go's key design goals is code
	// 3
}
<|endoftext|>


<commit_before>package steamscreenshots

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/big"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

type Settings struct {
	RemoteDirectory string
	Address         string
	AppidOverrides  []struct {
		Appid string `json:"id"`
		Name  string `json:"name"`
	}
	RefreshInterval int    // In minutes
	ApiKey          string // This will be regenerated if it is empty.
}

var re_gamename = regexp.MustCompile(`<td itemprop="name">(.+?)</td>`)

var (
	gitCommit string
	version   string
)

// Structure of json from steam's servers
type steamapps struct {
	Applist struct {
		Apps []struct {
			Appid uint64 `json:"appid"`
			Name  string `json:"name"`
		} `json:"apps"`
	} `json:"applist"`
}

type Server struct {
	// stats stuff
	startTime time.Time
	lastScan  time.Time

	lastUpdate *time.Time

	settings Settings

	Games      *GameList
	ImageCache *GameImages

	SettingsFile string
}

func (s *Server) Run() {
	fmt.Println("Starting server")

	if len(gitCommit) == 0 {
		gitCommit = "Missing commit hash"
	}

	if len(version) == 0 {
		version = "Missing version info"
	}
	fmt.Printf("%s@%s\n", version, gitCommit)

	s.startTime = time.Now()
	s.Games = NewGameList()

	if err := s.loadSettings(); err != nil {
		fmt.Printf("Error loading settings: %s\n", err)
		return
	}

	if err := init_templates(); err != nil {
		fmt.Printf("Error loading templates: %s\n", err)
		return
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/", s.handler_main)
	mux.HandleFunc("/thumb/", s.handler_thumb)
	mux.HandleFunc("/img/", s.handler_image)
	mux.HandleFunc("/banner/", s.handler_banner)
	mux.HandleFunc("/static/", s.handler_static)
	mux.HandleFunc("/debug/", s.handler_debug)
	mux.HandleFunc("/api/get-cache", s.handler_api_cache)
	mux.HandleFunc("/api/get-games", s.handler_api_games)
	mux.HandleFunc("/api/add-image", s.handler_api_addImage)

	server := &http.Server{
		Addr:           s.settings.Address,
		Handler:        mux,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	var err error
	s.ImageCache, err = LoadImageCache("image.cache")
	if err != nil {
		fmt.Println("Unable to load image.cache: ", err)

		s.ImageCache = NewGameImages()
		err = s.scan(true)
		if err != nil {
			fmt.Println("Initial scan error: ", err)
			return
		}
	} else {
		fmt.Println("Refreshing RemoteDirectory...")
		if err = s.scan(true); err != nil {
			fmt.Println("Error refreshing RemoteDirectory: ", err)
			return
		}
	}
	fmt.Println("Initial scan OK")

	// Fire and forget.  TODO: graceful shutdown
	go func() {
		for {
			time.Sleep(time.Minute * time.Duration(s.settings.RefreshInterval))
			if err := s.scan(false); err != nil {
				fmt.Printf("Error scanning: %s", err)
			}
		}
	}()

	// Generate a new API key if it's empty
	if s.settings.ApiKey == "" {
		out := ""
		large := big.NewInt(int64(1 << 60))
		large = large.Add(large, large)
		for len(out) < 50 {
			num, err := rand.Int(rand.Reader, large)
			if err != nil {
				panic("Error generating session key: " + err.Error())
			}
			out = fmt.Sprintf("%s%X", out, num)
		}
		s.settings.ApiKey = out
		fmt.Println("New API key generated: " + s.settings.ApiKey)
	} else {
		fmt.Printf("using API key in config: %q\n", s.settings.ApiKey)
	}

	go func(s *Server) {
		for {
			s.ImageCache.Save("image.cache")
			time.Sleep(10 * time.Minute)
		}
	}(s)

	fmt.Println("Listening on address: " + s.settings.Address)
	fmt.Println("Fisnished startup.")
	server.ListenAndServe()
}

// TODO: use GameImages.FullScan for this?
func (s *Server) scan(printOutput bool) error {
	s.lastScan = time.Now()

	if printOutput {
		fmt.Printf("Scanning %q\n", s.settings.RemoteDirectory)
	}

	dir, err := filepath.Glob(filepath.Join(s.settings.RemoteDirectory, "*"))
	if err != nil {
		return fmt.Errorf("Unable to glob RemoteDirectory: %s", err)
	}
	tmpTree := make(map[string][]string)

	for _, d := range dir {
		base := filepath.Base(d)

		// Ignore dotfiles
		if strings.HasPrefix(base, ".") {
			continue
		}

		if printOutput {
			fmt.Printf("[%s] %s\n", base, s.Games.Get(base))
		}

		jpg, err := filepath.Glob(filepath.Join(d, "screenshots", "*.jpg"))
		if err != nil {
			fmt.Printf("JPG glob error in %q: %s", d, err)
			continue
		}
		tmpTree[base] = jpg

		// TODO: merge ImageCache.ScanPath() and ImagePath.RefreshPath(), possibly removing the jpg glob above as well.
		err = s.ImageCache.ScanPath(d)
		if err != nil {
			fmt.Println(err)
		}
	}

	// Write cache to disk after it's updated in-memory so failing this doesn't skip updating.
	if err := s.ImageCache.Save("image.cache"); err != nil {
		return fmt.Errorf("Unable to save image cache: %s\n", err)
	}

	return nil
}

func SliceContains(s []string, val string) bool {
	for _, v := range s {
		if v == val {
			return true
		}
	}
	return false
}

// FIXME: pass the filename in here as an argument
func (s *Server) loadSettings() error {
	settingsFilename := "settings.json"

	if len(s.SettingsFile) > 0 {
		settingsFilename = s.SettingsFile
	}

	settingsFile, err := ioutil.ReadFile(settingsFilename)
	if err != nil {
		return fmt.Errorf("Error reading settings file: %s", err)
	}

	err = json.Unmarshal(settingsFile, &s.settings)
	if err != nil {
		return fmt.Errorf("Error unmarshaling settings: %s", err)
	}

	fmt.Println("Settings loaded")

	if s.settings.RefreshInterval < 1 {
		s.settings.RefreshInterval = 1
	}

	return s.loadGames()
}

func (s *Server) loadGames() error {
	if ex := exists("games.cache"); !ex {
		fmt.Println("games.cache doesn't exist.  Getting a new one.")
		if err := s.updateGamesJson(); err != nil {
			return fmt.Errorf("Unable update game list: %s", err)
		}
	}

	gamesFile, err := ioutil.ReadFile("games.cache")
	if err != nil {
		return fmt.Errorf("Error reading games file: %s", err)
	}

	var games GameIDs
	err = json.Unmarshal(gamesFile, &games)
	if err != nil {
		return fmt.Errorf("Error unmarshaling games: %s", err)
	}

	s.Games.Update(games)
	//fmt.Println("Number of games loaded: ", Games.Length())
	return nil
}

func (s *Server) getGameName(appid string) (string, error) {
	if appid == ".stfolder" {
		return appid, nil
	}

	//fmt.Printf("Getting name for appid %q\n", appid)
	if name := s.Games.Get(appid); name != appid {
		return name, nil
	}

	// Large appid, must be a non-steam game.  This could have some edge cases
	// as non-steam games' appids are CRCs.
	if len(appid) > 18 {
		return s.Games.Set(appid, fmt.Sprintf("Non-Steam game (%s)", appid)), nil
	}

	// TODO: rate limiting/cache age
	if err := s.updateGamesJson(); err == nil {
		if name := s.Games.Get(appid); name != appid {
			return name, nil
		}
	}
	return appid, nil
}

// Update the local cache of appids from steam's servers.
func (s *Server) updateGamesJson() error {
	if s.lastUpdate != nil && time.Since(*s.lastUpdate).Minutes() < 30 {
		//return fmt.Errorf("Cache still good.")
		fmt.Println("Not updating games list; cache still good.")
		return nil
	}

	now := time.Now()
	//fmt.Printf("time.Now(): {}\n", now)
	s.lastUpdate = &now

	fmt.Println("Updating games list")
	resp, err := http.Get("http://api.steampowered.com/ISteamApps/GetAppList/v2")
	if err != nil {
		return fmt.Errorf("Unable to get appid list from steam: %s", err)
	}
	defer resp.Body.Close()

	js, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("Unable to read appid json: %s", err)
	}

	alist := &steamapps{}
	if err := json.Unmarshal(js, alist); err != nil {
		return fmt.Errorf("Unable to unmarshal json: %s", err)
	}

	for _, a := range alist.Applist.Apps {
		id := fmt.Sprintf("%d", a.Appid)
		s.Games.Set(id, a.Name)

	}

	for _, ovr := range s.settings.AppidOverrides {
		s.Games.Set(ovr.Appid, ovr.Name)
		fmt.Printf("Setting override for [%s]: %q\n", ovr.Appid, ovr.Name)
	}

	// save games.cache
	games := s.Games.GetMap()
	marshaled, err := json.Marshal(games)
	if err != nil {
		return fmt.Errorf("Unable to marshal game json: %s", err)
	}

	err = ioutil.WriteFile("games.cache", marshaled, 0644)
	if err != nil {
		return fmt.Errorf("Unable to save games.cache: %s", err)
	}

	fmt.Printf("Finished updating games list.  Appids: %d\n", len(games))
	return nil
}

// Returns a filename
func (s *Server) getGameBanner(appid uint64) (string, error) {
	appstr := fmt.Sprintf("%d", appid)
	if exist := exists("banners/" + appstr + ".jpg"); exist {
		return "banners/" + appstr + ".jpg", nil
	}

	resp, err := http.Get("http://cdn.akamai.steamstatic.com/steam/apps/" + appstr + "/header.jpg")
	if err != nil {
		return "", fmt.Errorf("Unable to DL header: %s", err)
	}

	if resp.StatusCode >= 400 && resp.StatusCode < 500 {
		// Game not found.  Use unknown.

		raw, err := ioutil.ReadFile("banners/unknown.jpg")
		if err != nil {
			return "", fmt.Errorf("Unable to read unknown.jpg")
		}

		if err = ioutil.WriteFile("banners/"+appstr+".jpg", raw, 0777); err != nil {
			return "", fmt.Errorf("Unable to save file: %s", err)
		}

		return "banners/" + appstr + ".jpg", nil
	}

	defer resp.Body.Close()

	file, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("Unable to read file: %s", err)
	}

	if err = ioutil.WriteFile("banners/"+appstr+".jpg", file, 0777); err != nil {
		return "", fmt.Errorf("Unable to save file: %s", err)
	}

	return "banners/" + appstr + ".jpg", nil
}

// exists returns whether the given file or directory exists or not.
// Taken from https://stackoverflow.com/a/10510783
func exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return true
}
<commit_msg>Save API key to settings file<commit_after>package steamscreenshots

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/big"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

type Settings struct {
	RemoteDirectory string
	Address         string
	AppidOverrides  []struct {
		Appid string `json:"id"`
		Name  string `json:"name"`
	}
	RefreshInterval int    // In minutes
	ApiKey          string // This will be regenerated if it is empty.
}

var re_gamename = regexp.MustCompile(`<td itemprop="name">(.+?)</td>`)

var (
	gitCommit string
	version   string
)

// Structure of json from steam's servers
type steamapps struct {
	Applist struct {
		Apps []struct {
			Appid uint64 `json:"appid"`
			Name  string `json:"name"`
		} `json:"apps"`
	} `json:"applist"`
}

type Server struct {
	// stats stuff
	startTime time.Time
	lastScan  time.Time

	lastUpdate *time.Time

	settings Settings

	Games      *GameList
	ImageCache *GameImages

	SettingsFile string
}

func (s *Server) Run() {
	fmt.Println("Starting server")

	if len(gitCommit) == 0 {
		gitCommit = "Missing commit hash"
	}

	if len(version) == 0 {
		version = "Missing version info"
	}
	fmt.Printf("%s@%s\n", version, gitCommit)

	s.startTime = time.Now()
	s.Games = NewGameList()

	if err := s.loadSettings("settings.json"); err != nil {
		fmt.Printf("Error loading settings: %s\n", err)
		return
	}

	if err := init_templates(); err != nil {
		fmt.Printf("Error loading templates: %s\n", err)
		return
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/", s.handler_main)
	mux.HandleFunc("/thumb/", s.handler_thumb)
	mux.HandleFunc("/img/", s.handler_image)
	mux.HandleFunc("/banner/", s.handler_banner)
	mux.HandleFunc("/static/", s.handler_static)
	mux.HandleFunc("/debug/", s.handler_debug)
	mux.HandleFunc("/api/get-cache", s.handler_api_cache)
	mux.HandleFunc("/api/get-games", s.handler_api_games)
	mux.HandleFunc("/api/add-image", s.handler_api_addImage)

	server := &http.Server{
		Addr:           s.settings.Address,
		Handler:        mux,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	var err error
	s.ImageCache, err = LoadImageCache("image.cache")
	if err != nil {
		fmt.Println("Unable to load image.cache: ", err)

		s.ImageCache = NewGameImages()
		err = s.scan(true)
		if err != nil {
			fmt.Println("Initial scan error: ", err)
			return
		}
	} else {
		fmt.Println("Refreshing RemoteDirectory...")
		if err = s.scan(true); err != nil {
			fmt.Println("Error refreshing RemoteDirectory: ", err)
			return
		}
	}
	fmt.Println("Initial scan OK")

	// Fire and forget.  TODO: graceful shutdown
	go func() {
		for {
			time.Sleep(time.Minute * time.Duration(s.settings.RefreshInterval))
			if err := s.scan(false); err != nil {
				fmt.Printf("Error scanning: %s", err)
			}
		}
	}()

	// Generate a new API key if it's empty
	if s.settings.ApiKey == "" {
		out := ""
		large := big.NewInt(int64(1 << 60))
		large = large.Add(large, large)
		for len(out) < 50 {
			num, err := rand.Int(rand.Reader, large)
			if err != nil {
				panic("Error generating session key: " + err.Error())
			}
			out = fmt.Sprintf("%s%X", out, num)
		}
		s.settings.ApiKey = out
		fmt.Println("New API key generated: " + s.settings.ApiKey)
		if err = s.saveSettings("settings.json"); err != nil {
			panic(fmt.Sprintf("unable to save settings: %v", err))
		}
	} else {
		fmt.Printf("using API key in config: %q\n", s.settings.ApiKey)
	}

	go func(s *Server) {
		for {
			s.ImageCache.Save("image.cache")
			time.Sleep(10 * time.Minute)
		}
	}(s)

	fmt.Println("Listening on address: " + s.settings.Address)
	fmt.Println("Fisnished startup.")
	server.ListenAndServe()
}

// TODO: use GameImages.FullScan for this?
func (s *Server) scan(printOutput bool) error {
	s.lastScan = time.Now()

	if printOutput {
		fmt.Printf("Scanning %q\n", s.settings.RemoteDirectory)
	}

	dir, err := filepath.Glob(filepath.Join(s.settings.RemoteDirectory, "*"))
	if err != nil {
		return fmt.Errorf("Unable to glob RemoteDirectory: %s", err)
	}
	tmpTree := make(map[string][]string)

	for _, d := range dir {
		base := filepath.Base(d)

		// Ignore dotfiles
		if strings.HasPrefix(base, ".") {
			continue
		}

		if printOutput {
			fmt.Printf("[%s] %s\n", base, s.Games.Get(base))
		}

		jpg, err := filepath.Glob(filepath.Join(d, "screenshots", "*.jpg"))
		if err != nil {
			fmt.Printf("JPG glob error in %q: %s", d, err)
			continue
		}
		tmpTree[base] = jpg

		// TODO: merge ImageCache.ScanPath() and ImagePath.RefreshPath(), possibly removing the jpg glob above as well.
		err = s.ImageCache.ScanPath(d)
		if err != nil {
			fmt.Println(err)
		}
	}

	// Write cache to disk after it's updated in-memory so failing this doesn't skip updating.
	if err := s.ImageCache.Save("image.cache"); err != nil {
		return fmt.Errorf("Unable to save image cache: %s\n", err)
	}

	return nil
}

func SliceContains(s []string, val string) bool {
	for _, v := range s {
		if v == val {
			return true
		}
	}
	return false
}

func (s *Server) saveSettings(filename string) error {
	raw, err := json.MarshalIndent(s.settings, "", "    ")
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(filename, raw, 0600)
	if err != nil {
		return err
	}

	return os.Chmod(filename, 0600)
}

// FIXME: pass the filename in here as an argument
func (s *Server) loadSettings(filename string) error {
	settingsFile, err := ioutil.ReadFile(filename)
	if err != nil {
		return fmt.Errorf("Error reading settings file: %s", err)
	}

	err = json.Unmarshal(settingsFile, &s.settings)
	if err != nil {
		return fmt.Errorf("Error unmarshaling settings: %s", err)
	}

	fmt.Println("Settings loaded")

	if s.settings.RefreshInterval < 1 {
		s.settings.RefreshInterval = 1
	}

	return s.loadGames()
}

func (s *Server) loadGames() error {
	if ex := exists("games.cache"); !ex {
		fmt.Println("games.cache doesn't exist.  Getting a new one.")
		if err := s.updateGamesJson(); err != nil {
			return fmt.Errorf("Unable update game list: %s", err)
		}
	}

	gamesFile, err := ioutil.ReadFile("games.cache")
	if err != nil {
		return fmt.Errorf("Error reading games file: %s", err)
	}

	var games GameIDs
	err = json.Unmarshal(gamesFile, &games)
	if err != nil {
		return fmt.Errorf("Error unmarshaling games: %s", err)
	}

	s.Games.Update(games)
	//fmt.Println("Number of games loaded: ", Games.Length())
	return nil
}

func (s *Server) getGameName(appid string) (string, error) {
	if appid == ".stfolder" {
		return appid, nil
	}

	//fmt.Printf("Getting name for appid %q\n", appid)
	if name := s.Games.Get(appid); name != appid {
		return name, nil
	}

	// Large appid, must be a non-steam game.  This could have some edge cases
	// as non-steam games' appids are CRCs.
	if len(appid) > 18 {
		return s.Games.Set(appid, fmt.Sprintf("Non-Steam game (%s)", appid)), nil
	}

	// TODO: rate limiting/cache age
	if err := s.updateGamesJson(); err == nil {
		if name := s.Games.Get(appid); name != appid {
			return name, nil
		}
	}
	return appid, nil
}

// Update the local cache of appids from steam's servers.
func (s *Server) updateGamesJson() error {
	if s.lastUpdate != nil && time.Since(*s.lastUpdate).Minutes() < 30 {
		//return fmt.Errorf("Cache still good.")
		fmt.Println("Not updating games list; cache still good.")
		return nil
	}

	now := time.Now()
	//fmt.Printf("time.Now(): {}\n", now)
	s.lastUpdate = &now

	fmt.Println("Updating games list")
	resp, err := http.Get("http://api.steampowered.com/ISteamApps/GetAppList/v2")
	if err != nil {
		return fmt.Errorf("Unable to get appid list from steam: %s", err)
	}
	defer resp.Body.Close()

	js, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("Unable to read appid json: %s", err)
	}

	alist := &steamapps{}
	if err := json.Unmarshal(js, alist); err != nil {
		return fmt.Errorf("Unable to unmarshal json: %s", err)
	}

	for _, a := range alist.Applist.Apps {
		id := fmt.Sprintf("%d", a.Appid)
		s.Games.Set(id, a.Name)

	}

	for _, ovr := range s.settings.AppidOverrides {
		s.Games.Set(ovr.Appid, ovr.Name)
		fmt.Printf("Setting override for [%s]: %q\n", ovr.Appid, ovr.Name)
	}

	// save games.cache
	games := s.Games.GetMap()
	marshaled, err := json.Marshal(games)
	if err != nil {
		return fmt.Errorf("Unable to marshal game json: %s", err)
	}

	err = ioutil.WriteFile("games.cache", marshaled, 0644)
	if err != nil {
		return fmt.Errorf("Unable to save games.cache: %s", err)
	}

	fmt.Printf("Finished updating games list.  Appids: %d\n", len(games))
	return nil
}

// Returns a filename
func (s *Server) getGameBanner(appid uint64) (string, error) {
	appstr := fmt.Sprintf("%d", appid)
	if exist := exists("banners/" + appstr + ".jpg"); exist {
		return "banners/" + appstr + ".jpg", nil
	}

	resp, err := http.Get("http://cdn.akamai.steamstatic.com/steam/apps/" + appstr + "/header.jpg")
	if err != nil {
		return "", fmt.Errorf("Unable to DL header: %s", err)
	}

	if resp.StatusCode >= 400 && resp.StatusCode < 500 {
		// Game not found.  Use unknown.

		raw, err := ioutil.ReadFile("banners/unknown.jpg")
		if err != nil {
			return "", fmt.Errorf("Unable to read unknown.jpg")
		}

		if err = ioutil.WriteFile("banners/"+appstr+".jpg", raw, 0777); err != nil {
			return "", fmt.Errorf("Unable to save file: %s", err)
		}

		return "banners/" + appstr + ".jpg", nil
	}

	defer resp.Body.Close()

	file, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("Unable to read file: %s", err)
	}

	if err = ioutil.WriteFile("banners/"+appstr+".jpg", file, 0777); err != nil {
		return "", fmt.Errorf("Unable to save file: %s", err)
	}

	return "banners/" + appstr + ".jpg", nil
}

// exists returns whether the given file or directory exists or not.
// Taken from https://stackoverflow.com/a/10510783
func exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/adamdecaf/cert-manage/cmd"
)

const (
	version = "0.0.1-dev"
)

var (
	fs = flag.NewFlagSet("flags", flag.ExitOnError)

	// Commands
	list      = fs.Bool("list", false, "List certificates (by default on the system, see -app)")
	whitelist = fs.String("whitelist", "", "Filter certificates according to the provided whitelist")
	// TODO(adam): future commands
	// - backup
	// - restore

	// Filters
	app = fs.String("app", "", "Specify an application (see -list)")

	// Output
	format = fs.String("format", "table", "Specify the output format (options: raw, table)")
)

func main() {
	fs.Parse(os.Args[1:])

	if list != nil && *list {
		if app != nil && *app != "" {
			cmd.ListCertsForApp(*app, *format)
			return
		}
		cmd.ListCertsForPlatform(*format)
		return
	}

	wh := strings.TrimSpace(*whitelist)
	if whitelist != nil && wh != "" {
		if app != nil && *app != "" {
			err := cmd.WhitelistForApp(*app, *whitelist, *format)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			return
		}
		err := cmd.WhitelistForPlatform(*whitelist, *format)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		return
	}
}
<commit_msg>cmd: let -list be used after -whitelist<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/adamdecaf/cert-manage/cmd"
)

const (
	version = "0.0.1-dev"
)

var (
	fs = flag.NewFlagSet("flags", flag.ExitOnError)

	// Commands
	list      = fs.Bool("list", false, "List certificates (by default on the system, see -app)")
	whitelist = fs.String("whitelist", "", "Filter certificates according to the provided whitelist")
	// TODO(adam): future commands
	// - backup
	// - restore

	// Filters
	app = fs.String("app", "", "Specify an application (see -list)")

	// Output
	format = fs.String("format", "table", "Specify the output format (options: raw, table)")
)

func main() {
	fs.Parse(os.Args[1:])

	wh := strings.TrimSpace(*whitelist)
	if whitelist != nil && wh != "" {
		if app != nil && *app != "" {
			err := cmd.WhitelistForApp(*app, *whitelist, *format)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		}
		err := cmd.WhitelistForPlatform(*whitelist, *format)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}

	if list != nil && *list {
		if app != nil && *app != "" {
			cmd.ListCertsForApp(*app, *format)
			return
		}
		cmd.ListCertsForPlatform(*format)
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"time"

	log "github.com/Sirupsen/logrus"
)

type engineType int

const (
	engineNone engineType = iota
	engineRPGMVX
	engineWolf
)

var (
	engine = engineNone

	lineLength    int
	lineTolerance int
)

func main() {
	start := time.Now()

	args := parseFlags()
	if len(args) < 1 {
		log.Fatal("Program requires patch directory as argument")
	}

	dir := args[0]
	err := checkPatchVersion(dir)
	if err != nil {
		log.Fatal(err)
	}

	fileList := getDirectoryContents(filepath.Join(dir, "Patch"))
	if len(fileList) < 1 {
		log.Fatal("Couldn't find anything to translate")
	}

	if lineLength < 1 {
		switch engine {
		case engineWolf:
			lineLength = 54
		default:
			lineLength = 42
		}
	}
	fmt.Println("Current settings:")
	fmt.Println("- line length:", lineLength)
	fmt.Println("- line length tolerance:", lineTolerance)

	count := len(fileList)
	for i, file := range fileList {
		fmt.Printf("Processing %q (%d/%d)\n", filepath.Base(file), i+1, count)

		patch, err := parsePatchFile(file)
		if err != nil {
			log.Error(err)
			continue
		}

		patch, err = translatePatch(patch)
		check(err)

		err = writePatchFile(patch)
		check(err)
	}

	fmt.Printf("Finished in %s\n", time.Since(start))
}

func checkPatchVersion(dir string) error {
	file, err := os.Open(filepath.Join(dir, "RPGMKTRANSPATCH"))
	if err != nil {
		file, err = os.Open(filepath.Join(dir, "Patch", "dump", "GameDat.txt"))
		if err != nil {
			return fmt.Errorf("Unable to open RPGMKTRANSPATCH or Patch/dump/GameDat.txt")
		}
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		text := scanner.Text()

		if text == "> RPGMAKER TRANS PATCH V3" {
			fmt.Println("Detected RPG Maker VX Ace Patch")

			engine = engineRPGMVX

			return nil
		} else if text == "> WOLF TRANS PATCH FILE VERSION 1.0" {
			fmt.Println("Detected WOLF RPG Patch")

			engine = engineWolf

			return nil
		}

		return fmt.Errorf("Unsupported patch version")
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	return err
}

func getDirectoryContents(dir string) []string {
	var fileList []string

	err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
		if f.IsDir() || filepath.Ext(path) != ".txt" {
			return nil
		}

		fileList = append(fileList, path)

		return nil
	})

	if err != nil {
		log.Fatal(err)
	}

	return fileList
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func parseFlags() []string {
	flag.IntVar(&lineLength, "length", 42, "Max line legth")
	flag.IntVar(&lineTolerance, "tolerance", 5, "Max amount of characters allowed to go over the line limit")

	flag.Parse()

	return flag.Args()
}
<commit_msg>Change -length default value<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"time"

	log "github.com/Sirupsen/logrus"
)

type engineType int

const (
	engineNone engineType = iota
	engineRPGMVX
	engineWolf
)

var (
	engine = engineNone

	// Flags
	lineLength    int
	lineTolerance int
)

func main() {
	start := time.Now()

	args := parseFlags()
	if len(args) < 1 {
		log.Fatal("Program requires patch directory as argument")
	}

	dir := args[0]
	err := checkPatchVersion(dir)
	if err != nil {
		log.Fatal(err)
	}

	fileList := getDirectoryContents(filepath.Join(dir, "Patch"))
	if len(fileList) < 1 {
		log.Fatal("Couldn't find anything to translate")
	}

	if lineLength == -1 {
		if engine == engineWolf {
			lineLength = 54
		} else {
			lineLength = 42
		}
	}

	fmt.Println("Current settings:")
	fmt.Println("- line length:", lineLength)
	fmt.Println("- line length tolerance:", lineTolerance)

	count := len(fileList)
	for i, file := range fileList {
		fmt.Printf("Processing %q (%d/%d)\n", filepath.Base(file), i+1, count)

		patch, err := parsePatchFile(file)
		if err != nil {
			log.Error(err)
			continue
		}

		patch, err = translatePatch(patch)
		check(err)

		err = writePatchFile(patch)
		check(err)
	}

	fmt.Printf("Finished in %s\n", time.Since(start))
}

func checkPatchVersion(dir string) error {
	file, err := os.Open(filepath.Join(dir, "RPGMKTRANSPATCH"))
	if err != nil {
		file, err = os.Open(filepath.Join(dir, "Patch", "dump", "GameDat.txt"))
		if err != nil {
			return fmt.Errorf("Unable to open RPGMKTRANSPATCH or Patch/dump/GameDat.txt")
		}
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		text := scanner.Text()

		if text == "> RPGMAKER TRANS PATCH V3" {
			fmt.Println("Detected RPG Maker VX Ace Patch")

			engine = engineRPGMVX

			return nil
		} else if text == "> WOLF TRANS PATCH FILE VERSION 1.0" {
			fmt.Println("Detected WOLF RPG Patch")

			engine = engineWolf

			return nil
		}

		return fmt.Errorf("Unsupported patch version")
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	return err
}

func getDirectoryContents(dir string) []string {
	var fileList []string

	err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
		if f.IsDir() || filepath.Ext(path) != ".txt" {
			return nil
		}

		fileList = append(fileList, path)

		return nil
	})

	if err != nil {
		log.Fatal(err)
	}

	return fileList
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func parseFlags() []string {
	flag.IntVar(&lineLength, "length", -1, "Max line legth")
	flag.IntVar(&lineTolerance, "tolerance", 5, "Max amount of characters allowed to go over the line limit")

	flag.Parse()

	return flag.Args()
}
<|endoftext|>


<commit_before>package telnet

import (
	"crypto/tls"
	"net"
)

// ListenAndServe listens on the TCP network address `addr` and then spawns a call to the ServeTELNET
// method on the `handler` to serve each incoming connection.
//
// For a very simple example:
//
//	package main
//
//	import (
//		"github.com/reiver/go-telnet"
//	)
//
//	func main() {
//
//		//@TODO: In your code, you would probably want to use a different handler.
//		var handler telnet.Handler = telnet.EchoHandler
//
//		err := telnet.ListenAndServe(":5555", handler)
//		if nil != err {
//			//@TODO: Handle this error better.
//			panic(err)
//		}
//	}
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

// Serve accepts an incoming TELNET or TELNETS client connection on the net.Listener `listener`.
func Serve(listener net.Listener, handler Handler) error {

	server := &Server{Handler: handler}
	return server.Serve(listener)
}

// A Server defines parameters of a running TELNET server.
//
// For a simple example:
//
//	package main
//
//	import (
//		"github.com/reiver/go-telnet"
//	)
//
//	func main() {
//
//		var handler telnet.Handler = telnet.EchoHandler
//
//		server := &telnet.Server{
//			Addr:":5555",
//			Handler:handler,
//		}
//
//		err := server.ListenAndServe()
//		if nil != err {
//			//@TODO: Handle this error better.
//			panic(err)
//		}
//	}
type Server struct {
	Addr    string  // TCP address to listen on; ":telnet" or ":telnets" if empty (when used with ListenAndServe or ListenAndServeTLS respectively).
	Handler Handler // handler to invoke; telnet.EchoServer if nil

	TLSConfig *tls.Config // optional TLS configuration; used by ListenAndServeTLS.

	Logger Logger
}

// ListenAndServe listens on the TCP network address 'server.Addr' and then spawns a call to the ServeTELNET
// method on the 'server.Handler' to serve each incoming connection.
//
// For a simple example:
//
//	package main
//
//	import (
//		"github.com/reiver/go-telnet"
//	)
//
//	func main() {
//
//		var handler telnet.Handler = telnet.EchoHandler
//
//		server := &telnet.Server{
//			Addr:":5555",
//			Handler:handler,
//		}
//
//		err := server.ListenAndServe()
//		if nil != err {
//			//@TODO: Handle this error better.
//			panic(err)
//		}
//	}
func (server *Server) ListenAndServe() error {

	addr := server.Addr
	if "" == addr {
		addr = ":telnet"
	}

	listener, err := net.Listen("tcp", addr)
	if nil != err {
		return err
	}

	return server.Serve(listener)
}

// Serve accepts an incoming TELNET client connection on the net.Listener `listener`.
func (server *Server) Serve(listener net.Listener) error {

	defer listener.Close()

	logger := server.logger()

	handler := server.Handler
	if nil == handler {
//@TODO: Should this be a "ShellHandler" instead, that gives a shell-like experience by default
//       If this is changd, then need to change the comment in the "type Server struct" definition.
		logger.Debug("Defaulted handler to EchoHandler.")
		handler = EchoHandler
	}

	for {
		// Wait for a new TELNET client connection.
		logger.Debugf("Listening at %q.", listener.Addr())
		conn, err := listener.Accept()
		if err != nil {
//@TODO: Could try to recover from certain kinds of errors. Maybe waiting a while before trying again.
			return err
		}
		logger.Debugf("Received new connection from %q.", conn.RemoteAddr())

		// Handle the new TELNET client connection by spawning
		// a new goroutine.
		go func(c net.Conn) {
//@TODO: Add proper context.
			var ctx Context = NewContext().InjectLogger(logger)

			var w Writer = c
			var r Reader = c

			handler.ServeTELNET(ctx, w, r)
			c.Close()
		}(conn)
		logger.Debugf("Spawned handler to handle connection from %q.", conn.RemoteAddr())
        }
}

func (server *Server) logger() Logger {
	logger := server.Logger
	if nil == logger {
		logger = internalDiscardLogger{}
	}

	return logger
}
<commit_msg>some dev work<commit_after>package telnet

import (
	"crypto/tls"
	"net"
)

// ListenAndServe listens on the TCP network address `addr` and then spawns a call to the ServeTELNET
// method on the `handler` to serve each incoming connection.
//
// For a very simple example:
//
//	package main
//
//	import (
//		"github.com/reiver/go-telnet"
//	)
//
//	func main() {
//
//		//@TODO: In your code, you would probably want to use a different handler.
//		var handler telnet.Handler = telnet.EchoHandler
//
//		err := telnet.ListenAndServe(":5555", handler)
//		if nil != err {
//			//@TODO: Handle this error better.
//			panic(err)
//		}
//	}
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

// Serve accepts an incoming TELNET or TELNETS client connection on the net.Listener `listener`.
func Serve(listener net.Listener, handler Handler) error {

	server := &Server{Handler: handler}
	return server.Serve(listener)
}

// A Server defines parameters of a running TELNET server.
//
// For a simple example:
//
//	package main
//
//	import (
//		"github.com/reiver/go-telnet"
//	)
//
//	func main() {
//
//		var handler telnet.Handler = telnet.EchoHandler
//
//		server := &telnet.Server{
//			Addr:":5555",
//			Handler:handler,
//		}
//
//		err := server.ListenAndServe()
//		if nil != err {
//			//@TODO: Handle this error better.
//			panic(err)
//		}
//	}
type Server struct {
	Addr    string  // TCP address to listen on; ":telnet" or ":telnets" if empty (when used with ListenAndServe or ListenAndServeTLS respectively).
	Handler Handler // handler to invoke; telnet.EchoServer if nil

	TLSConfig *tls.Config // optional TLS configuration; used by ListenAndServeTLS.

	Logger Logger
}

// ListenAndServe listens on the TCP network address 'server.Addr' and then spawns a call to the ServeTELNET
// method on the 'server.Handler' to serve each incoming connection.
//
// For a simple example:
//
//	package main
//
//	import (
//		"github.com/reiver/go-telnet"
//	)
//
//	func main() {
//
//		var handler telnet.Handler = telnet.EchoHandler
//
//		server := &telnet.Server{
//			Addr:":5555",
//			Handler:handler,
//		}
//
//		err := server.ListenAndServe()
//		if nil != err {
//			//@TODO: Handle this error better.
//			panic(err)
//		}
//	}
func (server *Server) ListenAndServe() error {

	addr := server.Addr
	if "" == addr {
		addr = ":telnet"
	}

	listener, err := net.Listen("tcp", addr)
	if nil != err {
		return err
	}

	return server.Serve(listener)
}

// Serve accepts an incoming TELNET client connection on the net.Listener `listener`.
func (server *Server) Serve(listener net.Listener) error {

	defer listener.Close()

	logger := server.logger()

	handler := server.Handler
	if nil == handler {
//@TODO: Should this be a "ShellHandler" instead, that gives a shell-like experience by default
//       If this is changd, then need to change the comment in the "type Server struct" definition.
		logger.Debug("Defaulted handler to EchoHandler.")
		handler = EchoHandler
	}

	for {
		// Wait for a new TELNET client connection.
		logger.Debugf("Listening at %q.", listener.Addr())
		conn, err := listener.Accept()
		if err != nil {
//@TODO: Could try to recover from certain kinds of errors. Maybe waiting a while before trying again.
			return err
		}
		logger.Debugf("Received new connection from %q.", conn.RemoteAddr())

		// Handle the new TELNET client connection by spawning
		// a new goroutine.
		go func(c net.Conn) {
			var ctx Context = NewContext().InjectLogger(logger)

			var w Writer = NewDataWriter(c)
			var r Reader = NewDataReader(c)

			handler.ServeTELNET(ctx, w, r)
			c.Close()
		}(conn)
		logger.Debugf("Spawned handler to handle connection from %q.", conn.RemoteAddr())
        }
}

func (server *Server) logger() Logger {
	logger := server.Logger
	if nil == logger {
		logger = internalDiscardLogger{}
	}

	return logger
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"net/http"
	"os"
	"time"
)

const (
	waitTime  = 3 * time.Second
	execDigit = 5
	baseURL   = "https://git.io/"
	okFmt     = "%s OK!\n"
	ngFmt     = "%s NG!\n"
	locFmt    = "Location: %s\n"
)

func main() {
	c := new(http.Client)
	c.CheckRedirect = func(req *http.Request, via []*http.Request) error { return errors.New("not redirect") }

	runes := []rune{'0'}

	for {
		uri := string(runes)

		resp, err := c.Get(baseURL + uri)
		if err == nil {
			resp.Body.Close()
		}

		if resp.StatusCode == 404 {
			fmt.Printf(okFmt, uri)
		} else {
			fmt.Fprintf(os.Stderr, ngFmt, uri)
			fmt.Fprintf(os.Stderr, locFmt, resp.Header.Get("Location"))
		}

		runes = advanceRunes(runes)

		if len(runes) > execDigit {
			// exit
			break
		}

		time.Sleep(waitTime)
	}
}

func advanceRunes(runes []rune) []rune {
	if runes[len(runes)-1] == 'Z' {
		// carry over
		addFlg := true
		for i := len(runes) - 1; i > -1; i-- {
			beforeRune := runes[i]
			runes[i] = getNextRune(runes[i])
			if beforeRune != 'Z' {
				addFlg = false
				break
			}
		}

		if addFlg {
			runes = append(runes, '0')
		}
	} else {
		runes[len(runes)-1] = getNextRune(runes[len(runes)-1])
	}

	return runes
}

func getNextRune(r rune) rune {
	// 0 -> 9, a -> z, A -> Z
	if r == '9' {
		return 'a'
	} else if r == 'z' {
		return 'A'
	} else if r == 'Z' {
		return '0'
	}

	r++
	return r
}
<commit_msg>split func "checkURI"<commit_after>package main

import (
	"errors"
	"fmt"
	"net/http"
	"os"
	"time"
)

const (
	waitTime  = 3 * time.Second
	execDigit = 5
	baseURL   = "https://git.io/"
	okFmt     = "%s OK!\n"
	ngFmt     = "%s NG!\n"
	locFmt    = "Location: %s\n"
)

var (
	c = new(http.Client)
)

func init() {
	c.CheckRedirect = func(req *http.Request, via []*http.Request) error { return errors.New("not redirect") }
}

func main() {
	runes := []rune{'0'}

	for {
		uri := string(runes)

		checkURI(uri)

		runes = advanceRunes(runes)

		if len(runes) > execDigit {
			// exit
			break
		}

		time.Sleep(waitTime)
	}
}

func checkURI(uri string) {
	resp, err := c.Get(baseURL + uri)
	if err == nil {
		resp.Body.Close()
	}

	if resp.StatusCode == 404 {
		fmt.Printf(okFmt, uri)
	} else {
		fmt.Fprintf(os.Stderr, ngFmt, uri)
		fmt.Fprintf(os.Stderr, locFmt, resp.Header.Get("Location"))
	}
}

func advanceRunes(runes []rune) []rune {
	if runes[len(runes)-1] == 'Z' {
		// carry over
		addFlg := true
		for i := len(runes) - 1; i > -1; i-- {
			beforeRune := runes[i]
			runes[i] = getNextRune(runes[i])
			if beforeRune != 'Z' {
				addFlg = false
				break
			}
		}

		if addFlg {
			runes = append(runes, '0')
		}
	} else {
		runes[len(runes)-1] = getNextRune(runes[len(runes)-1])
	}

	return runes
}

func getNextRune(r rune) rune {
	// 0 -> 9, a -> z, A -> Z
	if r == '9' {
		return 'a'
	} else if r == 'z' {
		return 'A'
	} else if r == 'Z' {
		return '0'
	}

	r++
	return r
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/labstack/echo"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Server - main server object
type Server struct {
	Listen    string
	Collector *Collector
	Debug     bool
	echo      *echo.Echo
}

// Status - response status struct
type Status struct {
	Status    string                       `json:"status"`
	SendQueue int                          `json:"send_queue,omitempty"`
	Servers   map[string]*ClickhouseServer `json:"servers,omitempty"`
	Tables    map[string]*Table            `json:"tables,omitempty"`
}

// NewServer - create server
func NewServer(listen string, collector *Collector, debug bool) *Server {
	return &Server{listen, collector, debug, echo.New()}
}

func (server *Server) writeHandler(c echo.Context) error {
	q, _ := ioutil.ReadAll(c.Request().Body)
	s := string(q)

	if server.Debug {
		log.Printf("DEBUG: query %+v %+v\n", c.QueryString(), s)
	}

	qs := c.QueryString()
	user, password, ok := c.Request().BasicAuth()
	if ok {
		if qs == "" {
			qs = "user=" + user + "&password=" + password
		} else {
			qs = "user=" + user + "&password=" + password + "&" + qs
		}
	}
	params, content, insert := server.Collector.ParseQuery(qs, s)
	if insert {
		go server.Collector.Push(params, content)
		return c.String(http.StatusOK, "")
	}
	resp, status, _ := server.Collector.Sender.SendQuery(&ClickhouseRequest{Params: params, Content: content})
	return c.String(status, resp)
}

func (server *Server) statusHandler(c echo.Context) error {
	return c.JSON(200, Status{Status: "ok"})
}

// Start - start http server
func (server *Server) Start() error {
	return server.echo.Start(server.Listen)
}

// Shutdown - stop http server
func (server *Server) Shutdown(ctx context.Context) error {
	return server.echo.Shutdown(ctx)
}

// InitServer - run server
func InitServer(listen string, collector *Collector, debug bool) *Server {
	server := NewServer(listen, collector, debug)
	server.echo.POST("/", server.writeHandler)
	server.echo.GET("/status", server.statusHandler)
	server.echo.GET("/metrics", echo.WrapHandler(promhttp.Handler()))

	return server
}

// SafeQuit - safe prepare to quit
func SafeQuit(collect *Collector, sender Sender) {
	collect.FlushAll()
	if count := sender.Len(); count > 0 {
		log.Printf("Sending %+v tables\n", count)
	}
	for !sender.Empty() && !collect.Empty() {
		collect.WaitFlush()
	}
	collect.WaitFlush()
}

// RunServer - run all
func RunServer(cnf Config) {
	InitMetrics()
	dumper := NewDumper(cnf.DumpDir)
	sender := NewClickhouse(cnf.Clickhouse.DownTimeout, cnf.Clickhouse.ConnectTimeout)
	sender.Dumper = dumper
	for _, url := range cnf.Clickhouse.Servers {
		sender.AddServer(url)
	}

	collect := NewCollector(sender, cnf.FlushCount, cnf.FlushInterval)

	// send collected data on SIGTERM and exit
	signals := make(chan os.Signal)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)

	srv := InitServer(cnf.Listen, collect, cnf.Debug)

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	go func() {
		for {
			_ = <-signals
			log.Printf("STOP signal\n")
			if err := srv.Shutdown(ctx); err != nil {
				log.Printf("Shutdown error %+v\n", err)
				SafeQuit(collect, sender)
				os.Exit(1)
			}
		}
	}()

	if cnf.DumpCheckInterval >= 0 {
		dumper.Listen(sender, cnf.DumpCheckInterval)
	}

	err := srv.Start()
	if err != nil {
		log.Printf("ListenAndServe: %+v\n", err)
		SafeQuit(collect, sender)
		os.Exit(1)
	}
}
<commit_msg>remove processing for non insert queries<commit_after>package main

import (
	"context"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/labstack/echo"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Server - main server object
type Server struct {
	Listen    string
	Collector *Collector
	Debug     bool
	echo      *echo.Echo
}

// Status - response status struct
type Status struct {
	Status    string                       `json:"status"`
	SendQueue int                          `json:"send_queue,omitempty"`
	Servers   map[string]*ClickhouseServer `json:"servers,omitempty"`
	Tables    map[string]*Table            `json:"tables,omitempty"`
}

// NewServer - create server
func NewServer(listen string, collector *Collector, debug bool) *Server {
	return &Server{listen, collector, debug, echo.New()}
}

func (server *Server) writeHandler(c echo.Context) error {
	q, _ := ioutil.ReadAll(c.Request().Body)
	s := string(q)

	if server.Debug {
		log.Printf("DEBUG: query %+v %+v\n", c.QueryString(), s)
	}

	qs := c.QueryString()
	user, password, ok := c.Request().BasicAuth()
	if ok {
		if qs == "" {
			qs = "user=" + user + "&password=" + password
		} else {
			qs = "user=" + user + "&password=" + password + "&" + qs
		}
	}
	params, content, insert := server.Collector.ParseQuery(qs, s)
	if insert {
		go server.Collector.Push(params, content)
		return c.String(http.StatusOK, "")
	}
	resp, status, _ := server.Collector.Sender.SendQuery(&ClickhouseRequest{Params: qs, Content: s})
	return c.String(status, resp)
}

func (server *Server) statusHandler(c echo.Context) error {
	return c.JSON(200, Status{Status: "ok"})
}

// Start - start http server
func (server *Server) Start() error {
	return server.echo.Start(server.Listen)
}

// Shutdown - stop http server
func (server *Server) Shutdown(ctx context.Context) error {
	return server.echo.Shutdown(ctx)
}

// InitServer - run server
func InitServer(listen string, collector *Collector, debug bool) *Server {
	server := NewServer(listen, collector, debug)
	server.echo.POST("/", server.writeHandler)
	server.echo.GET("/status", server.statusHandler)
	server.echo.GET("/metrics", echo.WrapHandler(promhttp.Handler()))

	return server
}

// SafeQuit - safe prepare to quit
func SafeQuit(collect *Collector, sender Sender) {
	collect.FlushAll()
	if count := sender.Len(); count > 0 {
		log.Printf("Sending %+v tables\n", count)
	}
	for !sender.Empty() && !collect.Empty() {
		collect.WaitFlush()
	}
	collect.WaitFlush()
}

// RunServer - run all
func RunServer(cnf Config) {
	InitMetrics()
	dumper := NewDumper(cnf.DumpDir)
	sender := NewClickhouse(cnf.Clickhouse.DownTimeout, cnf.Clickhouse.ConnectTimeout)
	sender.Dumper = dumper
	for _, url := range cnf.Clickhouse.Servers {
		sender.AddServer(url)
	}

	collect := NewCollector(sender, cnf.FlushCount, cnf.FlushInterval)

	// send collected data on SIGTERM and exit
	signals := make(chan os.Signal)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)

	srv := InitServer(cnf.Listen, collect, cnf.Debug)

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	go func() {
		for {
			_ = <-signals
			log.Printf("STOP signal\n")
			if err := srv.Shutdown(ctx); err != nil {
				log.Printf("Shutdown error %+v\n", err)
				SafeQuit(collect, sender)
				os.Exit(1)
			}
		}
	}()

	if cnf.DumpCheckInterval >= 0 {
		dumper.Listen(sender, cnf.DumpCheckInterval)
	}

	err := srv.Start()
	if err != nil {
		log.Printf("ListenAndServe: %+v\n", err)
		SafeQuit(collect, sender)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. }}} */

package main

import (
	"flag"
)

func main() {
	var sniffConfig string

	flag.StringVar(&sniffConfig, "config", "sniff.json", "Config")
	flag.Parse()

	config, err := LoadConfig("/home/paultag/sniff.json")
	if err != nil {
		panic(err)
	}

	panic(config.Serve())
}

// vim: foldmethod=marker
<commit_msg>update<commit_after>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. }}} */

package main

import (
	"flag"
)

func main() {
	var sniffConfig string

	flag.StringVar(&sniffConfig, "config", "sniff.json", "Config")
	flag.Parse()

	config, err := LoadConfig(sniffConfig)
	if err != nil {
		panic(err)
	}

	panic(config.Serve())
}

// vim: foldmethod=marker
<|endoftext|>