<commit_before>package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"

	xpost "github.com/jirwin/xpost-quadlek/pkg"
	"go.uber.org/zap"

	"github.com/Bullpeen/infobot"
	"github.com/Bullpeen/stox"
	"github.com/jirwin/quadlek/plugins/admin"
	"github.com/jirwin/quadlek/plugins/comics"
	"github.com/jirwin/quadlek/plugins/gifs"
	"github.com/jirwin/quadlek/plugins/karma"
	"github.com/jirwin/quadlek/plugins/random"
	"github.com/jirwin/quadlek/plugins/spotify"
	"github.com/jirwin/quadlek/plugins/twitter"
	"github.com/jirwin/quadlek/quadlek"

	"github.com/urfave/cli"
)

const Version = "0.0.1"

func run(c *cli.Context) error {
	var apiToken string
	if c.IsSet("api-key") {
		apiToken = c.String("api-key")
	} else {
		err := cli.ShowAppHelp(c)
		if err != nil {
			return err
		}
		return cli.NewExitError("Missing --api-key arg.", 1)
	}

	var verificationToken string
	if c.IsSet("verification-token") {
		verificationToken = c.String("verification-token")
	} else {
		err := cli.ShowAppHelp(c)
		if err != nil {
			return err
		}
		return cli.NewExitError("Missing --verification-token arg.", 1)
	}

	dbPath := c.String("db-path")

	bot, err := quadlek.NewBot(context.Background(), apiToken, verificationToken, dbPath)
	if err != nil {
		zap.L().Error("error creating bot", zap.Error(err))
		return err
	}

	err = bot.RegisterPlugin(karma.Register())
	if err != nil {
		zap.L().Error("error registering karma plugin", zap.Error(err))
		return err
	}

	err = bot.RegisterPlugin(random.Register())
	if err != nil {
		zap.L().Error("error registering random plugin", zap.Error(err))
		return err
	}

	err = bot.RegisterPlugin(spotify.Register())
	if err != nil {
		zap.L().Error("error registering spotify plugin", zap.Error(err))
		return err
	}

	// TVDB not working
	//if c.IsSet("tvdb-key") {
	//	tvdbKey := c.String("tvdb-key")
	//
	//	err = bot.RegisterPlugin(nextep.Register(tvdbKey))
	//	if err != nil {
	//		fmt.Printf("error registering nextep plugin: %s\n", err.Error())
	//		return nil
	//	}
	//}

	err = bot.RegisterPlugin(infobot.Register())
	if err != nil {
		zap.L().Error("error registering infobot plugin", zap.Error(err))
		return err
	}

	err = bot.RegisterPlugin(twitter.Register(
		c.String("twitter-consumer-key"),
		c.String("twitter-consumer-secret"),
		c.String("twitter-access-token"),
		c.String("twitter-access-secret"),
		// These must be twitter user ids, not names. https://tweeterid.com/ for easy conversion between the two.
		map[string]string{
			"138203134":           "politics-feed",  // @AOC
			"783792992":           "politics-feed",  // @IlhanMN
			"31013444":            "politics-feed",  // @AyannaPressley
			"1079769536730140672": "politics-feed",  // @RepRashida
			"1081222837459996672": "politics-feed",  // @RepKatiePorter
			"151444950":           "politics-feed",  // @DaviSusan
			"2315512764":          "politics-feed",  // @bellingcat
			"976366106561490944":  "artfolio",       // @DrawnDavidsOff
			"921111554371682304":  "artfolio",       // @DrawnDavidson
			"1581511":             "wwdc",           // @macrumorslive
			"29472803":            "final-frontier", // @NASAWebb
			"17217640":            "final-frontier", // @SpaceFlightNow
			"14091091":            "final-frontier", // @NASAHubble
			"18831926":            "covid19-feed",   // @DrEricDing
			"88589013":            "covid19-feed",   //@fitterhappierAJ
			"920943710254313472":  "covid19-feed",   //@dgurdasani1
			"29431996":            "covid19-feed",   //@patricklsimpson

			//added for retweet testing
			"778682": "quadlek-chat", // @jirwin

		},
	))
	if err != nil {
		zap.L().Error("error configuring twitter plugin", zap.Error(err))
		return err
	}

	//coinbasePlugin := cointip.Register(
	//	c.String("coinbase-key"),
	//	c.String("coinbase-secret"),
	//	c.String("coinbase-account"),
	//)
	//if coinbasePlugin != nil {
	//	err = bot.RegisterPlugin(coinbasePlugin)
	//	if err != nil {
	//		panic(err)
	//	}
	//}

	err = bot.RegisterPlugin(comics.Register(
		c.String("imgur-client-id"),
		"/opt/quad-assets/Arial.ttf",
	))
	if err != nil {
		fmt.Printf("error registering comic plugin: %s\n", err.Error())
		return err
	}
	//esPlugin, err := eslogs.Register(c.String("es-endpoint"), c.String("es-index"))
	//if err != nil {
	//	fmt.Printf("Error creating eslogs plugin: %s\n", err.Error())
	//	return err
	//}
	//err = bot.RegisterPlugin(esPlugin)
	//if err != nil {
	//	fmt.Printf("Error registering eslogs plugin: %s\n", err.Error())
	//	return err
	//}

	xpostPlugin := xpost.Register()
	err = bot.RegisterPlugin(xpostPlugin)
	if err != nil {
		fmt.Printf("error registering xpost plugin: %s\n", err.Error())
		return err
	}

	stoxPlugin := stox.Register(c.String("av-key"))
	err = bot.RegisterPlugin(stoxPlugin)
	if err != nil {
		fmt.Printf("Error registering stox plugin: %s\n", err.Error())
		return err
	}

	gifPlugin := gifs.Register(c.String("giphy-api-key"))
	err = bot.RegisterPlugin(gifPlugin)
	if err != nil {
		fmt.Printf("Error registering gifs plugin: %s\n", err.Error())
		return err
	}

	// Twitch not working
	//twitchFollows := []*twitch.TwitchFollow{
	//	{
	//		SlackChannels: []string{"kitboga"},
	//		TwitchUser:    "kitboga",
	//		WatchStream:   true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "telewyn",
	//		SlackUser:     "telewyn",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "khryo72",
	//		SlackUser:     "morgabra",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "mekilek",
	//		SlackUser:     "jirwin",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "sonicdm",
	//		SlackUser:     "sonicdm",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "sakatana",
	//		SlackUser:     "saka",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "pountaar",
	//		SlackUser:     "purdyk",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//}

	//twitchPlugin := twitch.Register(c.String("twitch-oauth-client-id"), "", "https://bullpeen-quadlek.quadlek.dev/slack/plugin/twitch", false, twitchFollows)
	//if twitchPlugin != nil {
	//	err = bot.RegisterPlugin(twitchPlugin)
	//	if err != nil {
	//		fmt.Printf("error registering twitch plugin: %s\n", err.Error())
	//		return err
	//	}
	//}
	//
	//err = bot.RegisterPlugin(slices.Register([]string{
	//	"76561197980107683", // sonicdm
	//	"76561197976367183", // morgabra
	//	"76561198057633471", // greenjeans
	//	"76561198002272597", // newsomr
	//	"76561197974723967", // purdyk
	//	"1967806609958425",  // bh
	//	"1964439323566513",  // schonstal
	//}))
	//if err != nil {
	//	fmt.Printf("error registering slices plugin: %s\n", err.Error())
	//	return nil
	//}

	err = bot.RegisterPlugin(admin.Register())
	if err != nil {
		fmt.Printf("error registering admin plugin: %s\n", err.Error())
		return nil
	}

	err = bot.RegisterPlugin(admin.RegisterInteraction())
	if err != nil {
		fmt.Printf("error registering admin interaction plugin: %s\n", err.Error())
		return nil
	}

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	bot.Start()
	<-signals
	bot.Stop()

	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "quadlek"
	app.Version = Version
	app.Usage = "a slack bot"
	app.Action = run
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "api-key",
			Usage:  "The slack api token for the bot",
			EnvVar: "QUADLEK_API_TOKEN",
		},
		cli.StringFlag{
			Name:   "verification-token",
			Usage:  "The slack webhook verification token.",
			EnvVar: "QUADLEK_VERIFICATION_TOKEN",
		},
		cli.StringFlag{
			Name:   "db-path",
			Usage:  "The path where the database is stored.",
			Value:  "quadlek.db",
			EnvVar: "QUADLEK_DB_PATH",
		},
		cli.StringFlag{
			Name:   "tvdb-key",
			Usage:  "The TVDB api key for the bot, used by the nextep command",
			EnvVar: "QUADLEK_TVDB_TOKEN",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-key",
			Usage:  "The consumer key for the twitter api",
			EnvVar: "QUADLEK_TWITTER_CONSUMER_KEY",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-secret",
			Usage:  "The consumer secret for the twitter api",
			EnvVar: "QUADLEK_TWITTER_CONSUMER_SECRET",
		},
		cli.StringFlag{
			Name:   "twitter-access-token",
			Usage:  "The access key for the twitter api",
			EnvVar: "QUADLEK_TWITTER_ACCESS_TOKEN",
		},
		cli.StringFlag{
			Name:   "twitter-access-secret",
			Usage:  "The access secret for the twitter api",
			EnvVar: "QUADLEK_TWITTER_ACCESS_SECRET",
		},
		cli.StringFlag{
			Name:   "coinbase-key",
			Usage:  "The access key for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_KEY",
		},
		cli.StringFlag{
			Name:   "coinbase-secret",
			Usage:  "The access secret for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_SECRET",
		},
		cli.StringFlag{
			Name:   "coinbase-account",
			Usage:  "The bank account for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_BANK_ACCOUNT",
		},
		cli.StringFlag{
			Name:   "imgur-client-id",
			Usage:  "The imgur client id",
			EnvVar: "QUADLEK_IMGUR_CLIENT_ID",
		},
		cli.StringFlag{
			Name:   "es-endpoint",
			Usage:  "The elastic search endpoint",
			EnvVar: "QUADLEK_ES_ENDPOINT",
		},
		cli.StringFlag{
			Name:   "es-index",
			Usage:  "The elastic search index",
			EnvVar: "QUADLEK_ES_INDEX",
		},
		cli.StringFlag{
			Name:   "av-key",
			Usage:  "Alpha Vantage Api Key",
			EnvVar: "QUADLEK_AV_KEY",
		},
		cli.StringFlag{
			Name:   "giphy-api-key",
			Usage:  "Giphy API Key",
			EnvVar: "QUADLEK_GIPHY_KEY",
		},
		cli.StringFlag{
			Name:   "twitch-oauth-client-id",
			Usage:  "OAuth application client id for twitch API",
			EnvVar: "QUADLEK_TWITCH_OAUTH_CLIENT_ID",
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		zap.L().Error("error while running quad", zap.Error(err))
		os.Exit(1)
	}
}
<commit_msg>Remove splaceflight<commit_after>package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"

	xpost "github.com/jirwin/xpost-quadlek/pkg"
	"go.uber.org/zap"

	"github.com/Bullpeen/infobot"
	"github.com/Bullpeen/stox"
	"github.com/jirwin/quadlek/plugins/admin"
	"github.com/jirwin/quadlek/plugins/comics"
	"github.com/jirwin/quadlek/plugins/gifs"
	"github.com/jirwin/quadlek/plugins/karma"
	"github.com/jirwin/quadlek/plugins/random"
	"github.com/jirwin/quadlek/plugins/spotify"
	"github.com/jirwin/quadlek/plugins/twitter"
	"github.com/jirwin/quadlek/quadlek"

	"github.com/urfave/cli"
)

const Version = "0.0.1"

func run(c *cli.Context) error {
	var apiToken string
	if c.IsSet("api-key") {
		apiToken = c.String("api-key")
	} else {
		err := cli.ShowAppHelp(c)
		if err != nil {
			return err
		}
		return cli.NewExitError("Missing --api-key arg.", 1)
	}

	var verificationToken string
	if c.IsSet("verification-token") {
		verificationToken = c.String("verification-token")
	} else {
		err := cli.ShowAppHelp(c)
		if err != nil {
			return err
		}
		return cli.NewExitError("Missing --verification-token arg.", 1)
	}

	dbPath := c.String("db-path")

	bot, err := quadlek.NewBot(context.Background(), apiToken, verificationToken, dbPath)
	if err != nil {
		zap.L().Error("error creating bot", zap.Error(err))
		return err
	}

	err = bot.RegisterPlugin(karma.Register())
	if err != nil {
		zap.L().Error("error registering karma plugin", zap.Error(err))
		return err
	}

	err = bot.RegisterPlugin(random.Register())
	if err != nil {
		zap.L().Error("error registering random plugin", zap.Error(err))
		return err
	}

	err = bot.RegisterPlugin(spotify.Register())
	if err != nil {
		zap.L().Error("error registering spotify plugin", zap.Error(err))
		return err
	}

	// TVDB not working
	//if c.IsSet("tvdb-key") {
	//	tvdbKey := c.String("tvdb-key")
	//
	//	err = bot.RegisterPlugin(nextep.Register(tvdbKey))
	//	if err != nil {
	//		fmt.Printf("error registering nextep plugin: %s\n", err.Error())
	//		return nil
	//	}
	//}

	err = bot.RegisterPlugin(infobot.Register())
	if err != nil {
		zap.L().Error("error registering infobot plugin", zap.Error(err))
		return err
	}

	err = bot.RegisterPlugin(twitter.Register(
		c.String("twitter-consumer-key"),
		c.String("twitter-consumer-secret"),
		c.String("twitter-access-token"),
		c.String("twitter-access-secret"),
		// These must be twitter user ids, not names. https://tweeterid.com/ for easy conversion between the two.
		map[string]string{
			"138203134":           "politics-feed",  // @AOC
			"783792992":           "politics-feed",  // @IlhanMN
			"31013444":            "politics-feed",  // @AyannaPressley
			"1079769536730140672": "politics-feed",  // @RepRashida
			"1081222837459996672": "politics-feed",  // @RepKatiePorter
			"151444950":           "politics-feed",  // @DaviSusan
			"2315512764":          "politics-feed",  // @bellingcat
			"976366106561490944":  "artfolio",       // @DrawnDavidsOff
			"921111554371682304":  "artfolio",       // @DrawnDavidson
			"1581511":             "wwdc",           // @macrumorslive
			"29472803":            "final-frontier", // @NASAWebb
			"14091091":            "final-frontier", // @NASAHubble
			"18831926":            "covid19-feed",   // @DrEricDing
			"88589013":            "covid19-feed",   //@fitterhappierAJ
			"920943710254313472":  "covid19-feed",   //@dgurdasani1
			"29431996":            "covid19-feed",   //@patricklsimpson

			//added for retweet testing
			"778682": "quadlek-chat", // @jirwin

		},
	))
	if err != nil {
		zap.L().Error("error configuring twitter plugin", zap.Error(err))
		return err
	}

	//coinbasePlugin := cointip.Register(
	//	c.String("coinbase-key"),
	//	c.String("coinbase-secret"),
	//	c.String("coinbase-account"),
	//)
	//if coinbasePlugin != nil {
	//	err = bot.RegisterPlugin(coinbasePlugin)
	//	if err != nil {
	//		panic(err)
	//	}
	//}

	err = bot.RegisterPlugin(comics.Register(
		c.String("imgur-client-id"),
		"/opt/quad-assets/Arial.ttf",
	))
	if err != nil {
		fmt.Printf("error registering comic plugin: %s\n", err.Error())
		return err
	}
	//esPlugin, err := eslogs.Register(c.String("es-endpoint"), c.String("es-index"))
	//if err != nil {
	//	fmt.Printf("Error creating eslogs plugin: %s\n", err.Error())
	//	return err
	//}
	//err = bot.RegisterPlugin(esPlugin)
	//if err != nil {
	//	fmt.Printf("Error registering eslogs plugin: %s\n", err.Error())
	//	return err
	//}

	xpostPlugin := xpost.Register()
	err = bot.RegisterPlugin(xpostPlugin)
	if err != nil {
		fmt.Printf("error registering xpost plugin: %s\n", err.Error())
		return err
	}

	stoxPlugin := stox.Register(c.String("av-key"))
	err = bot.RegisterPlugin(stoxPlugin)
	if err != nil {
		fmt.Printf("Error registering stox plugin: %s\n", err.Error())
		return err
	}

	gifPlugin := gifs.Register(c.String("giphy-api-key"))
	err = bot.RegisterPlugin(gifPlugin)
	if err != nil {
		fmt.Printf("Error registering gifs plugin: %s\n", err.Error())
		return err
	}

	// Twitch not working
	//twitchFollows := []*twitch.TwitchFollow{
	//	{
	//		SlackChannels: []string{"kitboga"},
	//		TwitchUser:    "kitboga",
	//		WatchStream:   true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "telewyn",
	//		SlackUser:     "telewyn",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "khryo72",
	//		SlackUser:     "morgabra",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "mekilek",
	//		SlackUser:     "jirwin",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "sonicdm",
	//		SlackUser:     "sonicdm",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "sakatana",
	//		SlackUser:     "saka",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "pountaar",
	//		SlackUser:     "purdyk",
	//		WatchStream:   true,
	//		WatchFollows:  true,
	//	},
	//}

	//twitchPlugin := twitch.Register(c.String("twitch-oauth-client-id"), "", "https://bullpeen-quadlek.quadlek.dev/slack/plugin/twitch", false, twitchFollows)
	//if twitchPlugin != nil {
	//	err = bot.RegisterPlugin(twitchPlugin)
	//	if err != nil {
	//		fmt.Printf("error registering twitch plugin: %s\n", err.Error())
	//		return err
	//	}
	//}
	//
	//err = bot.RegisterPlugin(slices.Register([]string{
	//	"76561197980107683", // sonicdm
	//	"76561197976367183", // morgabra
	//	"76561198057633471", // greenjeans
	//	"76561198002272597", // newsomr
	//	"76561197974723967", // purdyk
	//	"1967806609958425",  // bh
	//	"1964439323566513",  // schonstal
	//}))
	//if err != nil {
	//	fmt.Printf("error registering slices plugin: %s\n", err.Error())
	//	return nil
	//}

	err = bot.RegisterPlugin(admin.Register())
	if err != nil {
		fmt.Printf("error registering admin plugin: %s\n", err.Error())
		return nil
	}

	err = bot.RegisterPlugin(admin.RegisterInteraction())
	if err != nil {
		fmt.Printf("error registering admin interaction plugin: %s\n", err.Error())
		return nil
	}

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	bot.Start()
	<-signals
	bot.Stop()

	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "quadlek"
	app.Version = Version
	app.Usage = "a slack bot"
	app.Action = run
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "api-key",
			Usage:  "The slack api token for the bot",
			EnvVar: "QUADLEK_API_TOKEN",
		},
		cli.StringFlag{
			Name:   "verification-token",
			Usage:  "The slack webhook verification token.",
			EnvVar: "QUADLEK_VERIFICATION_TOKEN",
		},
		cli.StringFlag{
			Name:   "db-path",
			Usage:  "The path where the database is stored.",
			Value:  "quadlek.db",
			EnvVar: "QUADLEK_DB_PATH",
		},
		cli.StringFlag{
			Name:   "tvdb-key",
			Usage:  "The TVDB api key for the bot, used by the nextep command",
			EnvVar: "QUADLEK_TVDB_TOKEN",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-key",
			Usage:  "The consumer key for the twitter api",
			EnvVar: "QUADLEK_TWITTER_CONSUMER_KEY",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-secret",
			Usage:  "The consumer secret for the twitter api",
			EnvVar: "QUADLEK_TWITTER_CONSUMER_SECRET",
		},
		cli.StringFlag{
			Name:   "twitter-access-token",
			Usage:  "The access key for the twitter api",
			EnvVar: "QUADLEK_TWITTER_ACCESS_TOKEN",
		},
		cli.StringFlag{
			Name:   "twitter-access-secret",
			Usage:  "The access secret for the twitter api",
			EnvVar: "QUADLEK_TWITTER_ACCESS_SECRET",
		},
		cli.StringFlag{
			Name:   "coinbase-key",
			Usage:  "The access key for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_KEY",
		},
		cli.StringFlag{
			Name:   "coinbase-secret",
			Usage:  "The access secret for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_SECRET",
		},
		cli.StringFlag{
			Name:   "coinbase-account",
			Usage:  "The bank account for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_BANK_ACCOUNT",
		},
		cli.StringFlag{
			Name:   "imgur-client-id",
			Usage:  "The imgur client id",
			EnvVar: "QUADLEK_IMGUR_CLIENT_ID",
		},
		cli.StringFlag{
			Name:   "es-endpoint",
			Usage:  "The elastic search endpoint",
			EnvVar: "QUADLEK_ES_ENDPOINT",
		},
		cli.StringFlag{
			Name:   "es-index",
			Usage:  "The elastic search index",
			EnvVar: "QUADLEK_ES_INDEX",
		},
		cli.StringFlag{
			Name:   "av-key",
			Usage:  "Alpha Vantage Api Key",
			EnvVar: "QUADLEK_AV_KEY",
		},
		cli.StringFlag{
			Name:   "giphy-api-key",
			Usage:  "Giphy API Key",
			EnvVar: "QUADLEK_GIPHY_KEY",
		},
		cli.StringFlag{
			Name:   "twitch-oauth-client-id",
			Usage:  "OAuth application client id for twitch API",
			EnvVar: "QUADLEK_TWITCH_OAUTH_CLIENT_ID",
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		zap.L().Error("error while running quad", zap.Error(err))
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"image"
	"image/jpeg"
	"io"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"unicode"

	"golang.org/x/text/transform"
	"golang.org/x/text/unicode/norm"

	_ "image/gif"
	_ "image/png"
)

var errHomeNotFound = errors.New("wimg: user home directory not found")

func main() {
	var (
		name    = flag.String("name", "", "name of the image")
		quality = flag.Int("quality", 100, "jpeg quality encoding")
	)
	flag.Parse()
	args := flag.Args()
	if len(args) != 1 {
		fmt.Fprintln(os.Stderr, "wimg: invalid arguments")
		flag.PrintDefaults()
		os.Exit(1)
	}
	err := run(args[0], *name, *quality)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(src, name string, quality int) error {
	home := getUserHome()
	if home == "" {
		return errHomeNotFound
	}
	if name == "" {
		name = baseWithoutExt(src)
	}
	name, err := normalize(name)
	if err != nil {
		return err
	}
	resp, err := http.Get(src)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return save(home, name, resp.Body, quality)
}

func getUserHome() string {
	home := os.Getenv("HOME")
	if home == "" {
		home = os.Getenv("USERPROFILE")
	}
	return home
}

func baseWithoutExt(src string) string {
	ext := path.Ext(src)
	name := path.Base(src)
	return strings.TrimSuffix(name, ext)
}

func normalize(name string) (string, error) {
	t := transform.Chain(norm.NFD, transform.RemoveFunc(remove), norm.NFC)
	name = strings.TrimSpace(name)
	name, _, err := transform.String(t, name)
	if err != nil {
		return "", err
	}
	name = strings.ToLower(name)
	name = strings.Replace(name, " ", "_", -1)
	return name, nil
}

func remove(r rune) bool {
	return unicode.Is(unicode.Mn, r)
}

func save(home, name string, r io.Reader, quality int) error {
	m, _, err := image.Decode(r)
	if err != nil {
		return err
	}
	rect := m.Bounds()
	filename := getSaveName(home, name, rect)
	f, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer f.Close()
	return jpeg.Encode(f, m, &jpeg.Options{Quality: quality})
}

func getSaveName(home, name string, rect image.Rectangle) string {
	name = fmt.Sprintf("%dx%d_%s.jpg", rect.Dx(), rect.Dy(), name)
	return filepath.Join(home, "img", name)
}
<commit_msg>Save to current working directory.<commit_after>package main

import (
	"flag"
	"fmt"
	"image"
	"image/jpeg"
	"io"
	"net/http"
	"os"
	"path"
	"strings"
	"unicode"

	"golang.org/x/text/transform"
	"golang.org/x/text/unicode/norm"

	_ "image/gif"
	_ "image/png"
)

func main() {
	var (
		name    = flag.String("name", "", "name of the image")
		quality = flag.Int("quality", 100, "jpeg quality encoding")
	)
	flag.Parse()
	args := flag.Args()
	if len(args) != 1 {
		fmt.Fprintln(os.Stderr, "wimg: invalid arguments")
		flag.PrintDefaults()
		os.Exit(1)
	}
	err := run(args[0], *name, *quality)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(src, name string, quality int) error {
	if name == "" {
		name = baseWithoutExt(src)
	}
	name, err := normalize(name)
	if err != nil {
		return err
	}
	resp, err := http.Get(src)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	return save(name, resp.Body, quality)
}

func baseWithoutExt(src string) string {
	ext := path.Ext(src)
	name := path.Base(src)
	return strings.TrimSuffix(name, ext)
}

func normalize(name string) (string, error) {
	t := transform.Chain(norm.NFD, transform.RemoveFunc(remove), norm.NFC)
	name = strings.TrimSpace(name)
	name, _, err := transform.String(t, name)
	if err != nil {
		return "", err
	}
	name = strings.ToLower(name)
	name = strings.Replace(name, " ", "_", -1)
	return name, nil
}

func remove(r rune) bool {
	return unicode.Is(unicode.Mn, r)
}

func save(name string, r io.Reader, quality int) error {
	m, _, err := image.Decode(r)
	if err != nil {
		return err
	}
	rect := m.Bounds()
	filename := getSaveName(name, rect)
	f, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer f.Close()
	return jpeg.Encode(f, m, &jpeg.Options{Quality: quality})
}

func getSaveName(name string, rect image.Rectangle) string {
	return fmt.Sprintf("%dx%d_%s.jpg", rect.Dx(), rect.Dy(), name)
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2015, Simon J Mudd
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"sort"
	"strings"
	"sync"
	"time"

	//	"github.com/sjmudd/ps-top/connector"
	"github.com/sjmudd/queryprofiler/collection"
	"github.com/sjmudd/queryprofiler/connection"
//	"github.com/sjmudd/queryprofiler/querycache"
	"github.com/sjmudd/queryprofiler/querykey"
	"github.com/sjmudd/queryprofiler/sample"
)

const (
	driver = "mysql"

	defaultIgnorePerformanceSchema = false
	defaultInterval                = 1
	defaultIterations              = 10
	defaultQueryFilter             = ""
	defaultTopN                    = 5
)

// put this somewhere else or use time or whatever
func toSeconds(t float64) string {
	if t == 0 {
		return "0s"
	}
	if t > 1 {
		return fmt.Sprintf("%.1f%s", t)
	}
	if t < 0.001 {
		return fmt.Sprintf(".1%f us", t*1000000)
	}
	return fmt.Sprintf(".1%f ms", t*1000)
}

// connect to the databases using the given DSNs
// - return a slice of handles to each one.
func connect(dsns []string) connection.Connections {
	var connections connection.Connections
	var Connections string = "Connection" // UPPER CASE C
	var servers string = "connection"

	if len(dsns) != 1 {
		servers += "s"
		Connections += "s"
	}
	log.Println(fmt.Sprintf("Connecting to %d servers...", len(dsns)))
	for i, d := range dsns {
		conn := connection.NewConnection(d)
		conn.Open() // should always work, or be fatal

		log.Println(fmt.Sprintf("%d: %s connected. Version: %s", i, conn.Name(), conn.Version()))

		connections = append(connections, conn)
	}

	return connections
}

// collect collects data from the given connections
func collect(connections connection.Connections, iterations int, intervalSeconds int, ignorePerformanceSchema bool, queryFilter string) []collection.Collections {
	var wg sync.WaitGroup
	var interval = time.Second * time.Duration(intervalSeconds)

	// preallocate the empty collection settings
	collections := make([]collection.Collections, len(connections))

	log.Println(fmt.Sprintf("Collecting data %d times...", iterations))
	for iteration := 1; iteration <= iterations; iteration++ {

		for i, conn := range connections {
			i := i       // avoid go concurrency issues
			conn := conn // for concurrency avoid overwriting stuff

			wg.Add(1)

			// launch collector in parallel
			go func() {
				defer wg.Done()

				collection, err := collection.Collect(conn, ignorePerformanceSchema, queryFilter)
				if err != nil {
					log.Fatal("Failed to collect rows:", err)
				}
				collections[i] = append(collections[i], collection)
			}()
		}
		wg.Wait()

		var lengths []string
		for i := range collections {
			collectedLen := collections[i][len(collections[i])-1].Len()
			lengths = append(lengths, fmt.Sprintf("%d: %d", i, collectedLen))
		}
		log.Println(fmt.Sprintf("  %d: Collected rows: %s", iteration, strings.Join(lengths, ", ")))

		time.Sleep(interval) // wait required time
	}

	log.Println("Collecting finished.")
	return collections
}

// close closes database connections
func close(connections connection.Connections) {
	log.Println("Closing database connections.")
	for _, dbh := range connections {
		dbh.Close()
	}
}

func main() {
	var (
		collections             []collection.Collections
		connections             connection.Connections
		dsns                    []string
		ignorePerformanceSchema bool
		interval                int
		iterations              int
		queryFilter             string
		samples                 sample.SamplesSlice
		test                    bool
		topN                    int
	)

	flag.StringVar(&queryFilter, "query-filter", defaultQueryFilter, "Filter queries matching this regexp")
	flag.IntVar(&interval, "interval", defaultInterval, "interval to collect samples")
	flag.IntVar(&iterations, "iterations", defaultIterations, "number of iterations to collect from each connection")
	flag.BoolVar(&ignorePerformanceSchema, "ignore-performance-schema", defaultIgnorePerformanceSchema, "do we ignore performance_schema queries")
	flag.IntVar(&topN, "top-n", defaultTopN, "the top-n queries to show results for")
	flag.BoolVar(&test, "test", false, "use a test data set for testing")
	flag.Parse()

	if len(flag.Args()) == 0 {
		log.Println("Usage: ", os.Args[0], "[--ignore-performance-schema] [--interval=<seconds>] [--iterations=<count>] <dsn1> [<dsn2> ...]")
		log.Println("Usage: ", os.Args[0], "[--test]  does testing with a test data set")
		os.Exit(1)
	}

	if !test {
		log.Println(fmt.Sprintf("Configuration: Showing top %d queries (by total elapsed time)", topN))
		log.Println(fmt.Sprintf("Configuration: Collecting %d metrics every %d second(s)", iterations, interval))
		log.Println(fmt.Sprintf("Configuration: Query Filter: %s", queryFilter))
		dsns = flag.Args()

		// now start to do stuff
		log.Println("=== Phase I ===")
		connections = connect(dsns)
		log.Println("=== Phase II ===")
		collections = collect(connections, iterations, interval, ignorePerformanceSchema, queryFilter)
		connections.Close()
	} else {
		log.Println("Configuration: test setup")
		dsns = []string{"testing1", "testing2"}
		connections = connection.Connections{nil, nil}
		collections = []collection.Collections{{ // double braces are deliberate!
			//			{CollectedTime: time.Now(), Rows: {
			//					{{"A", "QueryA"}, 100, 100000 },
			//					{{"A", "QueryA"}, 100, 100000 },
			//				},
			//			},
			{CollectedTime: time.Now(), Rows: nil},
			{CollectedTime: time.Now(), Rows: nil},
		},
			{
				{CollectedTime: time.Now(), Rows: nil},
				{CollectedTime: time.Now(), Rows: nil},
			}}
	}

	// show collection amounts
	log.Println("Collection sizes:")
	for i := range collections {
		log.Println(fmt.Sprintf("%d: Have %d entries, first entry: %d rows, last entry: %d rows",
			i,
			len(collections[i]),
			collections[i][0].Len(),
			collections[i][len(collections[i])-1].Len()))
	}

	log.Println("Converting collection information to samples...")
	for i := range collections {
		s := sample.SamplesFromCollections(collections[i])
		log.Println(fmt.Sprintf("%d: Generated %d sample(s)", i, len(s)))
		samples = append(samples, s)
	}

	/********************************************************************
	** figure out which are the busiest queries                         *
	*  - find the first connection's top n queries based on the sample  *
	*    between the first and last collections.                        *
	*********************************************************************/

	log.Println("=== Phase III ===")
	log.Println("Determining the longest running queries over collection period...")

	var topKeys []querykey.QueryKey
	first := collections[0][0]
	last := collections[0][len(collections[0])-1]
	log.Println(fmt.Sprintf("3.1 Making a single sample from the first/last collections of connection: %s", connections[0].Name()))
	sample := sample.NewSample(
		first.Rows,
		last.Rows,
		first.CollectedTime,
		last.CollectedTime.Sub(first.CollectedTime))

	log.Println("3.2 Sorting results")
	sort.Sort(sample) /* decreasing duration */

	var j int
	for i := 0; i < topN; i++ {
		if len(sample.Rows) > 1 {
//			if j == 0 {
//				log.Println("Top queries (in collection interval) are:")
//			}
			j++

			r := sample.Rows[i]
//			query, _ := querycache.Get(r.Key)
			topKeys = append(topKeys, r.Key)

//			log.Println(fmt.Sprintf("Query %d: [%s] time/count: %d/%d: query: %s",
//				j,
//				r.Key,
//				r.SUM_TIMER_WAIT,
//				r.COUNT_STAR,
//				query))
		}
	}
	if j == 0 {
		log.Println("Samples from", connections[0].Name(), "have no sample information.")
		log.Println("This usually means that no queries have run in the collection period.")
		log.Println("Unable to continue as this server appears idle.")
		return
	}

	/********************************************************************
	** We now have the top queries (in topKeys) so collect metrics      *
	*  from the samples for these specific queries and then report them *
	*  to the user. (including comparing the values.                    *
	*********************************************************************/

	log.Println("=== Phase IV ===")
	log.Println("Printing metrics from calculated samples")

	samples.CompareMetrics(connections, topKeys)
}
<commit_msg>Cleanup option displays and remove some dead code<commit_after>/*
Copyright (c) 2015, Simon J Mudd
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"sort"
	"strings"
	"sync"
	"time"

	//	"github.com/sjmudd/ps-top/connector"
	"github.com/sjmudd/queryprofiler/collection"
	"github.com/sjmudd/queryprofiler/connection"
//	"github.com/sjmudd/queryprofiler/querycache"
	"github.com/sjmudd/queryprofiler/querykey"
	"github.com/sjmudd/queryprofiler/sample"
)

const (
	driver = "mysql"

	defaultIgnorePerformanceSchema = false
	defaultInterval                = 1
	defaultIterations              = 10
	defaultQueryFilter             = ""
	defaultTopN                    = 5
)

// put this somewhere else or use time or whatever
func toSeconds(t float64) string {
	if t == 0 {
		return "0s"
	}
	if t > 1 {
		return fmt.Sprintf("%.1f%s", t)
	}
	if t < 0.001 {
		return fmt.Sprintf(".1%f us", t*1000000)
	}
	return fmt.Sprintf(".1%f ms", t*1000)
}

// connect to the databases using the given DSNs
// - return a slice of handles to each one.
func connect(dsns []string) connection.Connections {
	var connections connection.Connections
	var Connections string = "Connection" // UPPER CASE C
	var servers string = "connection"

	if len(dsns) != 1 {
		servers += "s"
		Connections += "s"
	}
	log.Println(fmt.Sprintf("Connecting to %d servers...", len(dsns)))
	for i, d := range dsns {
		conn := connection.NewConnection(d)
		conn.Open() // should always work, or be fatal

		log.Println(fmt.Sprintf("%d: %s connected. Version: %s", i, conn.Name(), conn.Version()))

		connections = append(connections, conn)
	}

	return connections
}

// collect collects data from the given connections
func collect(connections connection.Connections, iterations int, intervalSeconds int, ignorePerformanceSchema bool, queryFilter string) []collection.Collections {
	var wg sync.WaitGroup
	var interval = time.Second * time.Duration(intervalSeconds)

	// preallocate the empty collection settings
	collections := make([]collection.Collections, len(connections))

	log.Println(fmt.Sprintf("Collecting data %d times...", iterations))
	for iteration := 1; iteration <= iterations; iteration++ {

		for i, conn := range connections {
			i := i       // avoid go concurrency issues
			conn := conn // for concurrency avoid overwriting stuff

			wg.Add(1)

			// launch collector in parallel
			go func() {
				defer wg.Done()

				collection, err := collection.Collect(conn, ignorePerformanceSchema, queryFilter)
				if err != nil {
					log.Fatal("Failed to collect rows:", err)
				}
				collections[i] = append(collections[i], collection)
			}()
		}
		wg.Wait()

		var lengths []string
		for i := range collections {
			collectedLen := collections[i][len(collections[i])-1].Len()
			lengths = append(lengths, fmt.Sprintf("%d: %d", i, collectedLen))
		}
		log.Println(fmt.Sprintf("  %d: Collected rows: %s", iteration, strings.Join(lengths, ", ")))

		time.Sleep(interval) // wait required time
	}

	log.Println("Collecting finished.")
	return collections
}

// close closes database connections
func close(connections connection.Connections) {
	log.Println("Closing database connections.")
	for _, dbh := range connections {
		dbh.Close()
	}
}

func main() {
	var (
		collections             []collection.Collections
		connections             connection.Connections
		dsns                    []string
		ignorePerformanceSchema bool
		interval                int
		iterations              int
		queryFilter             string
		samples                 sample.SamplesSlice
		topN                    int
	)

	flag.StringVar(&queryFilter, "query-filter", defaultQueryFilter, "Filter queries matching this regexp")
	flag.IntVar(&interval, "interval", defaultInterval, "interval to collect samples")
	flag.IntVar(&iterations, "iterations", defaultIterations, "number of iterations to collect from each connection")
	flag.BoolVar(&ignorePerformanceSchema, "ignore-performance-schema", defaultIgnorePerformanceSchema, "do we ignore performance_schema queries")
	flag.IntVar(&topN, "top-n", defaultTopN, "the top-n queries to show results for")
	flag.Parse()

	if len(flag.Args()) == 0 {
		fmt.Println("Usage: ", os.Args[0], "[<options>] <dsn1> [<dsn2> ...]")
		fmt.Println("")
		fmt.Println("Flags:")
		fmt.Println(fmt.Sprintf("--ignore-performance-schema   ignores queries using performance_schema. Default: %v", defaultIgnorePerformanceSchema))
		fmt.Println(fmt.Sprintf("--interval=X                  interval to collect data. Default: %v", defaultInterval))
		fmt.Println(fmt.Sprintf("--iterations=X                number of collections to make. Should be a minimum of 2. Default: %v", defaultIterations))
		fmt.Println(fmt.Sprintf("--query-filter=X              regexp to use when filtering queries. Default: '%v'", defaultQueryFilter))
		fmt.Println(fmt.Sprintf("--top-n=X                     show the top N queries by query elapsed time. Default: %v", defaultTopN))
		os.Exit(1)
	}

	log.Println(fmt.Sprintf("Configuration: Showing top %d queries (by total elapsed time)", topN))
	log.Println(fmt.Sprintf("Configuration: Collecting %d metrics every %d second(s)", iterations, interval))
	log.Println(fmt.Sprintf("Configuration: Query Filter: %s", queryFilter))
	dsns = flag.Args()

	// now start to do stuff
	log.Println("=== Phase I ===")
	connections = connect(dsns)
	log.Println("=== Phase II ===")
	collections = collect(connections, iterations, interval, ignorePerformanceSchema, queryFilter)
	connections.Close()

	// show collection amounts
	log.Println("Collection sizes:")
	for i := range collections {
		log.Println(fmt.Sprintf("%d: Have %d entries, first entry: %d rows, last entry: %d rows",
			i,
			len(collections[i]),
			collections[i][0].Len(),
			collections[i][len(collections[i])-1].Len()))
	}

	log.Println("Converting collection information to samples...")
	for i := range collections {
		s := sample.SamplesFromCollections(collections[i])
		log.Println(fmt.Sprintf("%d: Generated %d sample(s)", i, len(s)))
		samples = append(samples, s)
	}

	/********************************************************************
	** figure out which are the busiest queries                         *
	*  - find the first connection's top n queries based on the sample  *
	*    between the first and last collections.                        *
	*********************************************************************/

	log.Println("=== Phase III ===")
	log.Println("Determining the longest running queries over collection period...")

	var topKeys []querykey.QueryKey
	first := collections[0][0]
	last := collections[0][len(collections[0])-1]
	log.Println(fmt.Sprintf("3.1 Making a single sample from the first/last collections of connection: %s", connections[0].Name()))
	sample := sample.NewSample(
		first.Rows,
		last.Rows,
		first.CollectedTime,
		last.CollectedTime.Sub(first.CollectedTime))

	log.Println("3.2 Sorting results")
	sort.Sort(sample) /* decreasing duration */

	var j int
	for i := 0; i < topN; i++ {
		if len(sample.Rows) > 1 {
			j++

			r := sample.Rows[i]
			topKeys = append(topKeys, r.Key)
		}
	}
	if j == 0 {
		log.Println("Samples from", connections[0].Name(), "have no sample information.")
		log.Println("This usually means that no queries have run in the collection period.")
		log.Println("Unable to continue as this server appears idle.")
		return
	}

	/********************************************************************
	** We now have the top queries (in topKeys) so collect metrics      *
	*  from the samples for these specific queries and then report them *
	*  to the user. (including comparing the values.                    *
	*********************************************************************/

	log.Println("=== Phase IV ===")
	log.Println("Printing metrics from calculated samples")

	samples.CompareMetrics(connections, topKeys)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/iris-contrib/template/django"
	"github.com/kataras/iris"
	"github.com/kataras/iris/config"
	"github.com/xgfone/gobtweb/g"
)

func handlerError(ctx *iris.Context, err error) {
	if err != nil {
		g.Logger.Error("Failed to return the response", "err", err)
		ctx.EmitError(iris.StatusInternalServerError)
	}
}

func index(ctx *iris.Context) {
	err := ctx.Render("index.html", nil)
	handlerError(ctx, err)
}

// func downloadTorrent(ctx *iris.Context) {
// 	infohash := ctx.URLParam("torrent")
// 	if infohash == "" {
// 		ctx.EmitError(iris.StatusNotFound)
// 		return
// 	} else if len(infohash) != 40 {
// 		ctx.EmitError(iris.StatusBadRequest)
// 		return
// 	}

// 	//data := map[string]interface{}{}
// 	data, err := GetTorrentByInfohashFromSE(g.Conf.ElasticClient, infohash)
// 	if err != nil {
// 		handlerError(err)
// 		return
// 	}

// 	err = ctx.Data(iris.StatusOK, data)
// 	handlerError(ctx, err)
// }

func search(ctx *iris.Context) {
	query := strings.TrimSpace(ctx.URLParam("q"))
	if query == "" {
		query = strings.TrimSpace(ctx.FormValueString("key"))
		if query == "" {
			ctx.Redirect("/")
			return
		}
	}

	page, err := ctx.URLParamInt("page")
	if err != nil || page < 1 {
		page = 1
	}

	size := g.Conf.PageSize
	from := size * (page - 1)

	data, total, err := SearchKeyword(g.ElasticClient, g.Repository, query, from, size)
	if err != nil {
		handlerError(ctx, err)
		return
	} else if data == nil || len(data) == 0 {
		ctx.EmitError(iris.StatusNotFound)
		return
	}

	urlPattern := fmt.Sprintf("/search?key=%v&page=", query)
	pn := NewPagination(total, page, size, urlPattern)
	pn.SetNumLinks(10).Init()
	err = ctx.Render("search.html", map[string]interface{}{
		"query":      query,
		"torrents":   data,
		"total":      total,
		"pagination": *pn,
	})
	handlerError(ctx, err)
}

func main() {
	g.Init(os.Args[1])

	cfg := config.Default()
	cfg.ProfilePath = "/debug"

	web := iris.New(cfg)

	templateDir := g.Conf.TemplateDirectory
	django_cfg := django.DefaultConfig()
	web.UseTemplate(django.New(django_cfg)).Directory(templateDir, ".html")

	web.Get("/", index)
	web.Get("/index.html", index)
	web.Get("/search", search)

	web.StaticWeb("/static", g.Conf.StaticDirectory, 1)

	web.OnError(iris.StatusInternalServerError, func(ctx *iris.Context) {
		ctx.Render("500.html", nil)
	})
	web.OnError(iris.StatusNotFound, func(ctx *iris.Context) {
		ctx.Render("404.html", nil)
	})

	web.Listen(g.Conf.Address)
}
<commit_msg>Replace the template<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/iris-contrib/template/django"
	"github.com/kataras/iris"
	"github.com/kataras/iris/config"
	"github.com/xgfone/gobtweb/g"
)

func handlerError(ctx *iris.Context, err error) {
	if err != nil {
		g.Logger.Error("Failed to return the response", "err", err)
		ctx.EmitError(iris.StatusInternalServerError)
	}
}

func index(ctx *iris.Context) {
	err := ctx.Render("index.html", nil)
	handlerError(ctx, err)
}

// func downloadTorrent(ctx *iris.Context) {
// 	infohash := ctx.URLParam("torrent")
// 	if infohash == "" {
// 		ctx.EmitError(iris.StatusNotFound)
// 		return
// 	} else if len(infohash) != 40 {
// 		ctx.EmitError(iris.StatusBadRequest)
// 		return
// 	}

// 	//data := map[string]interface{}{}
// 	data, err := GetTorrentByInfohashFromSE(g.Conf.ElasticClient, infohash)
// 	if err != nil {
// 		handlerError(err)
// 		return
// 	}

// 	err = ctx.Data(iris.StatusOK, data)
// 	handlerError(ctx, err)
// }

func search(ctx *iris.Context) {
	query := strings.TrimSpace(ctx.URLParam("q"))
	if query == "" {
		query = strings.TrimSpace(ctx.FormValueString("key"))
		if query == "" {
			ctx.Redirect("/")
			return
		}
	}

	page, err := ctx.URLParamInt("page")
	if err != nil || page < 1 {
		page = 1
	}

	size := g.Conf.PageSize
	from := size * (page - 1)

	data, total, err := SearchKeyword(g.ElasticClient, g.Repository, query, from, size)
	if err != nil {
		handlerError(ctx, err)
		return
	} else if data == nil || len(data) == 0 {
		ctx.EmitError(iris.StatusNotFound)
		return
	}

	urlPattern := fmt.Sprintf("/search?key=%v&page=", query)
	pn := NewPagination(total, page, size, urlPattern)
	pn.SetNumLinks(10).Init()
	err = ctx.Render("search2.html", map[string]interface{}{
		"query":      query,
		"torrents":   data,
		"total":      total,
		"pagination": *pn,
	})
	handlerError(ctx, err)
}

func main() {
	g.Init(os.Args[1])

	cfg := config.Default()
	cfg.ProfilePath = "/debug"

	web := iris.New(cfg)

	templateDir := g.Conf.TemplateDirectory
	django_cfg := django.DefaultConfig()
	web.UseTemplate(django.New(django_cfg)).Directory(templateDir, ".html")

	web.Get("/", index)
	web.Get("/index.html", index)
	web.Get("/search", search)

	web.StaticWeb("/static", g.Conf.StaticDirectory, 1)

	web.OnError(iris.StatusInternalServerError, func(ctx *iris.Context) {
		ctx.Render("500.html", nil)
	})
	web.OnError(iris.StatusNotFound, func(ctx *iris.Context) {
		ctx.Render("404.html", nil)
	})

	web.Listen(g.Conf.Address)
}
<|endoftext|>


<commit_before>package main

import (
	"runtime"

	"fmt"

	"github.com/getfider/fider/app/models"
	"github.com/getfider/fider/app/pkg/env"
	_ "github.com/lib/pq"
	_ "github.com/mattes/migrate/database/postgres"
	_ "github.com/mattes/migrate/source/file"
)

var buildtime string
var version = "0.7.0-beta"

func main() {
	fmt.Printf("Application is starting...\n")
	fmt.Printf("GO_ENV: %s\n", env.Current())

	settings := &models.AppSettings{
		BuildTime:       buildtime,
		Version:         version,
		Compiler:        runtime.Version(),
		Environment:     env.Current(),
		GoogleAnalytics: env.GetEnvOrDefault("GOOGLE_ANALYTICS", ""),
		Mode:            env.Mode(),
		Domain:          env.MultiTenantDomain(),
	}

	e := GetMainEngine(settings)
	e.Start(":" + env.GetEnvOrDefault("PORT", "3000"))
}
<commit_msg>update to 0.7.0 (#155)<commit_after>package main

import (
	"runtime"

	"fmt"

	"github.com/getfider/fider/app/models"
	"github.com/getfider/fider/app/pkg/env"
	_ "github.com/lib/pq"
	_ "github.com/mattes/migrate/database/postgres"
	_ "github.com/mattes/migrate/source/file"
)

var buildtime string
var version = "0.7.0"

func main() {
	fmt.Printf("Application is starting...\n")
	fmt.Printf("GO_ENV: %s\n", env.Current())

	settings := &models.AppSettings{
		BuildTime:       buildtime,
		Version:         version,
		Compiler:        runtime.Version(),
		Environment:     env.Current(),
		GoogleAnalytics: env.GetEnvOrDefault("GOOGLE_ANALYTICS", ""),
		Mode:            env.Mode(),
		Domain:          env.MultiTenantDomain(),
	}

	e := GetMainEngine(settings)
	e.Start(":" + env.GetEnvOrDefault("PORT", "3000"))
}
<|endoftext|>


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

// WebBorer is a directory-enumeration tool written in Go.
package main

import (
	"github.com/Matir/webborer/client"
	"github.com/Matir/webborer/filter"
	"github.com/Matir/webborer/logging"
	"github.com/Matir/webborer/results"
	ss "github.com/Matir/webborer/settings"
	"github.com/Matir/webborer/util"
	"github.com/Matir/webborer/wordlist"
	"github.com/Matir/webborer/worker"
	"github.com/Matir/webborer/workqueue"
	"runtime"
)

// Load settings from flags
func loadSettings() (*ss.ScanSettings, error) {
	// Load scan settings
	settings, err := ss.GetScanSettings()
	if err != nil {
		logging.Logf(logging.LogFatal, err.Error())
		return nil, err
	}
	logging.ResetLog(settings.LogfilePath, settings.LogLevel)
	logging.Logf(logging.LogInfo, "Flags: %s", settings)
	return settings, nil
}

// This is the main runner for webborer.
// TODO: separate the actual scanning from all of the setup steps
func main() {
	util.EnableStackTraces()

	settings, err := loadSettings()
	if err != nil {
		return
	}

	// Enable CPU profiling
	var cpuProfStop func()
	if settings.DebugCPUProf {
		cpuProfStop = util.EnableCPUProfiling()
	}

	// Set number of threads
	logging.Logf(logging.LogDebug, "Setting GOMAXPROCS to %d.", settings.Threads)
	runtime.GOMAXPROCS(settings.Threads)

	// Load wordlist
	var words []string
	words, err = wordlist.LoadWordlist(settings.WordlistPath)
	if err != nil {
		logging.Logf(logging.LogFatal, "Unable to load wordlist: %s", err.Error())
		return
	}

	// Build an HTTP Client Factory
	logging.Logf(logging.LogDebug, "Creating Client Factory...")
	clientFactory, err := client.NewProxyClientFactory(settings.Proxies, settings.Timeout, settings.UserAgent)
	if err != nil {
		logging.Logf(logging.LogFatal, "Unable to build client factory: %s", err.Error())
		return
	}
	clientFactory.SetUsernamePassword(settings.HTTPUsername, settings.HTTPPassword)

	// Starting point
	scope, err := settings.GetScopes()
	if err != nil {
		logging.Logf(logging.LogFatal, err.Error())
		return
	}

	// Setup the main workqueue
	logging.Logf(logging.LogDebug, "Starting work queue...")
	queue := workqueue.NewWorkQueue(settings.QueueSize, scope, settings.AllowHTTPSUpgrade)
	queue.RunInBackground()

	logging.Logf(logging.LogDebug, "Creating expander and filter...")
	expander := filter.Expander{Wordlist: &words, Adder: queue.GetAddCount()}
	expander.ProcessWordlist()
	filter := filter.NewWorkFilter(settings, queue.GetDoneFunc())

	// Check robots mode
	if settings.RobotsMode == ss.ObeyRobots {
		filter.AddRobotsFilter(scope, clientFactory)
	}

	// filter paths after expansion
	logging.Debugf("Starting expansion and filtering...")
	work := filter.RunFilter(expander.Expand(queue.GetWorkChan()))

	logging.Logf(logging.LogDebug, "Creating results manager...")
	rchan := make(chan results.Result, settings.QueueSize)
	resultsManager, err := results.GetResultsManager(settings)
	if err != nil {
		logging.Logf(logging.LogFatal, "Unable to start results manager: %s", err.Error())
		return
	}

	logging.Logf(logging.LogDebug, "Starting %d workers...", settings.Workers)
	worker.StartWorkers(settings, clientFactory, work, queue.GetAddFunc(), queue.GetDoneFunc(), rchan)

	logging.Logf(logging.LogDebug, "Starting results manager...")
	resultsManager.Run(rchan)

	// Kick things off with the seed URL
	logging.Logf(logging.LogDebug, "Adding starting URLs: %v", scope)
	queue.AddURLs(scope...)

	// Potentially seed from robots
	if settings.RobotsMode == ss.SeedRobots {
		queue.SeedFromRobots(scope, clientFactory)
	}

	// Wait for work to be done
	logging.Logf(logging.LogDebug, "Main goroutine waiting for work...")
	queue.WaitPipe()
	logging.Logf(logging.LogDebug, "Work done.")

	// Cleanup
	queue.InputFinished()
	close(rchan)

	resultsManager.Wait()
	if cpuProfStop != nil {
		cpuProfStop()
	}
	logging.Logf(logging.LogDebug, "Done!")
}
<commit_msg>Add logigng.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// WebBorer is a directory-enumeration tool written in Go.
package main

import (
	"github.com/Matir/webborer/client"
	"github.com/Matir/webborer/filter"
	"github.com/Matir/webborer/logging"
	"github.com/Matir/webborer/results"
	ss "github.com/Matir/webborer/settings"
	"github.com/Matir/webborer/util"
	"github.com/Matir/webborer/wordlist"
	"github.com/Matir/webborer/worker"
	"github.com/Matir/webborer/workqueue"
	"runtime"
)

// Load settings from flags
func loadSettings() (*ss.ScanSettings, error) {
	// Load scan settings
	settings, err := ss.GetScanSettings()
	if err != nil {
		logging.Logf(logging.LogFatal, err.Error())
		return nil, err
	}
	logging.ResetLog(settings.LogfilePath, settings.LogLevel)
	logging.Logf(logging.LogInfo, "Flags: %s", settings)
	return settings, nil
}

// This is the main runner for webborer.
// TODO: separate the actual scanning from all of the setup steps
func main() {
	util.EnableStackTraces()

	settings, err := loadSettings()
	if err != nil {
		return
	}

	// Enable CPU profiling
	var cpuProfStop func()
	if settings.DebugCPUProf {
		cpuProfStop = util.EnableCPUProfiling()
	}

	// Set number of threads
	logging.Logf(logging.LogDebug, "Setting GOMAXPROCS to %d.", settings.Threads)
	runtime.GOMAXPROCS(settings.Threads)

	// Load wordlist
	var words []string
	words, err = wordlist.LoadWordlist(settings.WordlistPath)
	if err != nil {
		logging.Logf(logging.LogFatal, "Unable to load wordlist: %s", err.Error())
		return
	}

	// Build an HTTP Client Factory
	logging.Logf(logging.LogDebug, "Creating Client Factory...")
	clientFactory, err := client.NewProxyClientFactory(settings.Proxies, settings.Timeout, settings.UserAgent)
	if err != nil {
		logging.Logf(logging.LogFatal, "Unable to build client factory: %s", err.Error())
		return
	}
	clientFactory.SetUsernamePassword(settings.HTTPUsername, settings.HTTPPassword)

	// Starting point
	scope, err := settings.GetScopes()
	if err != nil {
		logging.Logf(logging.LogFatal, err.Error())
		return
	}

	// Setup the main workqueue
	logging.Logf(logging.LogDebug, "Starting work queue...")
	queue := workqueue.NewWorkQueue(settings.QueueSize, scope, settings.AllowHTTPSUpgrade)
	queue.RunInBackground()

	logging.Logf(logging.LogDebug, "Creating expander and filter...")
	expander := filter.Expander{Wordlist: &words, Adder: queue.GetAddCount()}
	expander.ProcessWordlist()
	filter := filter.NewWorkFilter(settings, queue.GetDoneFunc())

	// Check robots mode
	if settings.RobotsMode == ss.ObeyRobots {
		filter.AddRobotsFilter(scope, clientFactory)
	}

	// filter paths after expansion
	logging.Debugf("Starting expansion and filtering...")
	work := filter.RunFilter(expander.Expand(queue.GetWorkChan()))

	logging.Logf(logging.LogDebug, "Creating results manager...")
	rchan := make(chan results.Result, settings.QueueSize)
	resultsManager, err := results.GetResultsManager(settings)
	if err != nil {
		logging.Logf(logging.LogFatal, "Unable to start results manager: %s", err.Error())
		return
	}

	logging.Logf(logging.LogDebug, "Starting %d workers...", settings.Workers)
	worker.StartWorkers(settings, clientFactory, work, queue.GetAddFunc(), queue.GetDoneFunc(), rchan)

	logging.Logf(logging.LogDebug, "Starting results manager...")
	resultsManager.Run(rchan)

	// Kick things off with the seed URL
	logging.Logf(logging.LogDebug, "Adding starting URLs: %v", scope)
	queue.AddURLs(scope...)

	// Potentially seed from robots
	if settings.RobotsMode == ss.SeedRobots {
		queue.SeedFromRobots(scope, clientFactory)
	}

	// Wait for work to be done
	logging.Logf(logging.LogDebug, "Main goroutine waiting for work...")
	queue.WaitPipe()
	logging.Logf(logging.LogDebug, "Work done.")

	// Cleanup
	queue.InputFinished()
	close(rchan)

	logging.Debugf("Waiting for results manager.")
	resultsManager.Wait()
	if cpuProfStop != nil {
		cpuProfStop()
	}
	logging.Logf(logging.LogDebug, "Done!")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"code.google.com/p/gcfg"
	"code.google.com/p/go-uuid/uuid"
	"github.com/dpapathanasiou/go-recaptcha"
	"github.com/justinas/nosurf"
	"github.com/worr/chrooter"
	"github.com/worr/secstring"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"net/mail"
	"net/smtp"
	txttemplate "text/template"
)

type Config struct {
	Mail struct {
		Email    string
		Username string
		Password string
		Hostname string
		password *secstring.SecString
	}

	Recaptcha struct {
		Private string
	}
}

var t = template.Must(template.ParseFiles("template/index.html"))
var emailTemplate = txttemplate.Must(txttemplate.New("email").Parse("Here is your exclusive Vim download link: http://www.vim.org/download.php?code={{.Code}}"))
var c = make(chan string)
var conf Config

// Default handler
func dispatch(w http.ResponseWriter, r *http.Request) {
	context := map[string]string{
		"token": nosurf.Token(r),
	}

	if r.Method == "POST" {
		context["email"] = r.FormValue("email")
		if !recaptcha.Confirm(r.RemoteAddr, r.FormValue("recaptcha_challenge_field"), r.FormValue("recaptcha_response_field")) {
			http.Error(w, "Failed captcha", http.StatusBadRequest)
			return
		}

		if context["email"] == "" {
			http.Error(w, "Empty email address", http.StatusBadRequest)
			return
		}

		c <- context["email"]
	}

	if err := t.Execute(w, context); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// Handler for all bad CSRF requests
func failedCSRF(w http.ResponseWriter, r *http.Request) {
	http.Error(w, nosurf.Reason(r).Error(), http.StatusBadRequest)
}

// Pulls email off of the channel and possibly sends download codes
func email() {
	auth := smtp.PlainAuth("", conf.Mail.Username, string(conf.Mail.password.String), conf.Mail.Hostname)

	for addr := range c {
		// Exclusivity
		if r := rand.Intn(3); r != 0 {
			continue
		}

		buf := bytes.NewBuffer(make([]byte, 100))
		if err := emailTemplate.Execute(buf, struct{ Code string }{uuid.NewUUID().String()}); err != nil {
			log.Printf("Can't execute email template: %v", err)
			continue
		}

		var emailAddr *mail.Address
		var err error
		if emailAddr, err = mail.ParseAddress(addr); err != nil {
			log.Printf("Failed to send email to %v: %v", emailAddr.Address, err)
			continue
		}

		conf.Mail.password.Decrypt()
		if err = smtp.SendMail(conf.Mail.Hostname, auth, conf.Mail.Email, []string{emailAddr.Address}, buf.Bytes()); err != nil {
			log.Printf("Failed to send email to %v: %v", emailAddr.Address, err)
			continue
		}
		conf.Mail.password.Encrypt()
	}
}

func main() {
	if err := gcfg.ReadFileInto(&conf, "vim.sexy.ini"); err != nil {
		log.Fatalf("Can't read config file: %v", err)
	}

	if err := chrooter.Chroot("www", "/var/chroot/vim.sexy"); err != nil {
		log.Fatalf("Can't chroot: %v", err)
	}

	var err error
	if conf.Mail.password, err = secstring.FromString(&conf.Mail.Password); err != nil {
		log.Fatal(err)
	}

	recaptcha.Init(conf.Recaptcha.Private)

	go email()

	http.HandleFunc("/", dispatch)
	csrf := nosurf.New(http.DefaultServeMux)
	csrf.SetFailureHandler(http.HandlerFunc(failedCSRF))
	if err = http.ListenAndServe("127.0.0.1:8000", csrf); err != nil {
		log.Fatalf("Cannot listen: %v", err)
	}
}
<commit_msg>Email<commit_after>package main

import (
	"bytes"
	"code.google.com/p/gcfg"
	"code.google.com/p/go-uuid/uuid"
	"fmt"
	"github.com/dpapathanasiou/go-recaptcha"
	"github.com/justinas/nosurf"
	"github.com/worr/chrooter"
	"github.com/worr/secstring"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"net/mail"
	"net/smtp"
	"time"
	txttemplate "text/template"
)

type Config struct {
	Mail struct {
		Email    string
		Username string
		Password string
		Hostname string
		password *secstring.SecString
	}

	Recaptcha struct {
		Private string
	}
}

var t = template.Must(template.ParseFiles("template/index.html"))
var emailTemplate = txttemplate.Must(txttemplate.New("email").Parse("Here is your exclusive Vim download link: http://www.vim.org/download.php?code={{.Code}}"))
var c = make(chan string)
var conf Config

// Default handler
func dispatch(w http.ResponseWriter, r *http.Request) {
	context := map[string]string{
		"token": nosurf.Token(r),
	}

	if r.Method == "POST" {
		context["email"] = r.FormValue("email")
		if !recaptcha.Confirm(r.RemoteAddr, r.FormValue("recaptcha_challenge_field"), r.FormValue("recaptcha_response_field")) {
			http.Error(w, "Failed captcha", http.StatusBadRequest)
			return
		}

		if context["email"] == "" {
			http.Error(w, "Empty email address", http.StatusBadRequest)
			return
		}

		c <- context["email"]
	}

	if err := t.Execute(w, context); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// Handler for all bad CSRF requests
func failedCSRF(w http.ResponseWriter, r *http.Request) {
	http.Error(w, nosurf.Reason(r).Error(), http.StatusBadRequest)
}

// Pulls email off of the channel and possibly sends download codes
func email() {
	auth := smtp.PlainAuth("", conf.Mail.Username, string(conf.Mail.password.String), conf.Mail.Hostname)

	for addr := range c {
		// Exclusivity
		if r := rand.Intn(3); r != 0 {
			continue
		}

		var emailAddr *mail.Address
		var err error
		if emailAddr, err = mail.ParseAddress(addr); err != nil {
			log.Printf("Failed to send email to %v: %v", emailAddr.Address, err)
			continue
		}

		headers := make(map[string]string)
		headers["To"] = emailAddr.String()
		headers["From"] = conf.Mail.Email
		headers["Date"] = time.Now().String()
		headers["Subject"] = "Your Vim invite is ready"
		headers["MIME-Version"] = "1.0"
		headers["Content-Type"] = "text/plain; charset=\"utf-8\""
		headers["Content-Transfer-Encoding"] = "base64"

		msg := ""
		for key, val := range(headers) {
			msg += fmt.Sprintf("%v: %v\r\n", key, val)
		}

		buf := bytes.NewBuffer(make([]byte, 100))
		if err := emailTemplate.Execute(buf, struct{ Code string }{uuid.NewUUID().String()}); err != nil {
			log.Printf("Can't execute email template: %v", err)
			continue
		}

		msg += fmt.Sprintf("\r\n%v\r\n", buf.String())

		conf.Mail.password.Decrypt()
		if err = smtp.SendMail(conf.Mail.Hostname, auth, conf.Mail.Email, []string{emailAddr.Address}, []byte(msg)); err != nil {
			log.Printf("Failed to send email to %v: %v", emailAddr.Address, err)
			continue
		}
		conf.Mail.password.Encrypt()
	}
}

func main() {
	if err := gcfg.ReadFileInto(&conf, "vim.sexy.ini"); err != nil {
		log.Fatalf("Can't read config file: %v", err)
	}

	if err := chrooter.Chroot("www", "/var/chroot/vim.sexy"); err != nil {
		log.Fatalf("Can't chroot: %v", err)
	}

	var err error
	if conf.Mail.password, err = secstring.FromString(&conf.Mail.Password); err != nil {
		log.Fatal(err)
	}

	recaptcha.Init(conf.Recaptcha.Private)

	go email()

	http.HandleFunc("/", dispatch)
	csrf := nosurf.New(http.DefaultServeMux)
	csrf.SetFailureHandler(http.HandlerFunc(failedCSRF))
	if err = http.ListenAndServe("127.0.0.1:8000", csrf); err != nil {
		log.Fatalf("Cannot listen: %v", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"github.com/PuerkitoBio/goquery"
	"github.com/vincent-petithory/dataurl"
)

const yuyuteiURL = "http://yuyu-tei.jp/"
const wsDeckUrl = "http://wsdecks.com/"

type Prox struct {
	// target url of reverse proxy
	target *url.URL
	// instance of Go ReverseProxy thatwill do the job for us
	proxy *httputil.ReverseProxy
}

func New(target string) *Prox {
	url, _ := url.Parse(target)
	// you should handle error on parsing
	return &Prox{target: url, proxy: httputil.NewSingleHostReverseProxy(url)}
}

func (p *Prox) handle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("X-GoProxy", "GoProxy")
	// call to magic method from ReverseProxy object
	p.proxy.ServeHTTP(w, r)
}
func lowCostSystemToURL(syspath string) string {
	return strings.Replace(syspath, "\\", "/", -1)
}

func convertToJpg(filePath string) {
	// convert -density 150 -trim to_love-ru_darkness_2nd_trial_deck.pdf -quality 100 -sharpen 0x1.0 love.jpg
	cmd := exec.Command("convert", "-density", "150", "-trim", filePath, "-quality", "100", "-sharpen", "0x1.0", filePath+".jpg")
	err := cmd.Start()
	if err != nil {
		fmt.Println(err)
	}
	err = cmd.Wait()
}

func main() {
	proxy := New("http://localhost:8080")
	os.MkdirAll(filepath.Join("static", "yuyutei"), 0744)
	os.MkdirAll(filepath.Join("static", "wsdeck"), 0744)
	// static := http.FileServer(http.Dir("./"))
	http.HandleFunc("/", proxy.handle)

	http.HandleFunc("/translationimages", func(w http.ResponseWriter, r *http.Request) {
		file := r.PostFormValue("file")
		filename := r.PostFormValue("filename")
		uid := strings.Replace(filename, filepath.Ext(filename), "", 1)
		dir := filepath.Join("static", uid)
		filePath := filepath.Join(dir, filename)

		data, err := dataurl.DecodeString(file)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		// fmt.Println(dataURL.Data)
		os.MkdirAll(dir, 0777)
		ioutil.WriteFile(filePath, data.Data, 0644)
		if _, err := os.Stat(dir); os.IsNotExist(err) {
			convertToJpg(filePath)
		}
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		listJpg, err := filepath.Glob(filePath + "*.jpg")
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		for i, jpgfile := range listJpg {
			listJpg[i] = lowCostSystemToURL(jpgfile)
		}

		b, err := json.Marshal(listJpg)

		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		w.Write(b)
	})

	http.HandleFunc("/cardimages", func(w http.ResponseWriter, r *http.Request) {
		var wg sync.WaitGroup

		result := []string{}
		link := r.PostFormValue("url")
		classCSS := r.PostFormValue("class_css")
		filter := ""

		if classCSS == "" {
			classCSS = ".card_list_box"
		}

		if link != "" {
			doc, err := goquery.NewDocument(link)
			uid := ""
			site := ""
			imageURL := ""

			if strings.Contains(link, yuyuteiURL) {
				site = "yuyutei"
				filter = classCSS + " .image img"
				parsedURL, _ := url.Parse(link)
				values, _ := url.ParseQuery(parsedURL.RawQuery)
				uid = values.Get("ver")
			} else if strings.Contains(link, wsDeckUrl) {
				site = "wsdeck"
				filter = ".wscard" + " img"
				uid = filepath.Base(link)
			}
			// currentDir, _ := os.Getwd()
			dir := filepath.Join("static", site, uid)

			if filter == "" {
				http.Error(w, fmt.Sprintln("Url is not supported", link), 500)
			}

			if err != nil {
				fmt.Println("Nope")
			}
			if _, err := os.Stat(dir); os.IsNotExist(err) {
				os.MkdirAll(dir, 0744)
				doc.Find(filter).Each(func(i int, s *goquery.Selection) {
					wg.Add(1)
					val, _ := s.Attr("src")
					if site == "yuyutei" {
						big := strings.Replace(val, "90_126", "front", 1)
						imageURL = yuyuteiURL + big
					} else if site == "wsdeck" {
						imageURL = wsDeckUrl + val
					}

					go func(url string) {
						defer wg.Done()
						// fmt.Println("dir : ", dir)
						fileName := filepath.Join(dir, path.Base(url))
						out, err := os.Create(fileName)
						if err != nil {
							fmt.Println(err)
						}
						defer out.Close()
						reps, err := http.Get(url)

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

						file, err := io.Copy(out, reps.Body)
						if err != nil {
							fmt.Println(err)
						}

						fmt.Println("File", file)
						// fmt.Printf("Link: n-%d __ %v%v\n", i, imageURL, uid)
						defer reps.Body.Close()
						// fmt.Println("image url: ", strings.Replace(fileName, "\\", "/", 1))
						result = append(result, lowCostSystemToURL(fileName))
					}(imageURL)
				})
			} else {
				files, err := ioutil.ReadDir(dir)
				if err != nil {
					fmt.Println(err)
				}
				for _, file := range files {
					absPath := filepath.Join(dir, file.Name())
					urlPath := lowCostSystemToURL(absPath)
					result = append(result, urlPath)
				}

			}
		}

		wg.Wait()
		fmt.Printf("Finish")
		b, err := json.Marshal(result)

		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		w.Write(b)
	})

	http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
		// fmt.Println("static", r.URL.Path[1:])
		http.ServeFile(w, r, r.URL.Path[1:])
	})

	http.ListenAndServe(":8010", nil)
}
<commit_msg>Add print err<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"github.com/PuerkitoBio/goquery"
	"github.com/vincent-petithory/dataurl"
)

const yuyuteiURL = "http://yuyu-tei.jp/"
const wsDeckUrl = "http://wsdecks.com/"

type Prox struct {
	// target url of reverse proxy
	target *url.URL
	// instance of Go ReverseProxy thatwill do the job for us
	proxy *httputil.ReverseProxy
}

func New(target string) *Prox {
	url, _ := url.Parse(target)
	// you should handle error on parsing
	return &Prox{target: url, proxy: httputil.NewSingleHostReverseProxy(url)}
}

func (p *Prox) handle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("X-GoProxy", "GoProxy")
	// call to magic method from ReverseProxy object
	p.proxy.ServeHTTP(w, r)
}
func lowCostSystemToURL(syspath string) string {
	return strings.Replace(syspath, "\\", "/", -1)
}

func convertToJpg(filePath string) {
	// convert -density 150 -trim to_love-ru_darkness_2nd_trial_deck.pdf -quality 100 -sharpen 0x1.0 love.jpg
	cmd := exec.Command("convert", "-density", "150", "-trim", filePath, "-quality", "100", "-sharpen", "0x1.0", filePath+".jpg")
	err := cmd.Start()
	if err != nil {
		fmt.Println(err)
	}
	err = cmd.Wait()
}

func main() {
	proxy := New("http://localhost:8080")
	os.MkdirAll(filepath.Join("static", "yuyutei"), 0744)
	os.MkdirAll(filepath.Join("static", "wsdeck"), 0744)
	// static := http.FileServer(http.Dir("./"))
	http.HandleFunc("/", proxy.handle)

	http.HandleFunc("/translationimages", func(w http.ResponseWriter, r *http.Request) {
		file := r.PostFormValue("file")
		filename := r.PostFormValue("filename")
		uid := strings.Replace(filename, filepath.Ext(filename), "", 1)
		dir := filepath.Join("static", uid)
		filePath := filepath.Join(dir, filename)

		data, err := dataurl.DecodeString(file)
		if err != nil {
			fmt.Println(err)
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		// fmt.Println(dataURL.Data)
		os.MkdirAll(dir, 0777)
		ioutil.WriteFile(filePath, data.Data, 0644)
		if _, err := os.Stat(dir); os.IsNotExist(err) {
			convertToJpg(filePath)
		}
		if err != nil {
			fmt.Println(err)
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		listJpg, err := filepath.Glob(filePath + "*.jpg")
		if err != nil {
			fmt.Println(err)
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
		for i, jpgfile := range listJpg {
			listJpg[i] = lowCostSystemToURL(jpgfile)
		}

		b, err := json.Marshal(listJpg)

		if err != nil {
			fmt.Println(err)
			http.Error(w, err.Error(), 500)
			return
		}

		w.Write(b)
	})

	http.HandleFunc("/cardimages", func(w http.ResponseWriter, r *http.Request) {
		var wg sync.WaitGroup

		result := []string{}
		link := r.PostFormValue("url")
		classCSS := r.PostFormValue("class_css")
		filter := ""

		if classCSS == "" {
			classCSS = ".card_list_box"
		}

		if link != "" {
			doc, err := goquery.NewDocument(link)
			uid := ""
			site := ""
			imageURL := ""

			if strings.Contains(link, yuyuteiURL) {
				site = "yuyutei"
				filter = classCSS + " .image img"
				parsedURL, _ := url.Parse(link)
				values, _ := url.ParseQuery(parsedURL.RawQuery)
				uid = values.Get("ver")
			} else if strings.Contains(link, wsDeckUrl) {
				site = "wsdeck"
				filter = ".wscard" + " img"
				uid = filepath.Base(link)
			}
			// currentDir, _ := os.Getwd()
			dir := filepath.Join("static", site, uid)

			if filter == "" {
				http.Error(w, fmt.Sprintln("Url is not supported", link), 500)
			}

			if err != nil {
				fmt.Println("Nope")
			}
			if _, err := os.Stat(dir); os.IsNotExist(err) {
				os.MkdirAll(dir, 0744)
				doc.Find(filter).Each(func(i int, s *goquery.Selection) {
					wg.Add(1)
					val, _ := s.Attr("src")
					if site == "yuyutei" {
						big := strings.Replace(val, "90_126", "front", 1)
						imageURL = yuyuteiURL + big
					} else if site == "wsdeck" {
						imageURL = wsDeckUrl + val
					}

					go func(url string) {
						defer wg.Done()
						// fmt.Println("dir : ", dir)
						fileName := filepath.Join(dir, path.Base(url))
						out, err := os.Create(fileName)
						if err != nil {
							fmt.Println(err)
						}
						defer out.Close()
						reps, err := http.Get(url)

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

						file, err := io.Copy(out, reps.Body)
						if err != nil {
							fmt.Println(err)
						}

						fmt.Println("File", file)
						// fmt.Printf("Link: n-%d __ %v%v\n", i, imageURL, uid)
						defer reps.Body.Close()
						// fmt.Println("image url: ", strings.Replace(fileName, "\\", "/", 1))
						result = append(result, lowCostSystemToURL(fileName))
					}(imageURL)
				})
			} else {
				files, err := ioutil.ReadDir(dir)
				if err != nil {
					fmt.Println(err)
				}
				for _, file := range files {
					absPath := filepath.Join(dir, file.Name())
					urlPath := lowCostSystemToURL(absPath)
					result = append(result, urlPath)
				}

			}
		}

		wg.Wait()
		fmt.Printf("Finish")
		b, err := json.Marshal(result)

		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}
		w.Write(b)
	})

	http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
		// fmt.Println("static", r.URL.Path[1:])
		http.ServeFile(w, r, r.URL.Path[1:])
	})

	http.ListenAndServe(":8010", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"runtime"
	"runtime/pprof"
	"sort"
	"strconv"
	"strings"
	"sync"
)

const (
	CmdDisplayMax = 32
	// max length of /proc/$PID/comm
	CommMax = 16
	// from ps_mem - average error due to truncation in the kernel
	// pss calculations
	PssAdjust    = .5
	pageSize     = 4096
	mapDetailLen = len("Size:                  4 kB")

	usage = `Usage: %s [OPTION...]
Simple, accurate RAM and swap reporting.

Options:
`
)

var (
	memProfile string
	cpuProfile string
)

// store info about a command (group of processes), similar to how
// ps_mem works.
type CmdMemInfo struct {
	PIDs    []int
	Name    string
	Pss     float64
	Private uint64
	Swapped uint64
}

type MapInfo struct {
	Inode uint64
	Name  string
}

// mapLine is a line from /proc/$PID/maps, or one of the same header
// lines from smaps.
func NewMapInfo(mapLine []byte) MapInfo {
	var mi MapInfo
	var err error
	pieces := splitSpaces(mapLine)
	if len(pieces) == 6 {
		mi.Name = string(pieces[5])
	}
	mi.Inode, err = ParseUint(pieces[4], 10, 64)
	if err != nil {
		panic(fmt.Sprintf("NewMapInfo: Atoi(%s): %s (%s)",
			string(pieces[4]), err, string(mapLine)))
	}
	return mi
}

func (mi MapInfo) IsAnon() bool {
	return mi.Inode == 0
}

// isDigit returns true if the rune d represents an ascii digit
// between 0 and 9, inclusive.
func isDigit(d uint8) bool {
	return d >= '0' && d <= '9'
}

// pidList returns a list of the process-IDs of every currently
// running process on the local system.
func pidList() ([]int, error) {
	procLs, err := ioutil.ReadDir("/proc")
	if err != nil {
		return nil, fmt.Errorf("ReadDir(/proc): %s", err)
	}

	pids := make([]int, 0, len(procLs))
	for _, pInfo := range procLs {
		if !isDigit(pInfo.Name()[0]) || !pInfo.IsDir() {
			continue
		}
		pidInt, err := strconv.Atoi(pInfo.Name())
		if err != nil {
			return nil, fmt.Errorf("Atoi(%s): %s", pInfo.Name(), err)
		}
		pids = append(pids, pidInt)
	}
	return pids, nil
}

// procName gets the process name for a worker.  It first checks the
// value of /proc/$PID/cmdline.  If setproctitle(3) has been called,
// it will use this.  Otherwise it uses the value of
// path.Base(/proc/$PID/exe), which has info on whether the executable
// has changed since the process was exec'ed.
func procName(pid int) (string, error) {
	p, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
	// this would return an error if the PID doesn't
	// exist, or if the PID refers to a kernel thread.
	if err != nil {
		return "", nil
	}
	// cmdline is the null separated list of command line
	// arguments for the process, unless setproctitle(3)
	// has been called, in which case it is the
	argsB, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
	if err != nil {
		return "", fmt.Errorf("ReadFile(%s): %s", fmt.Sprintf("/proc/%d/cmdline", pid), err)
	}
	args := strings.Split(string(argsB), "\000")
	n := args[0]

	nTrunc := n
	if len(n) > CommMax {
		nTrunc = n[:CommMax]
	}
	if strings.HasPrefix(p, nTrunc) {
		n = path.Base(p)
	}
	return n, nil
}

// splitSpaces returns a slice of byte slices which are the space
// delimited words from the original byte slice.  Unlike
// strings.Split($X, " "), runs of multiple spaces in a row are
// discarded.  NOTE WELL: this only checks for spaces (' '), other
// unicode whitespace isn't supported.
func splitSpaces(b []byte) [][]byte {
	res := make([][]byte, 0, 6) // 6 is empirically derived
	start, i := 0, 0
	lenB := len(b)
	for i = 0; i < lenB; i++ {
		// fast forward past any spaces
		for i < lenB-1 && b[i] == ' ' {
			i++
			start = i
		}
		for i < lenB-1 && b[i] != ' ' {
			i++
		}
		if i > start {
			// we sometimes have to rewind
			if i < lenB-1 && b[i] == ' ' {
				i--
			}
			res = append(res, b[start:i+1])
			start = i + 1
		}
	}
	return res
}

// procMem returns the amount of Pss, shared, and swapped out memory
// used.  The swapped out amount refers to anonymous pages only.
func procMem(pid int) (pss float64, priv, swap uint64, err error) {
	fPath := fmt.Sprintf("/proc/%d/smaps", pid)
	f, err := os.Open(fPath)
	if err != nil {
		err = fmt.Errorf("os.Open(%s): %s", fPath, err)
		return
	}
	//var curr MapInfo
	r := bufio.NewReaderSize(f, pageSize)
	for {
		var l []byte
		var isPrefix bool
		l, isPrefix, err = r.ReadLine()
		// this should never happen, so take the easy way out.
		if isPrefix {
			err = fmt.Errorf("ReadLine(%s): isPrefix", fPath)
		}
		if err != nil {
			// if we've got EOF, then we're simply done
			// processing smaps.
			if err == io.EOF {
				err = nil
				break
			}
			// otherwise error out
			err = fmt.Errorf("ReadLine(%s): %s", fPath, err)
			return
		}

		if len(l) != mapDetailLen {
			//curr = NewMapInfo(l)
			continue
		}
		pieces := splitSpaces(l)
		ty := string(pieces[0])
		var v uint64
		switch ty {
		case "Pss:":
			v, err = ParseUint(pieces[1], 10, 64)
			if err != nil {
				err = fmt.Errorf("Atoi(%s): %s", string(pieces[1]), err)
				return
			}
			pss += float64(v) + PssAdjust
		case "Private_Clean:", "Private_Dirty:":
			v, err = ParseUint(pieces[1], 10, 64)
			if err != nil {
				err = fmt.Errorf("Atoi(%s): %s", string(pieces[1]), err)
				return
			}
			priv += v
		case "Swap:":
			v, err = ParseUint(pieces[1], 10, 64)
			if err != nil {
				err = fmt.Errorf("Atoi(%s): %s", string(pieces[1]), err)
				return
			}
			swap += v
		}
	}
	return
}

func worker(pidRequest chan int, wg *sync.WaitGroup, result chan *CmdMemInfo) {
	for pid := range pidRequest {
		var err error
		cmi := new(CmdMemInfo)

		cmi.PIDs = []int{pid}
		cmi.Name, err = procName(pid)
		if err != nil {
			log.Printf("procName(%d): %s", pid, err)
			wg.Done()
			continue
		} else if cmi.Name == "" {
			// XXX: This happens with kernel
			// threads. maybe warn? idk.
			wg.Done()
			continue
		}

		cmi.Pss, cmi.Private, cmi.Swapped, err = procMem(pid)
		if err != nil {
			log.Printf("procMem(%d): %s", pid, err)
			wg.Done()
			continue
		}

		result <- cmi
		wg.Done()
	}
}

type byTotal []*CmdMemInfo

func (c byTotal) Len() int           { return len(c) }
func (c byTotal) Less(i, j int) bool { return c[i].Pss < c[j].Pss }
func (c byTotal) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, usage, os.Args[0])
		flag.PrintDefaults()
	}

	flag.StringVar(&memProfile, "memprofile", "",
		"write memory profile to this file")
	flag.StringVar(&cpuProfile, "cpuprofile", "",
		"write cpu profile to this file")

	flag.Parse()
}

// startProfiling enables memory and/or CPU profiling if the
// appropriate command line flags have been set.
func startProfiling() {
	var err error
	// if we've passed in filenames to dump profiling data too,
	// start collecting profiling data.
	if memProfile != "" {
		runtime.MemProfileRate = 1
	}
	if cpuProfile != "" {
		var f *os.File
		if f, err = os.Create(cpuProfile); err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
	}
}

func stopProfiling() {
	if memProfile != "" {
		runtime.GC()
		f, err := os.Create(memProfile)
		if err != nil {
			log.Println(err)
		}
		pprof.WriteHeapProfile(f)
		f.Close()
	}
	if cpuProfile != "" {
		pprof.StopCPUProfile()
		cpuProfile = ""
	}
}

func main() {
	startProfiling()
	defer stopProfiling()

	// need to be root to read map info for other user's
	// processes.
	if os.Geteuid() != 0 {
		fmt.Printf("FATAL: root required.\n")
		return
	}

	pids, err := pidList()
	if err != nil {
		log.Printf("pidList: %s", err)
		return
	}

	var wg sync.WaitGroup
	work := make(chan int, len(pids))
	result := make(chan *CmdMemInfo, len(pids))

	// give us as much parallelism as possible
	nCPU := runtime.NumCPU()
	runtime.GOMAXPROCS(nCPU)
	for i := 0; i < nCPU; i++ {
		go worker(work, &wg, result)
	}

	wg.Add(len(pids))
	for _, pid := range pids {
		work <- pid
	}
	wg.Wait()

	// aggregate similar processes by command name.
	cmdMap := map[string]*CmdMemInfo{}
loop:
	for {
		// this only works correctly because we a channel
		// where the buffer size >= the number of potential
		// results.
		select {
		case c := <-result:
			n := c.Name
			if _, ok := cmdMap[n]; !ok {
				cmdMap[n] = c
				continue
			}
			cmdMap[n].PIDs = append(cmdMap[n].PIDs, c.PIDs...)
			cmdMap[n].Pss += c.Pss
			cmdMap[n].Private += c.Private
			cmdMap[n].Swapped += c.Swapped
		default:
			break loop
		}
	}

	// extract map values to a slice so we can sort them
	cmds := make([]*CmdMemInfo, 0, len(cmdMap))
	for _, c := range cmdMap {
		cmds = append(cmds, c)
	}
	sort.Sort(byTotal(cmds))

	// keep track of total RAM and swap usage
	var totPss, totSwap float64

	fmt.Printf("%10s%10s%10s\t%s\n", "MB RAM", "PRIVATE", "SWAPPED", "PROCESS (COUNT)")
	for _, c := range cmds {
		n := c.Name
		if len(n) > CmdDisplayMax {
			if n[0] == '[' {
				n = n[:strings.IndexRune(n, ']')+1]
			} else {
				n = n[:CmdDisplayMax]
			}
		}
		s := ""
		if c.Swapped > 0 {
			swap := float64(c.Swapped) / 1024.
			totSwap += swap
			s = fmt.Sprintf("%10.1f", swap)
		}
		pss := float64(c.Pss) / 1024.
		fmt.Printf("%10.1f%10.1f%10s\t%s (%d)\n", pss, float64(c.Private)/1024., s, n, len(c.PIDs))
		totPss += pss
	}
	fmt.Printf("#%9.1f%20.1f\tTOTAL USED BY PROCESSES\n", totPss, totSwap)
}
<commit_msg>procMem: don't allocate a string just to check the type<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"runtime"
	"runtime/pprof"
	"sort"
	"strconv"
	"strings"
	"sync"
)

const (
	CmdDisplayMax = 32
	// max length of /proc/$PID/comm
	CommMax = 16
	// from ps_mem - average error due to truncation in the kernel
	// pss calculations
	PssAdjust    = .5
	pageSize     = 4096
	mapDetailLen = len("Size:                  4 kB")

	usage = `Usage: %s [OPTION...]
Simple, accurate RAM and swap reporting.

Options:
`
)

var (
	memProfile string
	cpuProfile string

	tyPss          = []byte("Pss:")
	tySwap         = []byte("Swap:")
	tyPrivateClean = []byte("Private_Clean:")
	tyPrivateDirty = []byte("Private_Dirty:")
)

// store info about a command (group of processes), similar to how
// ps_mem works.
type CmdMemInfo struct {
	PIDs    []int
	Name    string
	Pss     float64
	Private uint64
	Swapped uint64
}

type MapInfo struct {
	Inode uint64
	Name  string
}

// mapLine is a line from /proc/$PID/maps, or one of the same header
// lines from smaps.
func NewMapInfo(mapLine []byte) MapInfo {
	var mi MapInfo
	var err error
	pieces := splitSpaces(mapLine)
	if len(pieces) == 6 {
		mi.Name = string(pieces[5])
	}
	mi.Inode, err = ParseUint(pieces[4], 10, 64)
	if err != nil {
		panic(fmt.Sprintf("NewMapInfo: Atoi(%s): %s (%s)",
			string(pieces[4]), err, string(mapLine)))
	}
	return mi
}

func (mi MapInfo) IsAnon() bool {
	return mi.Inode == 0
}

// isDigit returns true if the rune d represents an ascii digit
// between 0 and 9, inclusive.
func isDigit(d uint8) bool {
	return d >= '0' && d <= '9'
}

// pidList returns a list of the process-IDs of every currently
// running process on the local system.
func pidList() ([]int, error) {
	procLs, err := ioutil.ReadDir("/proc")
	if err != nil {
		return nil, fmt.Errorf("ReadDir(/proc): %s", err)
	}

	pids := make([]int, 0, len(procLs))
	for _, pInfo := range procLs {
		if !isDigit(pInfo.Name()[0]) || !pInfo.IsDir() {
			continue
		}
		pidInt, err := strconv.Atoi(pInfo.Name())
		if err != nil {
			return nil, fmt.Errorf("Atoi(%s): %s", pInfo.Name(), err)
		}
		pids = append(pids, pidInt)
	}
	return pids, nil
}

// procName gets the process name for a worker.  It first checks the
// value of /proc/$PID/cmdline.  If setproctitle(3) has been called,
// it will use this.  Otherwise it uses the value of
// path.Base(/proc/$PID/exe), which has info on whether the executable
// has changed since the process was exec'ed.
func procName(pid int) (string, error) {
	p, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
	// this would return an error if the PID doesn't
	// exist, or if the PID refers to a kernel thread.
	if err != nil {
		return "", nil
	}
	// cmdline is the null separated list of command line
	// arguments for the process, unless setproctitle(3)
	// has been called, in which case it is the
	argsB, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
	if err != nil {
		return "", fmt.Errorf("ReadFile(%s): %s", fmt.Sprintf("/proc/%d/cmdline", pid), err)
	}
	args := strings.Split(string(argsB), "\000")
	n := args[0]

	nTrunc := n
	if len(n) > CommMax {
		nTrunc = n[:CommMax]
	}
	if strings.HasPrefix(p, nTrunc) {
		n = path.Base(p)
	}
	return n, nil
}

// splitSpaces returns a slice of byte slices which are the space
// delimited words from the original byte slice.  Unlike
// strings.Split($X, " "), runs of multiple spaces in a row are
// discarded.  NOTE WELL: this only checks for spaces (' '), other
// unicode whitespace isn't supported.
func splitSpaces(b []byte) [][]byte {
	res := make([][]byte, 0, 6) // 6 is empirically derived
	start, i := 0, 0
	lenB := len(b)
	for i = 0; i < lenB; i++ {
		// fast forward past any spaces
		for i < lenB-1 && b[i] == ' ' {
			i++
			start = i
		}
		for i < lenB-1 && b[i] != ' ' {
			i++
		}
		if i > start {
			// we sometimes have to rewind
			if i < lenB-1 && b[i] == ' ' {
				i--
			}
			res = append(res, b[start:i+1])
			start = i + 1
		}
	}
	return res
}

// procMem returns the amount of Pss, shared, and swapped out memory
// used.  The swapped out amount refers to anonymous pages only.
func procMem(pid int) (pss float64, priv, swap uint64, err error) {
	fPath := fmt.Sprintf("/proc/%d/smaps", pid)
	f, err := os.Open(fPath)
	if err != nil {
		err = fmt.Errorf("os.Open(%s): %s", fPath, err)
		return
	}
	//var curr MapInfo
	r := bufio.NewReaderSize(f, pageSize)
	for {
		var l []byte
		var isPrefix bool
		l, isPrefix, err = r.ReadLine()
		// this should never happen, so take the easy way out.
		if isPrefix {
			err = fmt.Errorf("ReadLine(%s): isPrefix", fPath)
		}
		if err != nil {
			// if we've got EOF, then we're simply done
			// processing smaps.
			if err == io.EOF {
				err = nil
				break
			}
			// otherwise error out
			err = fmt.Errorf("ReadLine(%s): %s", fPath, err)
			return
		}

		if len(l) != mapDetailLen {
			//curr = NewMapInfo(l)
			continue
		}
		pieces := splitSpaces(l)
		ty := pieces[0]
		var v uint64
		if bytes.Equal(ty, tyPss) {
			v, err = ParseUint(pieces[1], 10, 64)
			if err != nil {
				err = fmt.Errorf("Atoi(%s): %s", string(pieces[1]), err)
				return
			}
			pss += float64(v) + PssAdjust
		} else if bytes.Equal(ty, tyPrivateClean) || bytes.Equal(ty, tyPrivateDirty) {
			v, err = ParseUint(pieces[1], 10, 64)
			if err != nil {
				err = fmt.Errorf("Atoi(%s): %s", string(pieces[1]), err)
				return
			}
			priv += v
		} else if bytes.Equal(ty, tySwap) {
			v, err = ParseUint(pieces[1], 10, 64)
			if err != nil {
				err = fmt.Errorf("Atoi(%s): %s", string(pieces[1]), err)
				return
			}
			swap += v
		}
	}
	return
}

func worker(pidRequest chan int, wg *sync.WaitGroup, result chan *CmdMemInfo) {
	for pid := range pidRequest {
		var err error
		cmi := new(CmdMemInfo)

		cmi.PIDs = []int{pid}
		cmi.Name, err = procName(pid)
		if err != nil {
			log.Printf("procName(%d): %s", pid, err)
			wg.Done()
			continue
		} else if cmi.Name == "" {
			// XXX: This happens with kernel
			// threads. maybe warn? idk.
			wg.Done()
			continue
		}

		cmi.Pss, cmi.Private, cmi.Swapped, err = procMem(pid)
		if err != nil {
			log.Printf("procMem(%d): %s", pid, err)
			wg.Done()
			continue
		}

		result <- cmi
		wg.Done()
	}
}

type byTotal []*CmdMemInfo

func (c byTotal) Len() int           { return len(c) }
func (c byTotal) Less(i, j int) bool { return c[i].Pss < c[j].Pss }
func (c byTotal) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, usage, os.Args[0])
		flag.PrintDefaults()
	}

	flag.StringVar(&memProfile, "memprofile", "",
		"write memory profile to this file")
	flag.StringVar(&cpuProfile, "cpuprofile", "",
		"write cpu profile to this file")

	flag.Parse()
}

// startProfiling enables memory and/or CPU profiling if the
// appropriate command line flags have been set.
func startProfiling() {
	var err error
	// if we've passed in filenames to dump profiling data too,
	// start collecting profiling data.
	if memProfile != "" {
		runtime.MemProfileRate = 1
	}
	if cpuProfile != "" {
		var f *os.File
		if f, err = os.Create(cpuProfile); err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
	}
}

func stopProfiling() {
	if memProfile != "" {
		runtime.GC()
		f, err := os.Create(memProfile)
		if err != nil {
			log.Println(err)
		}
		pprof.WriteHeapProfile(f)
		f.Close()
	}
	if cpuProfile != "" {
		pprof.StopCPUProfile()
		cpuProfile = ""
	}
}

func main() {
	startProfiling()
	defer stopProfiling()

	// need to be root to read map info for other user's
	// processes.
	if os.Geteuid() != 0 {
		fmt.Printf("FATAL: root required.\n")
		return
	}

	pids, err := pidList()
	if err != nil {
		log.Printf("pidList: %s", err)
		return
	}

	var wg sync.WaitGroup
	work := make(chan int, len(pids))
	result := make(chan *CmdMemInfo, len(pids))

	// give us as much parallelism as possible
	nCPU := runtime.NumCPU()
	runtime.GOMAXPROCS(nCPU)
	for i := 0; i < nCPU; i++ {
		go worker(work, &wg, result)
	}

	wg.Add(len(pids))
	for _, pid := range pids {
		work <- pid
	}
	wg.Wait()

	// aggregate similar processes by command name.
	cmdMap := map[string]*CmdMemInfo{}
loop:
	for {
		// this only works correctly because we a channel
		// where the buffer size >= the number of potential
		// results.
		select {
		case c := <-result:
			n := c.Name
			if _, ok := cmdMap[n]; !ok {
				cmdMap[n] = c
				continue
			}
			cmdMap[n].PIDs = append(cmdMap[n].PIDs, c.PIDs...)
			cmdMap[n].Pss += c.Pss
			cmdMap[n].Private += c.Private
			cmdMap[n].Swapped += c.Swapped
		default:
			break loop
		}
	}

	// extract map values to a slice so we can sort them
	cmds := make([]*CmdMemInfo, 0, len(cmdMap))
	for _, c := range cmdMap {
		cmds = append(cmds, c)
	}
	sort.Sort(byTotal(cmds))

	// keep track of total RAM and swap usage
	var totPss, totSwap float64

	fmt.Printf("%10s%10s%10s\t%s\n", "MB RAM", "PRIVATE", "SWAPPED", "PROCESS (COUNT)")
	for _, c := range cmds {
		n := c.Name
		if len(n) > CmdDisplayMax {
			if n[0] == '[' {
				n = n[:strings.IndexRune(n, ']')+1]
			} else {
				n = n[:CmdDisplayMax]
			}
		}
		s := ""
		if c.Swapped > 0 {
			swap := float64(c.Swapped) / 1024.
			totSwap += swap
			s = fmt.Sprintf("%10.1f", swap)
		}
		pss := float64(c.Pss) / 1024.
		fmt.Printf("%10.1f%10.1f%10s\t%s (%d)\n", pss, float64(c.Private)/1024., s, n, len(c.PIDs))
		totPss += pss
	}
	fmt.Printf("#%9.1f%20.1f\tTOTAL USED BY PROCESSES\n", totPss, totSwap)
}
<|endoftext|>