<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/joho/godotenv"
	"github.com/urfave/cli"
)

var build = "0" // build number set at compile-time

func main() {
	app := cli.NewApp()
	app.Name = "helm plugin"
	app.Usage = "helm plugin"
	app.Action = run
	app.Version = fmt.Sprintf("1.0.%s", build)
	app.Flags = []cli.Flag{
		cli.StringSliceFlag{
			Name:   "helm_command",
			Usage:  "add the command Helm has to execute",
			EnvVar: "PLUGIN_HELM_COMMAND,HELM_COMMAND",
		},
		cli.StringFlag{
			Name:   "kube-config",
			Usage:  "Kubernetes configuration file path",
			EnvVar: "PLUGIN_KUBE_CONFIG,KUBE_CONFIG",
			Value:  "/root/.kube/config",
		},
		cli.StringFlag{
			Name:   "namespace",
			Usage:  "Kubernetes namespace",
			EnvVar: "PLUGIN_NAMESPACE,NAMESPACE",
		},
		cli.StringFlag{
			Name:   "release",
			Usage:  "Kubernetes helm release",
			EnvVar: "PLUGIN_RELEASE,RELEASE",
		},
		cli.StringFlag{
			Name:   "chart",
			Usage:  "Kubernetes helm chart name",
			EnvVar: "PLUGIN_CHART,CHART",
		},
		cli.StringFlag{
			Name:   "chart-version",
			Usage:  "specify the exact chart version to use. If this is not specified, the latest version is used",
			EnvVar: "PLUGIN_CHART_VERSION,CHART_VERSION",
		},
		cli.StringFlag{
			Name:   "values",
			Usage:  "Kubernetes helm release",
			EnvVar: "PLUGIN_VALUES,VALUES",
		},
		cli.StringFlag{
			Name:   "values_files",
			Usage:  "Helm values override files",
			EnvVar: "PLUGIN_VALUES_FILES,VALUES_FILES",
		},
		cli.BoolFlag{
			Name:   "skip_tls_verify",
			Usage:  "Skip TLS verification",
			EnvVar: "PLUGIN_SKIP_TLS_VERIFY,SKIP_TLS_VERIFY",
		},
		cli.BoolFlag{
			Name:   "debug",
			Usage:  "Debug",
			EnvVar: "PLUGIN_DEBUG,DEBUG",
		},
		cli.BoolFlag{
			Name:   "dry-run",
			Usage:  "Helm dry-run",
			EnvVar: "PLUGIN_DRY_RUN,DRY_RUN",
		},
		cli.StringFlag{
			Name:   "prefix",
			Usage:  "Prefix for all the secrets",
			EnvVar: "PLUGIN_PREFIX,PREFIX",
		},
		cli.StringFlag{
			Name:   "tiller-ns",
			Usage:  "Namespace to install Tiller",
			EnvVar: "PLUGIN_TILLER_NS,TILLER_NS",
		},
		cli.BoolFlag{
			Name:   "wait",
			Usage:  "if set, will wait until all Pods, PVCs, and Services are in a ready state before marking the release as successful.",
			EnvVar: "PLUGIN_WAIT,WAIT",
		},
		cli.BoolFlag{
			Name:   "recreate-pods",
			Usage:  "performs pods restart for the resource if applicable",
			EnvVar: "PLUGIN_RECREATE_PODS,RECREATE_PODS",
		},
		cli.BoolFlag{
			Name:   "upgrade",
			Usage:  "if set, will upgrade tiller to the latest version",
			EnvVar: "PLUGIN_UPGRADE,UPGRADE",
		},
		cli.BoolFlag{
			Name:   "client-only",
			Usage:  "if set, Helm will use the canary tiller image",
			EnvVar: "PLUGIN_CANARY_IMAGE,CANARY_IMAGE",
		},
		cli.BoolFlag{
			Name:   "canary-image",
			Usage:  "if set, it will initilises helm in the client side only",
			EnvVar: "PLUGIN_CLIENT_ONLY,CLIENT_ONLY",
		},
		cli.BoolFlag{
			Name:   "reuse-values",
			Usage:  "when upgrading, reuse the last release's values, and merge in any new values",
			EnvVar: "PLUGIN_REUSE_VALUES,REUSE_VALUES",
		},
		cli.StringFlag{
			Name:   "timeout",
			Usage:  "time in seconds to wait for any individual kubernetes operation (like Jobs for hooks) (default 300)",
			EnvVar: "PLUGIN_TIMEOUT,TIMEOUT",
		},
		cli.BoolFlag{
			Name:   "force",
			Usage:  "force resource update through delete/recreate if needed",
			EnvVar: "PLUGIN_FORCE,FORCE",
		},
	}
	if err := app.Run(os.Args); err != nil {
		logrus.Fatal(err)
	}
}

func run(c *cli.Context) error {
	if c.String("env-file") != "" {
		_ = godotenv.Load(c.String("env-file"))
	}
	plugin := Plugin{
		Config: Config{
			APIServer:      c.String("api_server"),
			Token:          c.String("token"),
			ServiceAccount: c.String("service-account"),
			KubeConfig:     c.String("kube-config"),
			HelmCommand:    c.StringSlice("helm_command"),
			Namespace:      c.String("namespace"),
			SkipTLSVerify:  c.Bool("skip_tls_verify"),
			Values:         c.String("values"),
			ValuesFiles:    c.String("values_files"),
			Release:        c.String("release"),
			Chart:          c.String("chart"),
			Version:        c.String("chart-version"),
			Debug:          c.Bool("debug"),
			DryRun:         c.Bool("dry-run"),
			Secrets:        c.StringSlice("secrets"),
			Prefix:         c.String("prefix"),
			TillerNs:       c.String("tiller-ns"),
			Wait:           c.Bool("wait"),
			RecreatePods:   c.Bool("recreate-pods"),
			ClientOnly:     c.Bool("client-only"),
			CanaryImage:    c.Bool("canary-image"),
			Upgrade:        c.Bool("upgrade"),
			ReuseValues:    c.Bool("reuse-values"),
			Timeout:        c.String("timeout"),
			Force:          c.Bool("force"),
		},
	}
	resolveSecrets(&plugin)
	if plugin.Config.Debug {
		plugin.debug()
	}
	return plugin.Exec()
}
<commit_msg>Remove duplicate code<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/joho/godotenv"
	"github.com/urfave/cli"
)

var build = "0" // build number set at compile-time

func main() {
	app := cli.NewApp()
	app.Name = "helm plugin"
	app.Usage = "helm plugin"
	app.Action = run
	app.Version = fmt.Sprintf("1.0.%s", build)
	app.Flags = []cli.Flag{
		cli.StringSliceFlag{
			Name:   "helm_command",
			Usage:  "add the command Helm has to execute",
			EnvVar: "PLUGIN_HELM_COMMAND,HELM_COMMAND",
		},
		cli.StringFlag{
			Name:   "kube-config",
			Usage:  "Kubernetes configuration file path",
			EnvVar: "PLUGIN_KUBE_CONFIG,KUBE_CONFIG",
			Value:  "/root/.kube/config",
		},
		cli.StringFlag{
			Name:   "namespace",
			Usage:  "Kubernetes namespace",
			EnvVar: "PLUGIN_NAMESPACE,NAMESPACE",
		},
		cli.StringFlag{
			Name:   "release",
			Usage:  "Kubernetes helm release",
			EnvVar: "PLUGIN_RELEASE,RELEASE",
		},
		cli.StringFlag{
			Name:   "chart",
			Usage:  "Kubernetes helm chart name",
			EnvVar: "PLUGIN_CHART,CHART",
		},
		cli.StringFlag{
			Name:   "chart-version",
			Usage:  "specify the exact chart version to use. If this is not specified, the latest version is used",
			EnvVar: "PLUGIN_CHART_VERSION,CHART_VERSION",
		},
		cli.StringFlag{
			Name:   "values",
			Usage:  "Kubernetes helm release",
			EnvVar: "PLUGIN_VALUES,VALUES",
		},
		cli.StringFlag{
			Name:   "values_files",
			Usage:  "Helm values override files",
			EnvVar: "PLUGIN_VALUES_FILES,VALUES_FILES",
		},
		cli.BoolFlag{
			Name:   "skip_tls_verify",
			Usage:  "Skip TLS verification",
			EnvVar: "PLUGIN_SKIP_TLS_VERIFY,SKIP_TLS_VERIFY",
		},
		cli.BoolFlag{
			Name:   "debug",
			Usage:  "Debug",
			EnvVar: "PLUGIN_DEBUG,DEBUG",
		},
		cli.BoolFlag{
			Name:   "dry-run",
			Usage:  "Helm dry-run",
			EnvVar: "PLUGIN_DRY_RUN,DRY_RUN",
		},
		cli.StringFlag{
			Name:   "prefix",
			Usage:  "Prefix for all the secrets",
			EnvVar: "PLUGIN_PREFIX,PREFIX",
		},
		cli.StringFlag{
			Name:   "tiller-ns",
			Usage:  "Namespace to install Tiller",
			EnvVar: "PLUGIN_TILLER_NS,TILLER_NS",
		},
		cli.BoolFlag{
			Name:   "wait",
			Usage:  "if set, will wait until all Pods, PVCs, and Services are in a ready state before marking the release as successful.",
			EnvVar: "PLUGIN_WAIT,WAIT",
		},
		cli.BoolFlag{
			Name:   "recreate-pods",
			Usage:  "performs pods restart for the resource if applicable",
			EnvVar: "PLUGIN_RECREATE_PODS,RECREATE_PODS",
		},
		cli.BoolFlag{
			Name:   "upgrade",
			Usage:  "if set, will upgrade tiller to the latest version",
			EnvVar: "PLUGIN_UPGRADE,UPGRADE",
		},
		cli.BoolFlag{
			Name:   "client-only",
			Usage:  "if set, Helm will use the canary tiller image",
			EnvVar: "PLUGIN_CANARY_IMAGE,CANARY_IMAGE",
		},
		cli.BoolFlag{
			Name:   "canary-image",
			Usage:  "if set, it will initilises helm in the client side only",
			EnvVar: "PLUGIN_CLIENT_ONLY,CLIENT_ONLY",
		},
		cli.BoolFlag{
			Name:   "reuse-values",
			Usage:  "when upgrading, reuse the last release's values, and merge in any new values",
			EnvVar: "PLUGIN_REUSE_VALUES,REUSE_VALUES",
		},
		cli.StringFlag{
			Name:   "timeout",
			Usage:  "time in seconds to wait for any individual kubernetes operation (like Jobs for hooks) (default 300)",
			EnvVar: "PLUGIN_TIMEOUT,TIMEOUT",
		},
		cli.BoolFlag{
			Name:   "force",
			Usage:  "force resource update through delete/recreate if needed",
			EnvVar: "PLUGIN_FORCE,FORCE",
		},
	}
	if err := app.Run(os.Args); err != nil {
		logrus.Fatal(err)
	}
}

func run(c *cli.Context) error {
	if c.String("env-file") != "" {
		_ = godotenv.Load(c.String("env-file"))
	}
	plugin := Plugin{
		Config: Config{
			APIServer:      c.String("api_server"),
			Token:          c.String("token"),
			ServiceAccount: c.String("service-account"),
			KubeConfig:     c.String("kube-config"),
			HelmCommand:    c.StringSlice("helm_command"),
			Namespace:      c.String("namespace"),
			SkipTLSVerify:  c.Bool("skip_tls_verify"),
			Values:         c.String("values"),
			ValuesFiles:    c.String("values_files"),
			Release:        c.String("release"),
			Chart:          c.String("chart"),
			Version:        c.String("chart-version"),
			Debug:          c.Bool("debug"),
			DryRun:         c.Bool("dry-run"),
			Secrets:        c.StringSlice("secrets"),
			Prefix:         c.String("prefix"),
			TillerNs:       c.String("tiller-ns"),
			Wait:           c.Bool("wait"),
			RecreatePods:   c.Bool("recreate-pods"),
			ClientOnly:     c.Bool("client-only"),
			CanaryImage:    c.Bool("canary-image"),
			Upgrade:        c.Bool("upgrade"),
			ReuseValues:    c.Bool("reuse-values"),
			Timeout:        c.String("timeout"),
			Force:          c.Bool("force"),
		},
	}
	return plugin.Exec()
}
<|endoftext|>


<commit_before>// Copyright 2016 Liam Stanley <me@liamstanley.io>. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

// Package girc provides a high level, yet flexible IRC library for use
// with interacting with IRC servers. girc has support for user/channel
// tracking, as well as a few other neat features (like auto-reconnect).
//
// Much of what girc can do, can also be disabled. The goal is to
// provide a solid API that you don't necessarily have to work with out
// of the box if you don't want to.
//
// See "example/main.go" for a brief and very useful example taking
// advantage of girc, that should give you a general idea of how the API
// works.
package girc

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"sync"
	"time"
)

// Client contains all of the information necessary to run a single IRC
// client.
type Client struct {
	// Config represents the configuration
	Config Config
	// Events is a buffer of events waiting to be processed.
	Events chan *Event
	// Sender is a Sender{} interface implementation.
	Sender Sender

	// state represents the throw-away state for the irc session.
	state *state
	// initTime represents the creation time of the client.
	initTime time.Time

	// cbLock is the internal locking mechanism for the callbacks map.
	cbMux sync.Mutex
	// callbacks is an internal mapping of COMMAND -> callback.
	callbacks map[string][]Callback
	// internalCallbacks is a list of callbacks used internally.
	internalCallbacks []string

	// reader is the socket buffer reader from the IRC server.
	reader *Decoder
	// reader is the socket buffer write to the IRC server.
	writer *Encoder
	// conn is a net.Conn reference to the IRC server.
	conn net.Conn
	// tries represents the internal reconnect count to the IRC server.
	tries int
	// log is used if a writer is supplied for Client.Config.Logger.
	log *log.Logger
	// quitChan is used to close the connection to the IRC server.
	quitChan chan struct{}
	// hasQuit is used to determine if we've finished quitting/cleaning up.
	hasQuit bool
}

// Config contains configuration options for an IRC client
type Config struct {
	// Server is a host/ip of the server you want to connect to.
	Server string
	// Port is the port that will be used during server connection.
	Port int
	// Password is the server password used to authenticate.
	Password string
	// Nick is an rfc-valid nickname used during connect.
	Nick string
	// User is the username/ident to use on connect. Ignored if identd server
	// is used.
	User string
	// Name is the "realname" that's used during connect.
	Name string
	// TLSConfig is an optional user-supplied tls configuration, used during
	// socket creation to the server.
	TLSConfig *tls.Config
	// MaxRetries is the number of times the client will attempt to reconnect
	// to the server after the last disconnect.
	MaxRetries int
	// Logger is an optional, user supplied logger to log the raw lines sent
	// from the server. Useful for debugging. Defaults to ioutil.Discard.
	Logger io.Writer
	// ReconnectDelay is the a duration of time to delay before attempting a
	// reconnection. Defaults to 10s (minimum of 10s).
	ReconnectDelay time.Duration
	// DisableTracking disables all channel and user-level tracking. Useful
	// for highly embedded scripts with single purposes.
	DisableTracking bool
	// DisableCapTracking disables all network/server capability tracking.
	// This includes determining what feature the IRC server supports, what
	// the "NETWORK=" variables are, and other useful stuff.
	DisableCapTracking bool
	// DisableNickCollision disables the clients auto-response to nickname
	// collisions. For example, if "test" is already in use, or is blocked by
	// the network/a service, the client will try and use "test_", then it
	// will attempt "test__", "test___", and so on.
	DisableNickCollision bool
}

// New creates a new IRC client with the specified server, name and
// config.
func New(config Config) *Client {
	client := &Client{
		Config:    config,
		Events:    make(chan *Event, 40), // buffer 40 events
		quitChan:  make(chan struct{}),
		callbacks: make(map[string][]Callback),
		tries:     0,
		initTime:  time.Now(),
	}

	if client.Config.Logger == nil {
		client.Config.Logger = ioutil.Discard
	}
	client.log = log.New(client.Config.Logger, "", log.Ldate|log.Ltime|log.Lshortfile)

	// Register builtin helpers.
	client.registerHelpers()

	return client
}

// Quit disconnects from the server.s
func (c *Client) Quit(message string) {
	c.Send(&Event{Command: QUIT, Trailing: message})

	c.hasQuit = true

	if c.conn != nil {
		c.conn.Close()
	}

	c.quitChan <- struct{}{}
}

// Uptime returns the amount of time that has passed since the
// client was created.
func (c *Client) Uptime() time.Duration {
	return time.Since(c.initTime)
}

// Server returns the string representation of host+port pair for net.Conn
func (c *Client) Server() string {
	return fmt.Sprintf("%s:%d", c.Config.Server, c.Config.Port)
}

// Send sends an event to the server. Use Client.RunCallback() if you are
// are simply looking to trigger callbacks with an event.
func (c *Client) Send(event *Event) error {
	// log the event
	if !event.Sensitive {
		c.log.Print("--> ", event.String())
	}

	return c.Sender.Send(event)
}

// Connect attempts to connect to the given IRC server
func (c *Client) Connect() error {
	var conn net.Conn
	var err error

	// Sanity check a few options.
	if c.Config.Server == "" || c.Config.Port == 0 || c.Config.Nick == "" || c.Config.User == "" {
		return errors.New("invalid configuration (server/port/nick/user)")
	}

	// Reset the state.
	c.state = newState()

	if c.Config.TLSConfig == nil {
		conn, err = net.Dial("tcp", c.Server())
	} else {
		conn, err = tls.Dial("tcp", c.Server(), c.Config.TLSConfig)
	}
	if err != nil {
		return err
	}

	c.conn = conn
	c.reader = NewDecoder(conn)
	c.writer = NewEncoder(conn)
	c.Sender = serverSender{writer: c.writer}
	for _, event := range c.connectMessages() {
		if err := c.Send(event); err != nil {
			return err
		}
	}

	c.tries = 0
	go c.ReadLoop()

	// Consider the connection a success at this point.
	c.state.connected = true

	return nil
}

// connectMessages is a list of IRC messages to send when attempting
// to connect to the IRC server.
func (c *Client) connectMessages() (events []*Event) {
	// Passwords first.
	if c.Config.Password != "" {
		events = append(events, &Event{Command: PASS, Params: []string{c.Config.Password}})
	}

	// Then nickname.
	events = append(events, &Event{Command: NICK, Params: []string{c.Config.Nick}})

	// Then username and realname.
	if c.Config.Name == "" {
		c.Config.Name = c.Config.User
	}

	events = append(events, &Event{
		Command:  USER,
		Params:   []string{c.Config.User, "+iw", "*"},
		Trailing: c.Config.Name,
	})

	return events
}

// Reconnect checks to make sure we want to, and then attempts to
// reconnect to the server.
func (c *Client) Reconnect() (err error) {
	if c.hasQuit {
		return nil
	}

	if c.Config.ReconnectDelay < (10 * time.Second) {
		c.Config.ReconnectDelay = 10 * time.Second
	}

	if c.Config.MaxRetries > 0 {
		var err error
		c.conn.Close()

		// Re-setup events.
		c.Events = make(chan *Event, 40)

		// Delay so we're not slaughtering the server with a bunch of
		// connections.
		c.log.Printf("reconnecting to %s in %s", c.Server(), c.Config.ReconnectDelay)
		time.Sleep(c.Config.ReconnectDelay)

		for err = c.Connect(); err != nil && c.tries < c.Config.MaxRetries; c.tries++ {
			c.log.Printf("reconnecting to %s in %s (%d tries)", c.Server(), c.Config.ReconnectDelay, c.tries)
			time.Sleep(c.Config.ReconnectDelay)
		}

		return err
	}

	close(c.Events)
	return nil
}

// ReadLoop sets a timeout of 300 seconds, and then attempts to read
// from the IRC server. If there is an error, it calls Reconnect.
func (c *Client) ReadLoop() error {
	for {
		c.conn.SetDeadline(time.Now().Add(300 * time.Second))
		event, err := c.reader.Decode()
		if err != nil {
			return c.Reconnect()
		}

		c.Events <- event
	}
}

// Loop reads from the events channel and sends the events to be
// handled for every message it receives.
func (c *Client) Loop() {
	for {
		select {
		case event := <-c.Events:
			c.handleEvent(event)
		case <-c.quitChan:
			return
		}
	}
}

// IsConnected returns true if the client is connected to the server.
func (c *Client) IsConnected() bool {
	c.state.m.RLock()
	defer c.state.m.RUnlock()

	return c.state.connected
}

// GetNick returns the current nickname of the active connection.
//
// Returns empty string if tracking is disabled.
func (c *Client) GetNick() string {
	if c.Config.DisableTracking {
		return ""
	}

	c.state.m.RLock()
	defer c.state.m.RUnlock()

	if c.state.nick == "" {
		return c.Config.Nick
	}

	return c.state.nick
}

// SetNick changes the client nickname.
func (c *Client) SetNick(name string) {
	c.state.m.Lock()
	defer c.state.m.Unlock()

	c.state.nick = name
	c.Send(&Event{Command: NICK, Params: []string{name}})
}

// GetChannels returns the active list of channels that the client
// is in.
//
// Returns nil if tracking is disabled.
func (c *Client) GetChannels() map[string]*Channel {
	if c.Config.DisableTracking {
		return nil
	}

	c.state.m.RLock()
	defer c.state.m.RUnlock()

	return c.state.channels
}

// Who tells the client to update it's channel/user records.
//
// Does not update internal state if tracking is disabled.
func (c *Client) Who(target string) {
	c.Send(&Event{Command: WHO, Params: []string{target, "%tcuhn,1"}})
}

// Join attempts to enter an IRC channel with an optional password.
func (c *Client) Join(channel, password string) {
	if password != "" {
		c.Send(&Event{Command: JOIN, Params: []string{channel, password}})
		return
	}

	c.Send(&Event{Command: JOIN, Params: []string{channel}})
}

// Part leaves an IRC channel with an optional leave message.
func (c *Client) Part(channel, message string) {
	if message != "" {
		c.Send(&Event{Command: JOIN, Params: []string{channel}, Trailing: message})
		return
	}

	c.Send(&Event{Command: JOIN, Params: []string{channel}})
}

// Message sends a PRIVMSG to target (either channel, service, or
// user).
func (c *Client) Message(target, message string) {
	c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message})
}

// Messagef sends a formated PRIVMSG to target (either channel,
// service, or user).
func (c *Client) Messagef(target, format string, a ...interface{}) {
	c.Message(target, fmt.Sprintf(format, a...))
}

// Action sends a PRIVMSG ACTION (/me) to target (either channel,
// service, or user).
func (c *Client) Action(target, message string) {
	c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: fmt.Sprintf("\001ACTION %s\001", message)})
}

// Actionf sends a formated PRIVMSG ACTION (/me) to target (either
// channel, service, or user).
func (c *Client) Actionf(target, format string, a ...interface{}) {
	c.Action(target, fmt.Sprintf(format, a...))
}

// Notice sends a NOTICE to target (either channel, service, or user).
func (c *Client) Notice(target, message string) {
	c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message})
}

// Noticef sends a formated NOTICE to target (either channel, service, or user).
func (c *Client) Noticef(target, format string, a ...interface{}) {
	c.Notice(target, fmt.Sprintf(format, a...))
}

// SendRaw sends a raw string back to the server, without carriage returns or
// newlines.
func (c *Client) SendRaw(raw string) {
	e := ParseEvent(raw)
	if e == nil {
		c.log.Printf("invalid event: %q", raw)
		return
	}

	c.Send(e)
}

// SendRawf sends a formated string back to the server, without carriage
// returns or newlines.
func (c *Client) SendRawf(format string, a ...interface{}) {
	c.SendRaw(fmt.Sprintf(format, a...))
}
<commit_msg>make Client.ReadLoop() un-exported<commit_after>// Copyright 2016 Liam Stanley <me@liamstanley.io>. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

// Package girc provides a high level, yet flexible IRC library for use
// with interacting with IRC servers. girc has support for user/channel
// tracking, as well as a few other neat features (like auto-reconnect).
//
// Much of what girc can do, can also be disabled. The goal is to
// provide a solid API that you don't necessarily have to work with out
// of the box if you don't want to.
//
// See "example/main.go" for a brief and very useful example taking
// advantage of girc, that should give you a general idea of how the API
// works.
package girc

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"sync"
	"time"
)

// Client contains all of the information necessary to run a single IRC
// client.
type Client struct {
	// Config represents the configuration
	Config Config
	// Events is a buffer of events waiting to be processed.
	Events chan *Event
	// Sender is a Sender{} interface implementation.
	Sender Sender

	// state represents the throw-away state for the irc session.
	state *state
	// initTime represents the creation time of the client.
	initTime time.Time

	// cbLock is the internal locking mechanism for the callbacks map.
	cbMux sync.Mutex
	// callbacks is an internal mapping of COMMAND -> callback.
	callbacks map[string][]Callback
	// internalCallbacks is a list of callbacks used internally.
	internalCallbacks []string

	// reader is the socket buffer reader from the IRC server.
	reader *Decoder
	// reader is the socket buffer write to the IRC server.
	writer *Encoder
	// conn is a net.Conn reference to the IRC server.
	conn net.Conn
	// tries represents the internal reconnect count to the IRC server.
	tries int
	// log is used if a writer is supplied for Client.Config.Logger.
	log *log.Logger
	// quitChan is used to close the connection to the IRC server.
	quitChan chan struct{}
	// hasQuit is used to determine if we've finished quitting/cleaning up.
	hasQuit bool
}

// Config contains configuration options for an IRC client
type Config struct {
	// Server is a host/ip of the server you want to connect to.
	Server string
	// Port is the port that will be used during server connection.
	Port int
	// Password is the server password used to authenticate.
	Password string
	// Nick is an rfc-valid nickname used during connect.
	Nick string
	// User is the username/ident to use on connect. Ignored if identd server
	// is used.
	User string
	// Name is the "realname" that's used during connect.
	Name string
	// TLSConfig is an optional user-supplied tls configuration, used during
	// socket creation to the server.
	TLSConfig *tls.Config
	// MaxRetries is the number of times the client will attempt to reconnect
	// to the server after the last disconnect.
	MaxRetries int
	// Logger is an optional, user supplied logger to log the raw lines sent
	// from the server. Useful for debugging. Defaults to ioutil.Discard.
	Logger io.Writer
	// ReconnectDelay is the a duration of time to delay before attempting a
	// reconnection. Defaults to 10s (minimum of 10s).
	ReconnectDelay time.Duration
	// DisableTracking disables all channel and user-level tracking. Useful
	// for highly embedded scripts with single purposes.
	DisableTracking bool
	// DisableCapTracking disables all network/server capability tracking.
	// This includes determining what feature the IRC server supports, what
	// the "NETWORK=" variables are, and other useful stuff.
	DisableCapTracking bool
	// DisableNickCollision disables the clients auto-response to nickname
	// collisions. For example, if "test" is already in use, or is blocked by
	// the network/a service, the client will try and use "test_", then it
	// will attempt "test__", "test___", and so on.
	DisableNickCollision bool
}

// New creates a new IRC client with the specified server, name and
// config.
func New(config Config) *Client {
	client := &Client{
		Config:    config,
		Events:    make(chan *Event, 40), // buffer 40 events
		quitChan:  make(chan struct{}),
		callbacks: make(map[string][]Callback),
		tries:     0,
		initTime:  time.Now(),
	}

	if client.Config.Logger == nil {
		client.Config.Logger = ioutil.Discard
	}
	client.log = log.New(client.Config.Logger, "", log.Ldate|log.Ltime|log.Lshortfile)

	// Register builtin helpers.
	client.registerHelpers()

	return client
}

// Quit disconnects from the server.s
func (c *Client) Quit(message string) {
	c.Send(&Event{Command: QUIT, Trailing: message})

	c.hasQuit = true

	if c.conn != nil {
		c.conn.Close()
	}

	c.quitChan <- struct{}{}
}

// Uptime returns the amount of time that has passed since the
// client was created.
func (c *Client) Uptime() time.Duration {
	return time.Since(c.initTime)
}

// Server returns the string representation of host+port pair for net.Conn
func (c *Client) Server() string {
	return fmt.Sprintf("%s:%d", c.Config.Server, c.Config.Port)
}

// Send sends an event to the server. Use Client.RunCallback() if you are
// are simply looking to trigger callbacks with an event.
func (c *Client) Send(event *Event) error {
	// log the event
	if !event.Sensitive {
		c.log.Print("--> ", event.String())
	}

	return c.Sender.Send(event)
}

// Connect attempts to connect to the given IRC server
func (c *Client) Connect() error {
	var conn net.Conn
	var err error

	// Sanity check a few options.
	if c.Config.Server == "" || c.Config.Port == 0 || c.Config.Nick == "" || c.Config.User == "" {
		return errors.New("invalid configuration (server/port/nick/user)")
	}

	// Reset the state.
	c.state = newState()

	if c.Config.TLSConfig == nil {
		conn, err = net.Dial("tcp", c.Server())
	} else {
		conn, err = tls.Dial("tcp", c.Server(), c.Config.TLSConfig)
	}
	if err != nil {
		return err
	}

	c.conn = conn
	c.reader = NewDecoder(conn)
	c.writer = NewEncoder(conn)
	c.Sender = serverSender{writer: c.writer}
	for _, event := range c.connectMessages() {
		if err := c.Send(event); err != nil {
			return err
		}
	}

	c.tries = 0
	go c.readLoop()

	// Consider the connection a success at this point.
	c.state.connected = true

	return nil
}

// connectMessages is a list of IRC messages to send when attempting to
// connect to the IRC server.
func (c *Client) connectMessages() (events []*Event) {
	// Passwords first.
	if c.Config.Password != "" {
		events = append(events, &Event{Command: PASS, Params: []string{c.Config.Password}})
	}

	// Then nickname.
	events = append(events, &Event{Command: NICK, Params: []string{c.Config.Nick}})

	// Then username and realname.
	if c.Config.Name == "" {
		c.Config.Name = c.Config.User
	}

	events = append(events, &Event{
		Command:  USER,
		Params:   []string{c.Config.User, "+iw", "*"},
		Trailing: c.Config.Name,
	})

	return events
}

// Reconnect checks to make sure we want to, and then attempts to reconnect
// to the server.
func (c *Client) Reconnect() (err error) {
	if c.hasQuit {
		return nil
	}

	if c.Config.ReconnectDelay < (10 * time.Second) {
		c.Config.ReconnectDelay = 10 * time.Second
	}

	if c.Config.MaxRetries > 0 {
		var err error
		c.conn.Close()

		// Re-setup events.
		c.Events = make(chan *Event, 40)

		// Delay so we're not slaughtering the server with a bunch of
		// connections.
		c.log.Printf("reconnecting to %s in %s", c.Server(), c.Config.ReconnectDelay)
		time.Sleep(c.Config.ReconnectDelay)

		for err = c.Connect(); err != nil && c.tries < c.Config.MaxRetries; c.tries++ {
			c.log.Printf("reconnecting to %s in %s (%d tries)", c.Server(), c.Config.ReconnectDelay, c.tries)
			time.Sleep(c.Config.ReconnectDelay)
		}

		return err
	}

	close(c.Events)
	return nil
}

// readLoop sets a timeout of 300 seconds, and then attempts to read from the
// IRC server. If there is an error, it calls Reconnect.
func (c *Client) readLoop() error {
	for {
		c.conn.SetDeadline(time.Now().Add(300 * time.Second))
		event, err := c.reader.Decode()
		if err != nil {
			return c.Reconnect()
		}

		c.Events <- event
	}
}

// Loop reads from the events channel and sends the events to be handled for
// every message it receives.
func (c *Client) Loop() {
	for {
		select {
		case event := <-c.Events:
			c.handleEvent(event)
		case <-c.quitChan:
			return
		}
	}
}

// IsConnected returns true if the client is connected to the server.
func (c *Client) IsConnected() bool {
	c.state.m.RLock()
	defer c.state.m.RUnlock()

	return c.state.connected
}

// GetNick returns the current nickname of the active connection.
//
// Returns empty string if tracking is disabled.
func (c *Client) GetNick() string {
	if c.Config.DisableTracking {
		return ""
	}

	c.state.m.RLock()
	defer c.state.m.RUnlock()

	if c.state.nick == "" {
		return c.Config.Nick
	}

	return c.state.nick
}

// SetNick changes the client nickname.
func (c *Client) SetNick(name string) {
	c.state.m.Lock()
	defer c.state.m.Unlock()

	c.state.nick = name
	c.Send(&Event{Command: NICK, Params: []string{name}})
}

// GetChannels returns the active list of channels that the client
// is in.
//
// Returns nil if tracking is disabled.
func (c *Client) GetChannels() map[string]*Channel {
	if c.Config.DisableTracking {
		return nil
	}

	c.state.m.RLock()
	defer c.state.m.RUnlock()

	return c.state.channels
}

// Who tells the client to update it's channel/user records.
//
// Does not update internal state if tracking is disabled.
func (c *Client) Who(target string) {
	c.Send(&Event{Command: WHO, Params: []string{target, "%tcuhn,1"}})
}

// Join attempts to enter an IRC channel with an optional password.
func (c *Client) Join(channel, password string) {
	if password != "" {
		c.Send(&Event{Command: JOIN, Params: []string{channel, password}})
		return
	}

	c.Send(&Event{Command: JOIN, Params: []string{channel}})
}

// Part leaves an IRC channel with an optional leave message.
func (c *Client) Part(channel, message string) {
	if message != "" {
		c.Send(&Event{Command: JOIN, Params: []string{channel}, Trailing: message})
		return
	}

	c.Send(&Event{Command: JOIN, Params: []string{channel}})
}

// Message sends a PRIVMSG to target (either channel, service, or
// user).
func (c *Client) Message(target, message string) {
	c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message})
}

// Messagef sends a formated PRIVMSG to target (either channel,
// service, or user).
func (c *Client) Messagef(target, format string, a ...interface{}) {
	c.Message(target, fmt.Sprintf(format, a...))
}

// Action sends a PRIVMSG ACTION (/me) to target (either channel,
// service, or user).
func (c *Client) Action(target, message string) {
	c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: fmt.Sprintf("\001ACTION %s\001", message)})
}

// Actionf sends a formated PRIVMSG ACTION (/me) to target (either
// channel, service, or user).
func (c *Client) Actionf(target, format string, a ...interface{}) {
	c.Action(target, fmt.Sprintf(format, a...))
}

// Notice sends a NOTICE to target (either channel, service, or user).
func (c *Client) Notice(target, message string) {
	c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message})
}

// Noticef sends a formated NOTICE to target (either channel, service, or user).
func (c *Client) Noticef(target, format string, a ...interface{}) {
	c.Notice(target, fmt.Sprintf(format, a...))
}

// SendRaw sends a raw string back to the server, without carriage returns or
// newlines.
func (c *Client) SendRaw(raw string) {
	e := ParseEvent(raw)
	if e == nil {
		c.log.Printf("invalid event: %q", raw)
		return
	}

	c.Send(e)
}

// SendRawf sends a formated string back to the server, without carriage
// returns or newlines.
func (c *Client) SendRawf(format string, a ...interface{}) {
	c.SendRaw(fmt.Sprintf(format, a...))
}
<|endoftext|>


<commit_before>package main

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

	"github.com/ayoisaiah/stellar-photos-server/routes"
	"github.com/joho/godotenv"
)

func main() {
	err := godotenv.Load()

	if err != nil {
		log.Fatal("Error loading .env file")
	}

	port := fmt.Sprintf(":%v", os.Getenv("PORT"))

	mux := routes.NewRouter()

	log.Fatal(http.ListenAndServe(port, mux))
}
<commit_msg>Enable CORS<commit_after>package main

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

	"github.com/ayoisaiah/stellar-photos-server/routes"
	"github.com/joho/godotenv"
	"github.com/rs/cors"
)

func main() {
	err := godotenv.Load()

	if err != nil {
		log.Fatal("Error loading .env file")
	}

	port := fmt.Sprintf(":%v", os.Getenv("PORT"))

	mux := routes.NewRouter()

	handler := cors.Default().Handler(mux)

	log.Fatal(http.ListenAndServe(port, handler))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"io"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"time"

	. "agent/types"
	"agent/utils"

	log "github.com/Sirupsen/logrus"
	"github.com/xtaci/kcp-go"
	cli "gopkg.in/urfave/cli.v2"
)

const (
	SERVICE = "[AGENT]"
)

var (
	// 网络拥塞控制和削峰
	readDeadline  = time.Duration(15)       // 秒(没有网络包进入的最大间隔)
	receiveBuffer = 32767    // 每个连接的接收缓冲区
	sendBuffer    = 65535    // 每个连接的发送缓冲区
	udpBuffer     = 16777216 // UDP监听器的socket buffer
	tosEF         = 46       // Expedited Forwarding (EF)
)

var (
	rpmLimit = 200.0 // Request Per Minute
)

func main() {
	log.SetLevel(log.DebugLevel)

	// to catch all uncaught panic
	defer utils.PrintPanicStack()

	// open profiling
	go http.ListenAndServe("0.0.0.0:6060", nil)
	app := &cli.App{
		Name: "agent",
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:  "listen",
				Value: ":8888",
				Usage: "listening address:port",
			},
			&cli.StringSliceFlag{
				Name:  "etcd-hosts",
				Value: cli.NewStringSlice("http://127.0.0.1:2379"),
				Usage: "etcd hosts",
			},
			&cli.StringFlag{
				Name:  "etcd-root",
				Value: "/backends",
				Usage: "etcd root path",
			},
			&cli.StringSliceFlag{
				Name:  "services",
				Value: cli.NewStringSlice("snowflake-10000", "game-10000"),
				Usage: "auto-discovering services",
			},
			&cli.IntFlag{
				Name:"read-deadline",
				Value:15,
				Usage:"秒(没有网络包进入的最大间隔)",
			},
			&cli.IntFlag{
				Name:"receive-buffer",
				Value:32767,
				Usage:"每个连接的接收缓冲区",
			},
			&cli.IntFlag{
				Name:"send-buffer",
				Value:65535,
				Usage:"每个连接的发送缓冲区",
			},
			&cli.IntFlag{
				Name:"udp-buffer",
				Value:16777216,
				Usage:"UDP监听器的socket buffer",
			},
			&cli.IntFlag{
				Name:"tos-expedited-forwarding",
				Value:46,
				Usage:"Expedited Forwarding (EF)",
			},
			&cli.IntFlag{
				Name:"rpm-limit",
				Value:200,
				Usage:"Request Per Minute",
			},
		},
		Action: func(c *cli.Context) error {
			log.Println("listen:", c.String("listen"))
			log.Println("etcd-hosts:", c.StringSlice("etcd-hosts"))
			log.Println("etcd-root:", c.String("etcd-root"))
			log.Println("services:", c.StringSlice("services"))
			log.Println("read-deadline:", c.Int("read-deadline"))
			log.Println("send-buffer:", c.Int("send-buffer"))
			log.Println("receive-buffer:", c.Int("receive-buffer"))
			log.Println("udp-buffer:", c.Int("udp-buffer"))
			log.Println("tos-expedited-forwarding:", c.Int("tos-expedited-forwarding"))
			log.Println("rpm-limit:", c.Int("rpm-limit"))

			//setup net param
			readDeadline=c.Duration("read-deadline")
			receiveBuffer=c.Int("send-buffer")
			sendBuffer=c.Int("send-buffer")
			udpBuffer=c.Int("udp-buffer")
			tosEF=c.Int("tos-expedited-forwarding")

			rpmLimit=c.Float64("rpm-limit")
			// init services
			startup(c)

			// listeners
			go tcpServer(c.String("listen"))
			go udpServer(c.String("listen"))

			// wait forever
			select {}
		},
	}
	app.Run(os.Args)
}

func tcpServer(addr string) {
	// resolve address & start listening
	tcpAddr, err := net.ResolveTCPAddr("tcp4", addr)
	checkError(err)

	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)

	log.Info("listening on:", listener.Addr())

	// loop accepting
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			log.Warning("accept failed:", err)
			continue
		}
		// set socket read buffer
		conn.SetReadBuffer(sendBuffer)
		// set socket write buffer
		conn.SetWriteBuffer(receiveBuffer)
		// start a goroutine for every incoming connection for reading
		go handleClient(conn)

		// check server close signal
		select {
		case <-die:
			listener.Close()
			return
		default:
		}
	}
}

func udpServer(addr string) {
	l, err := kcp.Listen(addr)
	checkError(err)
	log.Info("udp listening on:", l.Addr())
	lis := l.(*kcp.Listener)

	if err := lis.SetReadBuffer(udpBuffer); err != nil {
		log.Println(err)
	}
	if err := lis.SetWriteBuffer(udpBuffer); err != nil {
		log.Println(err)
	}
	if err := lis.SetDSCP(tosEF); err != nil {
		log.Println(err)
	}

	// loop accepting
	for {
		conn, err := lis.AcceptKCP()
		if err != nil {
			log.Warning("accept failed:", err)
			continue
		}
		// set kcp parameters
		conn.SetWindowSize(32, 32)
		conn.SetNoDelay(1, 20, 1, 1)
		conn.SetKeepAlive(0) // require application ping
		conn.SetStreamMode(true)

		// start a goroutine for every incoming connection for reading
		go handleClient(conn)
	}
}

// PIPELINE #1: handleClient
// the goroutine is used for reading incoming PACKETS
// each packet is defined as :
// | 2B size |     DATA       |
//
func handleClient(conn net.Conn) {
	defer utils.PrintPanicStack()
	// for reading the 2-Byte header
	header := make([]byte, 2)
	// the input channel for agent()
	in := make(chan []byte)
	defer func() {
		close(in) // session will close
	}()

	// create a new session object for the connection
	// and record it's IP address
	var sess Session
	host, port, err := net.SplitHostPort(conn.RemoteAddr().String())
	if err != nil {
		log.Error("cannot get remote address:", err)
		return
	}
	sess.IP = net.ParseIP(host)
	log.Infof("new connection from:%v port:%v", host, port)

	// session die signal, will be triggered by agent()
	sess.Die = make(chan struct{})

	// create a write buffer
	out := new_buffer(conn, sess.Die)
	go out.start()

	// start agent for PACKET processing
	wg.Add(1)
	go agent(&sess, in, out)

	// read loop
	for {
		// solve dead link problem:
		// physical disconnection without any communcation between client and server
		// will cause the read to block FOREVER, so a timeout is a rescue.
		conn.SetReadDeadline(time.Now().Add(readDeadline * time.Second))

		// read 2B header
		n, err := io.ReadFull(conn, header)
		if err != nil {
			log.Warningf("read header failed, ip:%v reason:%v size:%v", sess.IP, err, n)
			return
		}
		size := binary.BigEndian.Uint16(header)

		// alloc a byte slice of the size defined in the header for reading data
		payload := make([]byte, size)
		n, err = io.ReadFull(conn, payload)
		if err != nil {
			log.Warningf("read payload failed, ip:%v reason:%v size:%v", sess.IP, err, n)
			return
		}

		// deliver the data to the input queue of agent()
		select {
		case in <- payload: // payload queued
		case <-sess.Die:
			log.Warningf("connection closed by logic, flag:%v ip:%v", sess.Flag, sess.IP)
			return
		}
	}
}

func checkError(err error) {
	if err != nil {
		log.Fatal(err)
		os.Exit(-1)
	}
}
<commit_msg>fix parameters<commit_after>package main

import (
	"encoding/binary"
	"io"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"time"

	. "agent/types"
	"agent/utils"

	log "github.com/Sirupsen/logrus"
	"github.com/xtaci/kcp-go"
	cli "gopkg.in/urfave/cli.v2"
)

const (
	SERVICE = "[AGENT]"
)

var (
	rpmLimit = 200.0 // Request Per Minute
)

func main() {
	log.SetLevel(log.DebugLevel)

	// to catch all uncaught panic
	defer utils.PrintPanicStack()

	// open profiling
	go http.ListenAndServe("0.0.0.0:6060", nil)
	app := &cli.App{
		Name: "agent",
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:  "listen",
				Value: ":8888",
				Usage: "listening address:port",
			},
			&cli.StringSliceFlag{
				Name:  "etcd-hosts",
				Value: cli.NewStringSlice("http://127.0.0.1:2379"),
				Usage: "etcd hosts",
			},
			&cli.StringFlag{
				Name:  "etcd-root",
				Value: "/backends",
				Usage: "etcd root path",
			},
			&cli.StringSliceFlag{
				Name:  "services",
				Value: cli.NewStringSlice("snowflake-10000", "game-10000"),
				Usage: "auto-discovering services",
			},
			&cli.DurationFlag{
				Name:  "read-deadline",
				Value: 15 * time.Second,
				Usage: "Read Timeout",
			},
			&cli.IntFlag{
				Name:  "sockbuf",
				Value: 32767,
				Usage: "per connection tcp socket buffer",
			},
			&cli.IntFlag{
				Name:  "udp-sockbuf",
				Value: 16777216,
				Usage: "global UDP socket buffer",
			},
			&cli.IntFlag{
				Name:  "udp-sndwnd",
				Value: 32,
				Usage: "per connection UDP send window",
			},
			&cli.IntFlag{
				Name:  "udp-rcvwnd",
				Value: 32,
				Usage: "per connection UDP recv window",
			},
			&cli.IntFlag{
				Name:  "tos",
				Value: 46,
				Usage: "Expedited Forwarding (EF)",
			},
			&cli.IntFlag{
				Name:  "rpm-limit",
				Value: 200,
				Usage: "Request Per Minute",
			},
		},
		Action: func(c *cli.Context) error {
			log.Println("listen:", c.String("listen"))
			log.Println("etcd-hosts:", c.StringSlice("etcd-hosts"))
			log.Println("etcd-root:", c.String("etcd-root"))
			log.Println("services:", c.StringSlice("services"))
			log.Println("read-deadline:", c.Duration("read-deadline"))
			log.Println("sockbuf:", c.Int("sockbuf"))
			log.Println("udp-sockbuf:", c.Int("udp-sockbuf"))
			log.Println("udp-sndwnd:", c.Int("udp-sndwnd"))
			log.Println("udp-rcvwnd:", c.Int("udp-rcvwnd"))
			log.Println("tos:", c.Int("tos"))
			log.Println("rpm-limit:", c.Int("rpm-limit"))

			//setup net param
			listen := c.String("listen")
			readDeadline := c.Duration("read-deadline")
			sockbuf := c.Int("sockbuf")
			udp_sockbuf := c.Int("udp-sockbuf")
			tos := c.Int("tos")
			sndwnd := c.Int("udp-sndwnd")
			rcvwnd := c.Int("udp-rcvwnd")
			rpmLimit = c.Float64("rpm-limit")

			// init services
			startup(c)

			// listeners
			go tcpServer(listen, readDeadline, sockbuf)
			go udpServer(listen, readDeadline, udp_sockbuf, tos, sndwnd, rcvwnd)

			// wait forever
			select {}
		},
	}
	app.Run(os.Args)
}

func tcpServer(addr string, readDeadline time.Duration, sockbuf int) {
	// resolve address & start listening
	tcpAddr, err := net.ResolveTCPAddr("tcp4", addr)
	checkError(err)

	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)

	log.Info("listening on:", listener.Addr())

	// loop accepting
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			log.Warning("accept failed:", err)
			continue
		}
		// set socket read buffer
		conn.SetReadBuffer(sockbuf)
		// set socket write buffer
		conn.SetWriteBuffer(sockbuf)
		// start a goroutine for every incoming connection for reading
		go handleClient(conn, readDeadline)

		// check server close signal
		select {
		case <-die:
			listener.Close()
			return
		default:
		}
	}
}

func udpServer(addr string, readDeadline time.Duration, sockbuf, tos, sndwnd, rcvwnd int) {
	l, err := kcp.Listen(addr)
	checkError(err)
	log.Info("udp listening on:", l.Addr())
	lis := l.(*kcp.Listener)

	if err := lis.SetReadBuffer(sockbuf); err != nil {
		log.Println(err)
	}
	if err := lis.SetWriteBuffer(sockbuf); err != nil {
		log.Println(err)
	}
	if err := lis.SetDSCP(tos); err != nil {
		log.Println(err)
	}

	// loop accepting
	for {
		conn, err := lis.AcceptKCP()
		if err != nil {
			log.Warning("accept failed:", err)
			continue
		}
		// set kcp parameters
		conn.SetWindowSize(sndwnd, rcvwnd)
		conn.SetNoDelay(1, 20, 1, 1)
		conn.SetKeepAlive(0) // require application ping
		conn.SetStreamMode(true)

		// start a goroutine for every incoming connection for reading
		go handleClient(conn, readDeadline)
	}
}

// PIPELINE #1: handleClient
// the goroutine is used for reading incoming PACKETS
// each packet is defined as :
// | 2B size |     DATA       |
//
func handleClient(conn net.Conn, readDeadline time.Duration) {
	defer utils.PrintPanicStack()
	// for reading the 2-Byte header
	header := make([]byte, 2)
	// the input channel for agent()
	in := make(chan []byte)
	defer func() {
		close(in) // session will close
	}()

	// create a new session object for the connection
	// and record it's IP address
	var sess Session
	host, port, err := net.SplitHostPort(conn.RemoteAddr().String())
	if err != nil {
		log.Error("cannot get remote address:", err)
		return
	}
	sess.IP = net.ParseIP(host)
	log.Infof("new connection from:%v port:%v", host, port)

	// session die signal, will be triggered by agent()
	sess.Die = make(chan struct{})

	// create a write buffer
	out := new_buffer(conn, sess.Die)
	go out.start()

	// start agent for PACKET processing
	wg.Add(1)
	go agent(&sess, in, out)

	// read loop
	for {
		// solve dead link problem:
		// physical disconnection without any communcation between client and server
		// will cause the read to block FOREVER, so a timeout is a rescue.
		conn.SetReadDeadline(time.Now().Add(readDeadline * time.Second))

		// read 2B header
		n, err := io.ReadFull(conn, header)
		if err != nil {
			log.Warningf("read header failed, ip:%v reason:%v size:%v", sess.IP, err, n)
			return
		}
		size := binary.BigEndian.Uint16(header)

		// alloc a byte slice of the size defined in the header for reading data
		payload := make([]byte, size)
		n, err = io.ReadFull(conn, payload)
		if err != nil {
			log.Warningf("read payload failed, ip:%v reason:%v size:%v", sess.IP, err, n)
			return
		}

		// deliver the data to the input queue of agent()
		select {
		case in <- payload: // payload queued
		case <-sess.Die:
			log.Warningf("connection closed by logic, flag:%v ip:%v", sess.Flag, sess.IP)
			return
		}
	}
}

func checkError(err error) {
	if err != nil {
		log.Fatal(err)
		os.Exit(-1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/carterjones/gouzuru/gouzuru"
	"github.com/carterjones/gouzuru/w32"
	"os"
	"path/filepath"
)

func handleError(err error) bool {
	if err != nil {
		fmt.Println("[-] error:", err)
		return true
	}
	return false
}

func main() {
	if len(os.Args) < 2 {
		_, exeName := filepath.Split(os.Args[0])
		fmt.Println("Usage:", exeName, "<process-name>")
		return
	}

	// Set the target process to the first argument.
	targetProcName := os.Args[1]

	// Get the process list.
	pids, err := w32.EnumProcesses()
	if handleError(err) {
		return
	}

	// Find the target PID.
	targetPid := int32(0)
	for _, p := range pids {
		name, err := gouzuru.GetProcessNameFromPid(p)
		if err != nil {
			fmt.Printf("[-] error for PID: %v: %v\n", p, err)
		} else if name == targetProcName {
			targetPid = p
		}
	}
	if targetPid == 0 {
		fmt.Printf("Unable to open %v. You might need more permissions or the "+
			"target process might not exist.\n", targetProcName)
		return
	}

	// Open the target process.
	hwnd, err := w32.OpenProcess(targetPid, int32(w32.PROCESS_ALL_ACCESS))
	if handleError(err) {
		return
	}

	// Make a process object.
	proc := gouzuru.Process{
		Name:   targetProcName,
		Pid:    targetPid,
		Handle: hwnd,
	}
	fmt.Printf("Successfully opened %v. PID: %v. Handle: %v.\n",
		proc.Name, proc.Pid, proc.Handle)

	// Get information about the page ranges of the process.
	regions, err := proc.IdentifyRegions()
	if handleError(err) {
		return
	}

	for _, r := range regions {
		fmt.Printf("base: %x, size: %x\n", r.BaseAddress, r.RegionSize)
	}

	// Read some memory.
	// TODO: data, err := ReadProcessMemory(hwnd, address, 1)
	// BOOL WINAPI ReadProcessMemory(
	//   _In_  HANDLE  hProcess,
	//   _In_  LPCVOID lpBaseAddress,
	//   _Out_ LPVOID  lpBuffer,
	//   _In_  SIZE_T  nSize,
	//   _Out_ SIZE_T  *lpNumberOfBytesRead
	// );
}
<commit_msg>use flags<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/carterjones/gouzuru/gouzuru"
	"github.com/carterjones/gouzuru/w32"
)

func handleError(err error) bool {
	if err != nil {
		fmt.Println("[-] error:", err)
		return true
	}
	return false
}

func main() {
	var targetProcName = flag.String("p",
		"<target-process.exe>",
		"name of the target process (including .exe)")
	flag.Parse()

	// Get the process list.
	pids, err := w32.EnumProcesses()
	if handleError(err) {
		return
	}

	// Find the target PID.
	targetPid := int32(0)
	for _, p := range pids {
		name, err := gouzuru.GetProcessNameFromPid(p)
		if err != nil {
			fmt.Printf("[-] error for PID: %v: %v\n", p, err)
		} else if name == *targetProcName {
			targetPid = p
		}
	}
	if targetPid == 0 {
		fmt.Printf("Unable to open %v. You might need more permissions or the "+
			"target process might not exist.\n", *targetProcName)
		return
	}

	// Open the target process.
	hwnd, err := w32.OpenProcess(targetPid, int32(w32.PROCESS_ALL_ACCESS))
	if handleError(err) {
		return
	}

	// Make a process object.
	proc := gouzuru.Process{
		Name:   *targetProcName,
		Pid:    targetPid,
		Handle: hwnd,
	}
	fmt.Printf("Successfully opened %v. PID: %v. Handle: %v.\n",
		proc.Name, proc.Pid, proc.Handle)

	// Get information about the page ranges of the process.
	regions, err := proc.IdentifyRegions()
	if handleError(err) {
		return
	}

	for _, r := range regions {
		fmt.Printf("base: %x, size: %x\n", r.BaseAddress, r.RegionSize)
	}

	// Read some memory.
	// TODO: data, err := ReadProcessMemory(hwnd, address, 1)
	// BOOL WINAPI ReadProcessMemory(
	//   _In_  HANDLE  hProcess,
	//   _In_  LPCVOID lpBaseAddress,
	//   _Out_ LPVOID  lpBuffer,
	//   _In_  SIZE_T  nSize,
	//   _Out_ SIZE_T  *lpNumberOfBytesRead
	// );
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/alanctgardner/gogen-avro/container"
	"github.com/alanctgardner/gogen-avro/generator"
	"github.com/alanctgardner/gogen-avro/types"
)

func main() {
	generateContainer := flag.Bool("container", false, "Whether to emit container file writer code")
	packageName := flag.String("package", "avro", "Name of generated package")
	flag.Parse()
	if flag.NArg() < 2 {
		fmt.Fprintf(os.Stderr, "Usage: gogen-avro [--container] [--package=<package name>] <target directory> <schema files>\n")
		os.Exit(1)
	}
	targetDir := flag.Arg(0)
	files := flag.Args()[1:]

	var err error
	pkg := generator.NewPackage(*packageName)
	namespace := types.NewNamespace()

	if *generateContainer {
		_, err = namespace.FieldDefinitionForSchema([]byte(container.AVRO_BLOCK_SCHEMA))
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error generating Avro container block schema - %v\n", err)
			os.Exit(2)
		}

		_, err = namespace.FieldDefinitionForSchema([]byte(container.AVRO_HEADER_SCHEMA))
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error generating Avro container header schema - %v\n", err)
			os.Exit(2)
		}
	}

	for _, fileName := range files {
		schema, err := ioutil.ReadFile(fileName)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error reading file %q - %v\n", fileName, err)
			os.Exit(2)
		}

		_, err = namespace.FieldDefinitionForSchema(schema)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error decoding schema for file %q - %v\n", fileName, err)
			os.Exit(3)
		}
	}

	// Resolve dependencies and add the schemas to the package
	err = addFieldsToPackage(namespace, pkg, *generateContainer)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error generating code for schema - %v\n", err)
		os.Exit(4)
	}

	// Add header comment to all generated files.
	for _, f := range pkg.Files() {
		pkg.AddHeader(f, codegenComment(files))
	}

	err = pkg.WriteFiles(targetDir)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error writing source files to directory %q - %v\n", targetDir, err)
		os.Exit(4)
	}
}

func addFieldsToPackage(namespace *types.Namespace, pkg *generator.Package, generateContainer bool) error {
	for _, schema := range namespace.Schemas {
		err := schema.Root.ResolveReferences(namespace)
		if err != nil {
			return err
		}

		schema.Root.AddStruct(pkg)
		schema.Root.AddSerializer(pkg)
		schema.Root.AddDeserializer(pkg)

		if generateContainer {
			containerWriter := container.NewAvroContainerWriter(schema)
			containerWriter.AddAvroContainerWriter(pkg)
		}
	}
	return nil
}

// codegenComment generates a comment informing readers they are looking at
// generated code and lists the source avro files used to generate the code
//
// invariant: sources > 0
func codegenComment(sources []string) string {
	const fileComment = `/*
 * CODE GENERATED AUTOMATICALLY WITH github.com/alanctgardner/gogen-avro
 * THIS FILE SHOULD NOT BE EDITED BY HAND
 *
 * %s
 */`
	var sourceBlock []string
	if len(sources) == 1 {
		sourceBlock = append(sourceBlock, "SOURCE:")
	} else {
		sourceBlock = append(sourceBlock, "SOURCES:")
	}

	for _, source := range sources {
		sourceBlock = append(sourceBlock, fmt.Sprintf(" *     %s", source))
	}

	return fmt.Sprintf(fileComment, strings.Join(sourceBlock, "\n"))
}
<commit_msg>Remove container argument from main<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/alanctgardner/gogen-avro/generator"
	"github.com/alanctgardner/gogen-avro/types"
)

func main() {
	packageName := flag.String("package", "avro", "Name of generated package")
	flag.Parse()
	if flag.NArg() < 2 {
		fmt.Fprintf(os.Stderr, "Usage: gogen-avro [--package=<package name>] <target directory> <schema files>\n")
		os.Exit(1)
	}
	targetDir := flag.Arg(0)
	files := flag.Args()[1:]

	var err error
	pkg := generator.NewPackage(*packageName)
	namespace := types.NewNamespace()

	for _, fileName := range files {
		schema, err := ioutil.ReadFile(fileName)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error reading file %q - %v\n", fileName, err)
			os.Exit(2)
		}

		_, err = namespace.FieldDefinitionForSchema(schema)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error decoding schema for file %q - %v\n", fileName, err)
			os.Exit(3)
		}
	}

	// Resolve dependencies and add the schemas to the package
	err = addFieldsToPackage(namespace, pkg)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error generating code for schema - %v\n", err)
		os.Exit(4)
	}

	// Add header comment to all generated files.
	for _, f := range pkg.Files() {
		pkg.AddHeader(f, codegenComment(files))
	}

	err = pkg.WriteFiles(targetDir)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error writing source files to directory %q - %v\n", targetDir, err)
		os.Exit(4)
	}
}

func addFieldsToPackage(namespace *types.Namespace, pkg *generator.Package) error {
	for _, schema := range namespace.Schemas {
		err := schema.Root.ResolveReferences(namespace)
		if err != nil {
			return err
		}

		schema.Root.AddStruct(pkg)
		schema.Root.AddSerializer(pkg)
		schema.Root.AddDeserializer(pkg)
	}
	return nil
}

// codegenComment generates a comment informing readers they are looking at
// generated code and lists the source avro files used to generate the code
//
// invariant: sources > 0
func codegenComment(sources []string) string {
	const fileComment = `/*
 * CODE GENERATED AUTOMATICALLY WITH github.com/alanctgardner/gogen-avro
 * THIS FILE SHOULD NOT BE EDITED BY HAND
 *
 * %s
 */`
	var sourceBlock []string
	if len(sources) == 1 {
		sourceBlock = append(sourceBlock, "SOURCE:")
	} else {
		sourceBlock = append(sourceBlock, "SOURCES:")
	}

	for _, source := range sources {
		sourceBlock = append(sourceBlock, fmt.Sprintf(" *     %s", source))
	}

	return fmt.Sprintf(fileComment, strings.Join(sourceBlock, "\n"))
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/domainr/whois"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/promlog"
	"github.com/prometheus/common/promlog/flag"
	"github.com/prometheus/common/version"
	"gopkg.in/alecthomas/kingpin.v2"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

var (
	// How often to check domains
	checkRate = 12 * time.Hour

	configFile = kingpin.Flag("config", "Domain exporter configuration file.").Default("domains.yml").String()
	httpBind   = kingpin.Flag("bind", "The address to listen on for HTTP requests.").Default(":9203").String()

	domainExpiration = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "domain_expiration_seconds",
			Help: "UNIX timestamp when the WHOIS record states this domain will expire",
		},
		[]string{"domain"},
	)
	parsedExpiration = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "domain_expiration_parsed",
			Help: "That the domain date was parsed",
		},
		[]string{"domain"},
	)

	expiryRegex = regexp.MustCompile(`(?i)(Registry Expiry Date|paid-till|Expiration Date|Expiration Time|Expiry.*|expires.*): (.*)`)

	formats = []string{
		"2006-01-02",
		"2006-01-02T15:04:05Z",
		"02-Jan-2006",
		"2006.01.02",
		"Mon Jan 2 15:04:05 MST 2006",
		"02/01/2006",
		"2006-01-02 15:04:05 MST",
		"2006/01/02",
		"Mon Jan 2006 15:04:05",
		"2006-01-02 15:04:05-07",
		"2006-01-02 15:04:05",
		"2.1.2006 15:04:05",
	}

	config promlog.Config
	logger log.Logger
)

type Config struct {
	Domains []string `yaml:"domains"`
}

func main() {
	flag.AddFlags(kingpin.CommandLine, &config)
	kingpin.Version(version.Print("domain_exporter"))
	kingpin.HelpFlag.Short('h')
	kingpin.Parse()

	logger = promlog.New(&config)

	level.Info(logger).Log("msg", "Starting domain_exporter", "version", version.Info())
	level.Info(logger).Log("msg", "Build context", version.BuildContext())

	prometheus.Register(domainExpiration)
	prometheus.Register(parsedExpiration)

	config := Config{}

	filename, err := filepath.Abs(*configFile)
	if err != nil {
		level.Warn(logger).Log("warn", err)
	}
	yamlFile, err := ioutil.ReadFile(filename)

	if err != nil {
		level.Warn(logger).Log("warn", err)
		level.Warn(logger).Log("warn", "Configuration file not present, you'll have to /probe me for metrics.")
	}
	err = yaml.Unmarshal(yamlFile, &config)

	if err != nil {
		level.Warn(logger).Log("warn", err)
	} else {
		go func() {
			for {
				for _, query := range config.Domains {
					_, err = lookup(query, domainExpiration, parsedExpiration)
					if err != nil {
						level.Warn(logger).Log("warn", err)
					}
					continue
				}
				time.Sleep(checkRate)
			}
		}()
	}

	http.Handle("/metrics", promhttp.Handler())
	http.HandleFunc("/probe", func(w http.ResponseWriter, r *http.Request) {
		probeHandler(w, r, logger)
	})
	level.Info(logger).Log("msg", "Listening", "port", *httpBind)
	if err := http.ListenAndServe(*httpBind, nil); err != nil {
		level.Error(logger).Log("msg", "Error starting HTTP server", "err", err)
		os.Exit(1)
	}
}

func probeHandler(w http.ResponseWriter, r *http.Request, logger log.Logger) {
	probeExpiration := prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "domain_expiration",
			Help: "Days until the WHOIS record states this domain will expire",
		},
		[]string{"domain"},
	)
	probeUnfindableExpiration := prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "domain_expiration_unfindable",
			Help: "That the domain date could not be parsed, or the domain doesn't have a whois record",
		},
		[]string{"domain"},
	)

	registry := prometheus.NewRegistry()
	registry.MustRegister(probeExpiration)
	registry.MustRegister(probeUnfindableExpiration)
	params := r.URL.Query()
	target := params.Get("target")
	if target == "" {
		http.Error(w, "Target parameter is missing", http.StatusBadRequest)
		return
	}
	_, err := lookup(target, probeExpiration, parsedExpiration)
	if err != nil {
		level.Warn(logger).Log("warn", err)
		http.Error(w, fmt.Sprintf("Don't know how to parse: %q", target), http.StatusBadRequest)
		return
	}

	h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
	h.ServeHTTP(w, r)
}

func parse(host string, res []byte) (float64, error) {
	results := expiryRegex.FindStringSubmatch(string(res))
	if len(results) < 1 {
		err := fmt.Errorf("Don't know how to parse domain: %s", host)
		level.Warn(logger).Log("warn", err.Error())
		return -2, err
	}

	for _, format := range formats {
		if date, err := time.Parse(format, strings.TrimSpace(results[2])); err == nil {
			level.Info(logger).Log("domain:", host, "date", date)
			return float64(date.Unix()), nil
		}

	}
	return -1, errors.New(fmt.Sprintf("Unable to parse date: %s, for %s\n", strings.TrimSpace(results[2]), host))
}

func lookup(domain string, handler *prometheus.GaugeVec, parsedExpiration *prometheus.GaugeVec) (float64, error) {
	req, err := whois.NewRequest(domain)
	if err != nil {
		return -1, err
	}

	res, err := whois.DefaultClient.Fetch(req)
	if err != nil {
		return -1, err
	}

	date, err := parse(domain, res.Body)
	if err != nil {
		if parsedExpiration != nil {
			parsedExpiration.WithLabelValues(domain).Set(0)
		}
		return -1, err
	}

	if handler != nil {
		handler.WithLabelValues(domain).Set(date)
	}
	if parsedExpiration != nil {
		parsedExpiration.WithLabelValues(domain).Set(1)
	}

	return date, nil
}
<commit_msg>added .ir domains regex<commit_after>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/domainr/whois"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/promlog"
	"github.com/prometheus/common/promlog/flag"
	"github.com/prometheus/common/version"
	"gopkg.in/alecthomas/kingpin.v2"
	"gopkg.in/yaml.v2"
)

var (
	// How often to check domains
	checkRate = 12 * time.Hour

	configFile = kingpin.Flag("config", "Domain exporter configuration file.").Default("domains.yml").String()
	httpBind   = kingpin.Flag("bind", "The address to listen on for HTTP requests.").Default(":9203").String()

	domainExpiration = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "domain_expiration_seconds",
			Help: "UNIX timestamp when the WHOIS record states this domain will expire",
		},
		[]string{"domain"},
	)
	parsedExpiration = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "domain_expiration_parsed",
			Help: "That the domain date was parsed",
		},
		[]string{"domain"},
	)

	expiryRegex = regexp.MustCompile(`(?i)(Registry Expiry Date|paid-till|Expiration Date|Expiration Time|Expiry.*|expires.*|expire-date):[ \t](.*)`)

	formats = []string{
		"2006-01-02",
		"2006-01-02T15:04:05Z",
		"02-Jan-2006",
		"2006.01.02",
		"Mon Jan 2 15:04:05 MST 2006",
		"02/01/2006",
		"2006-01-02 15:04:05 MST",
		"2006/01/02",
		"Mon Jan 2006 15:04:05",
		"2006-01-02 15:04:05-07",
		"2006-01-02 15:04:05",
		"2.1.2006 15:04:05",
	}

	config promlog.Config
	logger log.Logger
)

type Config struct {
	Domains []string `yaml:"domains"`
}

func main() {
	flag.AddFlags(kingpin.CommandLine, &config)
	kingpin.Version(version.Print("domain_exporter"))
	kingpin.HelpFlag.Short('h')
	kingpin.Parse()

	logger = promlog.New(&config)

	level.Info(logger).Log("msg", "Starting domain_exporter", "version", version.Info())
	level.Info(logger).Log("msg", "Build context", version.BuildContext())

	prometheus.Register(domainExpiration)
	prometheus.Register(parsedExpiration)

	config := Config{}

	filename, err := filepath.Abs(*configFile)
	if err != nil {
		level.Warn(logger).Log("warn", err)
	}
	yamlFile, err := ioutil.ReadFile(filename)

	if err != nil {
		level.Warn(logger).Log("warn", err)
		level.Warn(logger).Log("warn", "Configuration file not present, you'll have to /probe me for metrics.")
	}
	err = yaml.Unmarshal(yamlFile, &config)

	if err != nil {
		level.Warn(logger).Log("warn", err)
	} else {
		go func() {
			for {
				for _, query := range config.Domains {
					_, err = lookup(query, domainExpiration, parsedExpiration)
					if err != nil {
						level.Warn(logger).Log("warn", err)
					}
					continue
				}
				time.Sleep(checkRate)
			}
		}()
	}

	http.Handle("/metrics", promhttp.Handler())
	http.HandleFunc("/probe", func(w http.ResponseWriter, r *http.Request) {
		probeHandler(w, r, logger)
	})
	level.Info(logger).Log("msg", "Listening", "port", *httpBind)
	if err := http.ListenAndServe(*httpBind, nil); err != nil {
		level.Error(logger).Log("msg", "Error starting HTTP server", "err", err)
		os.Exit(1)
	}
}

func probeHandler(w http.ResponseWriter, r *http.Request, logger log.Logger) {
	probeExpiration := prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "domain_expiration",
			Help: "Days until the WHOIS record states this domain will expire",
		},
		[]string{"domain"},
	)
	probeUnfindableExpiration := prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "domain_expiration_unfindable",
			Help: "That the domain date could not be parsed, or the domain doesn't have a whois record",
		},
		[]string{"domain"},
	)

	registry := prometheus.NewRegistry()
	registry.MustRegister(probeExpiration)
	registry.MustRegister(probeUnfindableExpiration)
	params := r.URL.Query()
	target := params.Get("target")
	if target == "" {
		http.Error(w, "Target parameter is missing", http.StatusBadRequest)
		return
	}
	_, err := lookup(target, probeExpiration, parsedExpiration)
	if err != nil {
		level.Warn(logger).Log("warn", err)
		http.Error(w, fmt.Sprintf("Don't know how to parse: %q", target), http.StatusBadRequest)
		return
	}

	h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
	h.ServeHTTP(w, r)
}

func parse(host string, res []byte) (float64, error) {
	results := expiryRegex.FindStringSubmatch(string(res))
	if len(results) < 1 {
		err := fmt.Errorf("Don't know how to parse domain: %s", host)
		level.Warn(logger).Log("warn", err.Error())
		return -2, err
	}

	for _, format := range formats {
		if date, err := time.Parse(format, strings.TrimSpace(results[2])); err == nil {
			level.Info(logger).Log("domain:", host, "date", date)
			return float64(date.Unix()), nil
		}

	}
	return -1, errors.New(fmt.Sprintf("Unable to parse date: %s, for %s\n", strings.TrimSpace(results[2]), host))
}

func lookup(domain string, handler *prometheus.GaugeVec, parsedExpiration *prometheus.GaugeVec) (float64, error) {
	req, err := whois.NewRequest(domain)
	if err != nil {
		return -1, err
	}

	res, err := whois.DefaultClient.Fetch(req)
	if err != nil {
		return -1, err
	}

	date, err := parse(domain, res.Body)
	if err != nil {
		if parsedExpiration != nil {
			parsedExpiration.WithLabelValues(domain).Set(0)
		}
		return -1, err
	}

	if handler != nil {
		handler.WithLabelValues(domain).Set(date)
	}
	if parsedExpiration != nil {
		parsedExpiration.WithLabelValues(domain).Set(1)
	}

	return date, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"os"
	"strings"
)

type Tree struct {
	left   *Tree
	right  *Tree
	key    string
	weight int
}

type Forest struct {
	trees []Tree
}

/**
  * Given a dictionary body, generate the frequencies of each character
  * @param dictionary_corpus: a strong representing phrases in a dictionary
                              each word is on its own line
                              phrase declarations end with slashes or a newline
*/
func get_dictionary_frequencies(dictionary_corpus string) map[string]int {
	dict_frequencies := make(map[string]int)

	lines := strings.Split(dictionary_corpus, "\n")
	for _, line := range lines {
		word := strings.ToLower(strings.Split(line, "/")[0])
		for i := 0; i < len(word); i++ {

			dict_frequencies[string(word[i])] += 1
		}
	}
	return dict_frequencies
}

/**
 * Given the name of the file, gets the contents of the file
 */
func get_corpus(file_name string) string {
	corpus, err := ioutil.ReadFile(file_name)
	if err != nil {
		log.Fatal(err)
	}
	return string(corpus)
}

/**
 * Given a corpus string, generates a coding from letters in the corpus
 * to the codeword it should be
 *
 */
func generate_hamming_code(corpus string) Tree {

	frequency_map := get_dictionary_frequencies(corpus)
	// Go through the frequencies and generate the hamming
	for char, freq := range frequency_map {
		fmt.Println("character:", char, "frequency", freq)
	}
	tree_freqs := generate_forest(frequency_map)
	ham_tree := generate_ham_tree(tree_freqs)
	return ham_tree
}

func generate_ham_tree(trees []Tree) Tree {
	if len(trees) == 1 {
		// This is the base case where we have already generateed the ham tree
		return trees[0]
	} else {
		// Find the smallest trees
		smaller, small := find_two_mins(trees)
		// Coalesce the tiny trees
		new_tree := tree_union(smaller, small)                   // this is a new tree
		rest := forest_difference(trees, []Tree{smaller, small}) // remove 2 leaves
		return generate_ham_tree(append(rest, new_tree))
	}
}
func find_two_mins(trees []Tree) (Tree, Tree) {
	var smallest_value int = math.MaxInt64
	var smallest_tree, small_tree Tree
	for _, tree := range trees {
		if tree.weight <= smallest_value {
			small_tree = smallest_tree
			smallest_value = tree.weight
			smallest_tree = tree
		}
	}
	return smallest_tree, small_tree
}
func tree_union(left Tree, right Tree) Tree {
	return Tree{
		&left,
		&right,
		"",
		left.weight + right.weight,
	}
}
func forest_difference(left []Tree, right []Tree) []Tree {
	var diff []Tree
	right_temp := map[Tree]bool{}
	for _, t_r := range right {
		right_temp[t_r] = true
	}
	for _, t_l := range left {
		if _, ok := right_temp[t_l]; !ok {
			diff = append(diff, t_l)
		}
	}
	return diff
}

func generate_forest(weight_maps map[string]int) []Tree {
	var forest []Tree
	for key, value := range weight_maps {
		leaf := Tree{
			nil,
			nil,
			key,
			value,
		}
		forest = append(forest, leaf)
	}
	return forest
}

func (t *Tree) GetCodes() map[string]string {
	if t.left == nil || t.right == nil {
		return map[string]string{t.key: ""}
	}
	l_codes := t.left.GetCodes()
	r_codes := t.right.GetCodes()
	codes := make(map[string]string)
	for l_k, l_v := range l_codes {
		codes[l_k] = "0 " + l_v
	}
	for r_k, r_v := range r_codes {
		codes[r_k] = "1 " + r_v
	}
	return codes
}
func main() {
	corpus_file := os.Args[1]
	corpus := get_corpus(corpus_file)
	hamming_code := generate_hamming_code(corpus)
	codes := hamming_code.GetCodes()
	for word, code := range codes {
		fmt.Println("Word: ", word, "Code: ", code)
	}
	fmt.Println(hamming_code)
	fmt.Println("Hello, 世界")

}
<commit_msg>ignore numbers<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"os"
	"strings"
  "sort"
)

type Tree struct {
	left   *Tree
	right  *Tree
	key    string
	weight int
}

type Forest struct {
	trees []Tree
}

/**
  * Given a dictionary body, generate the frequencies of each character
  * @param dictionary_corpus: a strong representing phrases in a dictionary
                              each word is on its own line
                              phrase declarations end with slashes or a newline
*/
func get_dictionary_frequencies(dictionary_corpus string) map[string]int {
	dict_frequencies := make(map[string]int)

	lines := strings.Split(dictionary_corpus, "\n")
	for _, line := range lines {
		word := strings.ToLower(strings.Split(line, "/")[0])
		for i := 0; i < len(word); i++ {
      char := string(word[i])
      if (!strings.ContainsAny(char, "0123456789\t\n\r'") && char!="") {
        dict_frequencies[string(word[i])] += 1
      }
		}
	}
	return dict_frequencies
}

/**
 * Given the name of the file, gets the contents of the file
 */
func get_corpus(file_name string) string {
	corpus, err := ioutil.ReadFile(file_name)
	if err != nil {
		log.Fatal(err)
	}
	return string(corpus)
}

/**
 * Given a corpus string, generates a coding from letters in the corpus
 * to the codeword it should be
 *
 */
func generate_hamming_code(corpus string) Tree {

	frequency_map := get_dictionary_frequencies(corpus)
	// Go through the frequencies and generate the hamming

	for char, freq := range frequency_map {
		fmt.Println("character:", char, "frequency", freq)
	}
	tree_freqs := generate_forest(frequency_map)
	ham_tree := generate_ham_tree(tree_freqs)
	return ham_tree
}

func generate_ham_tree(trees []Tree) Tree {
	if len(trees) == 1 {
		// This is the base case where we have already generateed the ham tree
		return trees[0]
	} else {
		// Find the smallest trees
		smaller, small := find_two_mins(trees)
		// Coalesce the tiny trees
		new_tree := tree_union(smaller, small)                   // this is a new tree
		rest := forest_difference(trees, []Tree{smaller, small}) // remove 2 leaves
		return generate_ham_tree(append(rest, new_tree))
	}
}
func find_two_mins(trees []Tree) (Tree, Tree) {
	var smallest_value int = math.MaxInt64
	var smallest_tree, small_tree Tree
	for _, tree := range trees {
		if tree.weight <= smallest_value {
			small_tree = smallest_tree
			smallest_value = tree.weight
			smallest_tree = tree
		}
	}
  fmt.Println(smallest_tree, small_tree)
	return smallest_tree, small_tree
}
func tree_union(left Tree, right Tree) Tree {
	return Tree{
		&left,
		&right,
		"",
		left.weight + right.weight,
	}
}
func forest_difference(left []Tree, right []Tree) []Tree {
	var diff []Tree
	right_temp := map[Tree]bool{}
	for _, t_r := range right {
		right_temp[t_r] = true
	}
	for _, t_l := range left {
		if _, ok := right_temp[t_l]; !ok {
			diff = append(diff, t_l)
		}
	}
	return diff
}

func generate_forest(weight_maps map[string]int) []Tree {
	var forest []Tree
	for key, value := range weight_maps {
		leaf := Tree{
			nil,
			nil,
			key,
			value,
		}
		forest = append(forest, leaf)
	}
	return forest
}

func (t *Tree) GetCodes() map[string]string {
	if t.left == nil || t.right == nil {
		return map[string]string{t.key: ""}
	}
	l_codes := t.left.GetCodes()
	r_codes := t.right.GetCodes()
	codes := make(map[string]string)
	for l_k, l_v := range l_codes {
		codes[l_k] = "0 " + l_v
	}
	for r_k, r_v := range r_codes {
		codes[r_k] = "1 " + r_v
	}
	return codes
}

func main() {
	corpus_file := os.Args[1]
	corpus := get_corpus(corpus_file)
	hamming_code := generate_hamming_code(corpus)
	codes := hamming_code.GetCodes()

  var keys []string
  for k := range codes {
    keys = append(keys, k)
  }
  sort.Strings(keys)
	for _, word := range keys {
		fmt.Println("Word: ", word, "Code: ", codes[word])
	}

	fmt.Println("Hello, 世界")

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"strings"

	"github.com/alphagov/govuk_crawler_worker/http_crawler"
	"github.com/alphagov/govuk_crawler_worker/queue"
	"github.com/alphagov/govuk_crawler_worker/ttl_hash_set"
	"github.com/alphagov/govuk_crawler_worker/util"
)

var (
	amqpAddr       = util.GetEnvDefault("AMQP_ADDRESS", "amqp://guest:guest@localhost:5672/")
	exchangeName   = util.GetEnvDefault("AMQP_EXCHANGE", "govuk_crawler_exchange")
	queueName      = util.GetEnvDefault("AMQP_MESSAGE_QUEUE", "govuk_crawler_queue")
	redisAddr      = util.GetEnvDefault("REDIS_ADDRESS", "127.0.0.1:6379")
	redisKeyPrefix = util.GetEnvDefault("REDIS_KEY_PREFIX", "govuk_crawler_worker")
	rootURL        = util.GetEnvDefault("ROOT_URL", "https://www.gov.uk/")
	blacklistPaths = util.GetEnvDefault("BLACKLIST_PATHS", "/search,/government/uploads")
	mirrorRoot     = os.Getenv("MIRROR_ROOT")
)

func main() {
	if os.Getenv("GOMAXPROCS") == "" {
		// Use all available cores if not otherwise specified
		runtime.GOMAXPROCS(runtime.NumCPU())
	}
	log.Println(fmt.Sprintf("using GOMAXPROCS value of %d", runtime.NumCPU()))

	ttlHashSet, err := ttl_hash_set.NewTTLHashSet(redisKeyPrefix, redisAddr)
	if err != nil {
		log.Fatal(err)
	}
	defer ttlHashSet.Close()
	log.Println("Connected to Redis service:", ttlHashSet)

	queueManager, err := queue.NewQueueManager(amqpAddr, exchangeName, queueName)
	if err != nil {
		log.Fatal(err)
	}
	defer queueManager.Close()
	log.Println("Connected to AMQP service:", queueManager)

	crawler, err := http_crawler.NewCrawler(rootURL)
	if err != nil {
		log.Fatal("Couldn't generate Crawler:", err)
	}
	log.Println("Generated crawler:", crawler)

	deliveries, err := queueManager.Consume()
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Generated delivery (consumer) channel:", deliveries)

	dontQuit := make(chan int)

	var acknowledge, crawlItems, extract <-chan *CrawlerMessageItem
	publish := make(<-chan string, 100)

	crawlItems = ReadFromQueue(deliveries, ttlHashSet, splitPaths(blacklistPaths))
	extract = CrawlURL(crawlItems, crawler)
	extract = WriteItemToDisk(extract)
	publish, acknowledge = ExtractURLs(extract)

	go PublishURLs(ttlHashSet, queueManager, publish)
	go AcknowledgeItem(acknowledge, ttlHashSet)

	<-dontQuit
}

func splitPaths(paths string) []string {
	if !strings.Contains(paths, ",") {
		return []string{paths}
	}

	splitPaths := strings.Split(paths, ",")
	trimmedPaths := make([]string, len(splitPaths))

	for i, v := range splitPaths {
		trimmedPaths[i] = v
	}

	return trimmedPaths
}
<commit_msg>Rename channels to make their intent clearer<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"strings"

	"github.com/alphagov/govuk_crawler_worker/http_crawler"
	"github.com/alphagov/govuk_crawler_worker/queue"
	"github.com/alphagov/govuk_crawler_worker/ttl_hash_set"
	"github.com/alphagov/govuk_crawler_worker/util"
)

var (
	amqpAddr       = util.GetEnvDefault("AMQP_ADDRESS", "amqp://guest:guest@localhost:5672/")
	exchangeName   = util.GetEnvDefault("AMQP_EXCHANGE", "govuk_crawler_exchange")
	queueName      = util.GetEnvDefault("AMQP_MESSAGE_QUEUE", "govuk_crawler_queue")
	redisAddr      = util.GetEnvDefault("REDIS_ADDRESS", "127.0.0.1:6379")
	redisKeyPrefix = util.GetEnvDefault("REDIS_KEY_PREFIX", "govuk_crawler_worker")
	rootURL        = util.GetEnvDefault("ROOT_URL", "https://www.gov.uk/")
	blacklistPaths = util.GetEnvDefault("BLACKLIST_PATHS", "/search,/government/uploads")
	mirrorRoot     = os.Getenv("MIRROR_ROOT")
)

func main() {
	if os.Getenv("GOMAXPROCS") == "" {
		// Use all available cores if not otherwise specified
		runtime.GOMAXPROCS(runtime.NumCPU())
	}
	log.Println(fmt.Sprintf("using GOMAXPROCS value of %d", runtime.NumCPU()))

	ttlHashSet, err := ttl_hash_set.NewTTLHashSet(redisKeyPrefix, redisAddr)
	if err != nil {
		log.Fatal(err)
	}
	defer ttlHashSet.Close()
	log.Println("Connected to Redis service:", ttlHashSet)

	queueManager, err := queue.NewQueueManager(amqpAddr, exchangeName, queueName)
	if err != nil {
		log.Fatal(err)
	}
	defer queueManager.Close()
	log.Println("Connected to AMQP service:", queueManager)

	crawler, err := http_crawler.NewCrawler(rootURL)
	if err != nil {
		log.Fatal("Couldn't generate Crawler:", err)
	}
	log.Println("Generated crawler:", crawler)

	deliveries, err := queueManager.Consume()
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Generated delivery (consumer) channel:", deliveries)

	dontQuit := make(chan int)

	var itemToAcknowledge, itemToCrawl, itemToPersist, itemToParse <-chan *CrawlerMessageItem
	itemToPublish := make(<-chan string, 100)

	itemToCrawl = ReadFromQueue(deliveries, ttlHashSet, splitPaths(blacklistPaths))
	itemToPersist = CrawlURL(itemToCrawl, crawler)
	itemToParse = WriteItemToDisk(itemToPersist)
	itemToPublish, itemToAcknowledge = ExtractURLs(itemToParse)

	go PublishURLs(ttlHashSet, queueManager, itemToPublish)
	go AcknowledgeItem(itemToAcknowledge, ttlHashSet)

	<-dontQuit
}

func splitPaths(paths string) []string {
	if !strings.Contains(paths, ",") {
		return []string{paths}
	}

	splitPaths := strings.Split(paths, ",")
	trimmedPaths := make([]string, len(splitPaths))

	for i, v := range splitPaths {
		trimmedPaths[i] = v
	}

	return trimmedPaths
}
<|endoftext|>


<commit_before>/*
go-renamer is a tool to rename file.
It renames a file to a specified file name if the target is a file.
It also renames all files in a specified directory.
	Author: hinagishi
*/
package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

/*
FileName contains an old file name, a new file
and a modified flag.
*/
type FileName struct {
	Oldname string
	Newname string
	Modify  bool
}

/*
Options contains commandline-argments
*/
type Options struct {
	Trim   string
	Suffix string
	Prefix string
}

func (opt *Options) isEmpty() bool {
	return opt.Trim == "" && opt.Suffix == "" && opt.Prefix == ""
}

func usage() {
	fmt.Println("Usage: go-renamer filepath")
}

func renameAll(f string, opt Options) {
	if !strings.HasSuffix(f, "/") {
		f += "/"
	}
	files, err := ioutil.ReadDir(f)
	if err != nil {
		fmt.Println(err)
		return
	}
	var filename []FileName
	for i := 0; i < len(files); i++ {
		if !files[i].IsDir() {
			filename = append(filename, FileName{Oldname: files[i].Name(), Newname: "", Modify: false})
		}
	}

	setName(filename, opt)

	for {
		showChangeList(filename)
		fmt.Print("Really change files name or modify?(y/n/m): ")
		reader := bufio.NewReader(os.Stdin)
		c, err := reader.ReadByte()
		if err != nil {
			fmt.Println(err)
			return
		}
		if c == 'y' {
			for i := 0; i < len(filename); i++ {
				os.Rename(f+filename[i].Oldname, f+filename[i].Newname)
			}
			break
		} else if c == 'm' {
			modifyName(filename)
			setName(filename, opt)
		} else {
			break
		}
	}
}

func modifyName(filename []FileName) {
	fmt.Print("Input modify number -->")
	reader := bufio.NewReader(os.Stdin)
	c, _, err := reader.ReadLine()
	if err != nil {
		fmt.Println(err)
		return
	}
	num, err := strconv.Atoi(string(c))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Print("Input new file name -->")
	c, _, err = reader.ReadLine()
	if checkName(filename, string(c)) {
		fmt.Printf("\x1b[31m%s\x1b[0m\n", "Already exists the same name file")
		return
	}
	filename[num].Newname = string(c)
	filename[num].Modify = true
}

func checkName(filename []FileName, f string) bool {
	for _, fn := range filename {
		if fn.Newname == f {
			return true
		}
	}
	return false
}

func setName(filename []FileName, opt Options) {
	index := 0
	for i := 0; i < len(filename); i++ {
		if filename[i].Modify {
			continue
		}
		if filename[i].Oldname[0] == '.' {
			filename[i].Newname = filename[i].Oldname
		} else if strings.Index(filename[i].Oldname, ".") == -1 {
			tmp := fmt.Sprintf("%03d", index)
			filename[i].Newname = tmp
			index++
		} else if !opt.isEmpty() {
			base := filepath.Base(filename[i].Oldname)
			base = strings.Trim(base, opt.Trim)
			base += opt.Suffix
			base = opt.Prefix + base
			filename[i].Newname = base
			index++
		} else {
			tmp := strings.Split(filename[i].Oldname, ".")
			suffix := tmp[len(tmp)-1]
			fname := fmt.Sprintf("%03d.%s", index, suffix)
			filename[i].Newname = fname
			index++
		}
	}
}

func showChangeList(filename []FileName) {
	fmt.Print("\n")
	for i := 0; i < len(filename); i++ {
		fmt.Println(strconv.Itoa(i) + ": " + filename[i].Oldname + " --> " + filename[i].Newname)
	}
	fmt.Print("\n")
}

func main() {
	if len(os.Args) < 2 {
		usage()
		os.Exit(1)
	}

	var opt Options
	var target string
	for i := 1; i < len(os.Args); i++ {
		if os.Args[i] == "-t" {
			if i+1 >= len(os.Args) {
				usage()
				return
			} else {
				opt.Trim = os.Args[i+1]
				i++
			}
		} else if os.Args[i] == "-s" {
			if i+1 >= len(os.Args) {
				usage()
				return
			} else {
				opt.Suffix = os.Args[i+1]
				i++
			}
		} else if os.Args[i] == "-p" {
			if i+1 >= len(os.Args) {
				usage()
				return
			} else {
				opt.Prefix = os.Args[i+1]
				i++
			}
		} else {
			target = os.Args[i]
		}
	}
	if target == "" {
		usage()
		return
	}
	f, err := os.Stat(target)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	if f.IsDir() {
		renameAll(target, opt)
	} else {
		path := filepath.Dir(target)
		if opt.isEmpty() {
			scanner := bufio.NewScanner(os.Stdin)
			fmt.Print(f.Name() + "--> ")
			for scanner.Scan() {
				nf := scanner.Text()
				if nf == "" {
					fmt.Print(f.Name() + "--> ")
					continue
				}
				path := filepath.Dir(target)
				os.Rename(target, path+"/"+nf)
				break
			}

		}
		base := filepath.Base(target)
		base = strings.Trim(base, opt.Trim)
		base += opt.Suffix
		base = opt.Prefix + base
		os.Rename(target, path+"/"+base)

	}
}
<commit_msg>fix to allow wildcard argment<commit_after>/*
go-renamer is a tool to rename file.
It renames a file to a specified file name if the target is a file.
It also renames all files in a specified directory.
	Author: hinagishi
*/
package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

/*
FileName contains an old file name, a new file
and a modified flag.
*/
type FileName struct {
	Oldname string
	Newname string
	Modify  bool
}

/*
Options contains commandline-argments
*/
type Options struct {
	Trim   string
	Suffix string
	Prefix string
}

func (opt *Options) isEmpty() bool {
	return opt.Trim == "" && opt.Suffix == "" && opt.Prefix == ""
}

func usage() {
	fmt.Println("Usage: go-renamer filepath")
}

func renameAll(f string, opt Options) {
	if !strings.HasSuffix(f, "/") {
		f += "/"
	}
	files, err := ioutil.ReadDir(f)
	if err != nil {
		fmt.Println(err)
		return
	}
	var filename []FileName
	for i := 0; i < len(files); i++ {
		if !files[i].IsDir() {
			filename = append(filename, FileName{Oldname: files[i].Name(), Newname: "", Modify: false})
		}
	}

	setName(filename, opt)

	for {
		showChangeList(filename)
		fmt.Print("Really change files name or modify?(y/n/m): ")
		reader := bufio.NewReader(os.Stdin)
		c, err := reader.ReadByte()
		if err != nil {
			fmt.Println(err)
			return
		}
		if c == 'y' {
			for i := 0; i < len(filename); i++ {
				os.Rename(f+filename[i].Oldname, f+filename[i].Newname)
			}
			break
		} else if c == 'm' {
			modifyName(filename)
			setName(filename, opt)
		} else {
			break
		}
	}
}

func modifyName(filename []FileName) {
	fmt.Print("Input modify number -->")
	reader := bufio.NewReader(os.Stdin)
	c, _, err := reader.ReadLine()
	if err != nil {
		fmt.Println(err)
		return
	}
	num, err := strconv.Atoi(string(c))
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Print("Input new file name -->")
	c, _, err = reader.ReadLine()
	if checkName(filename, string(c)) {
		fmt.Printf("\x1b[31m%s\x1b[0m\n", "Already exists the same name file")
		return
	}
	filename[num].Newname = string(c)
	filename[num].Modify = true
}

func checkName(filename []FileName, f string) bool {
	for _, fn := range filename {
		if fn.Newname == f {
			return true
		}
	}
	return false
}

func setName(filename []FileName, opt Options) {
	index := 0
	for i := 0; i < len(filename); i++ {
		if filename[i].Modify {
			continue
		}
		if filename[i].Oldname[0] == '.' {
			filename[i].Newname = filename[i].Oldname
		} else if strings.Index(filename[i].Oldname, ".") == -1 {
			tmp := fmt.Sprintf("%03d", index)
			filename[i].Newname = tmp
			index++
		} else if !opt.isEmpty() {
			base := filepath.Base(filename[i].Oldname)
			base = strings.Trim(base, opt.Trim)
			base += opt.Suffix
			base = opt.Prefix + base
			filename[i].Newname = base
			index++
		} else {
			tmp := strings.Split(filename[i].Oldname, ".")
			suffix := tmp[len(tmp)-1]
			fname := fmt.Sprintf("%03d.%s", index, suffix)
			filename[i].Newname = fname
			index++
		}
	}
}

func showChangeList(filename []FileName) {
	fmt.Print("\n")
	for i := 0; i < len(filename); i++ {
		fmt.Println(strconv.Itoa(i) + ": " + filename[i].Oldname + " --> " + filename[i].Newname)
	}
	fmt.Print("\n")
}

func main() {
	if len(os.Args) < 2 {
		usage()
		os.Exit(1)
	}

	var opt Options
	var target []string
	for i := 1; i < len(os.Args); i++ {
		if os.Args[i] == "-t" {
			if i+1 >= len(os.Args) {
				usage()
				return
			} else {
				opt.Trim = os.Args[i+1]
				i++
			}
		} else if os.Args[i] == "-s" {
			if i+1 >= len(os.Args) {
				usage()
				return
			} else {
				opt.Suffix = os.Args[i+1]
				i++
			}
		} else if os.Args[i] == "-p" {
			if i+1 >= len(os.Args) {
				usage()
				return
			} else {
				opt.Prefix = os.Args[i+1]
				i++
			}
		} else {
			target = append(target, os.Args[i])
		}
	}
	if len(target) == 0 {
		usage()
		return
	}

	for _, t := range target {
		f, err := os.Stat(t)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		if f.IsDir() && len(target) == 1 {
			renameAll(t, opt)
		} else {
			path := filepath.Dir(t)
			if opt.isEmpty() {
				scanner := bufio.NewScanner(os.Stdin)
				fmt.Print(f.Name() + "--> ")
				for scanner.Scan() {
					nf := scanner.Text()
					if nf == "" {
						fmt.Print(f.Name() + "--> ")
						continue
					}
					os.Rename(t, path+"/"+nf)
					break
				}

			}

			base := filepath.Base(t)
			base = strings.Trim(base, opt.Trim)
			base += opt.Suffix
			base = opt.Prefix + base
			os.Rename(t, path+"/"+base)

		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/codegangsta/cli"
)

var (
	noop    = false
	build   = ""
	version = "dev-build"
)

func readIn(lines chan string, tee bool) {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		lines <- scanner.Text()
		if tee {
			fmt.Println(scanner.Text())
		}
	}
	close(lines)
}

func writeTemp(lines chan string) string {
	tmp, err := ioutil.TempFile(os.TempDir(), "slackcat-")
	failOnError(err, "unable to create tmpfile")

	w := bufio.NewWriter(tmp)
	for line := range lines {
		fmt.Fprintln(w, line)
	}
	w.Flush()

	return tmp.Name()
}

func printFullVersion(c *cli.Context) {
	fmt.Fprintf(c.App.Writer, "%v version %v, build %v\n", c.App.Name, c.App.Version, build)
}

func main() {
	cli.VersionPrinter = printFullVersion

	app := cli.NewApp()
	app.Name = "slackcat"
	app.Usage = "redirect a file to slack"
	app.Version = version
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "channel, c",
			Usage: "Slack channel or group to post to",
		},
		cli.StringFlag{
			Name:  "comment",
			Usage: "Initial comment for snippet",
		},
		cli.BoolFlag{
			Name:  "configure",
			Usage: "Configure Slackcat via oauth",
		},
		cli.StringFlag{
			Name:  "filename, n",
			Usage: "Filename for upload. Defaults to current timestamp",
		},
		cli.StringFlag{
			Name:  "filetype",
			Usage: "Specify filetype for syntax highlighting",
		},
		cli.BoolFlag{
			Name:  "list",
			Usage: "List team channel names",
		},
		cli.BoolFlag{
			Name:  "noop",
			Usage: "Skip posting file to Slack. Useful for testing",
		},
		cli.BoolFlag{
			Name:  "stream, s",
			Usage: "Stream messages to Slack continuously instead of uploading a single snippet",
		},
		cli.BoolFlag{
			Name:  "tee, t",
			Usage: "Print stdin to screen before posting",
		},
	}

	app.Action = func(c *cli.Context) {
		if c.Bool("configure") {
			configureOA()
			os.Exit(0)
		}

		configPath, exists := getConfigPath()
		if !exists {
			exitErr(fmt.Errorf("missing config file at %s\nuse --configure to create", configPath))
		}
		config := ReadConfig(configPath)

		team, channel, err := config.parseChannelOpt(c.String("channel"))
		failOnError(err)

		noop = c.Bool("noop")
		fileName := c.String("filename")
		fileType := c.String("filetype")
		fileComment := c.String("comment")

		token := config.Teams[team]
		if token == "" {
			exitErr(fmt.Errorf("no such team: %s", team))
		}

		slackcat := newSlackcat(token, channel)

		if c.Bool("list") {
			fmt.Println("channels:")
			for _, n := range slackcat.listChannels() {
				fmt.Printf("  %s\n", n)
			}
			fmt.Println("groups:")
			for _, n := range slackcat.listGroups() {
				fmt.Printf("  %s\n", n)
			}
			fmt.Println("ims:")
			for _, n := range slackcat.listIms() {
				fmt.Printf("  %s\n", n)
			}
			os.Exit(0)
		}

		if len(c.Args()) > 0 {
			if c.Bool("stream") {
				output("filepath provided, ignoring stream option")
			}
			filePath := c.Args()[0]
			if fileName == "" {
				fileName = filepath.Base(filePath)
			}
			slackcat.postFile(filePath, fileName, fileType, fileComment)
			os.Exit(0)
		}

		lines := make(chan string)
		go readIn(lines, c.Bool("tee"))

		if c.Bool("stream") {
			slackcat.stream(lines)
		} else {
			filePath := writeTemp(lines)
			defer os.Remove(filePath)
			slackcat.postFile(filePath, fileName, fileType, fileComment)
			os.Exit(0)
		}
	}

	app.Run(os.Args)

}
<commit_msg>add custom usage error handler with non-0 exit code<commit_after>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/codegangsta/cli"
)

var (
	noop    = false
	build   = ""
	version = "dev-build"
)

func readIn(lines chan string, tee bool) {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		lines <- scanner.Text()
		if tee {
			fmt.Println(scanner.Text())
		}
	}
	close(lines)
}

func writeTemp(lines chan string) string {
	tmp, err := ioutil.TempFile(os.TempDir(), "slackcat-")
	failOnError(err, "unable to create tmpfile")

	w := bufio.NewWriter(tmp)
	for line := range lines {
		fmt.Fprintln(w, line)
	}
	w.Flush()

	return tmp.Name()
}

func handleUsageError(c *cli.Context, err error, _ bool) error {
	fmt.Fprintf(c.App.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
	cli.ShowAppHelp(c)
	return cli.NewExitError("", 1)
}

func printFullVersion(c *cli.Context) {
	fmt.Fprintf(c.App.Writer, "%v version %v, build %v\n", c.App.Name, c.App.Version, build)
}

func main() {
	cli.VersionPrinter = printFullVersion

	app := cli.NewApp()
	app.Name = "slackcat"
	app.Usage = "redirect a file to slack"
	app.Version = version
	app.OnUsageError = handleUsageError
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "channel, c",
			Usage: "Slack channel or group to post to",
		},
		cli.StringFlag{
			Name:  "comment",
			Usage: "Initial comment for snippet",
		},
		cli.BoolFlag{
			Name:  "configure",
			Usage: "Configure Slackcat via oauth",
		},
		cli.StringFlag{
			Name:  "filename, n",
			Usage: "Filename for upload. Defaults to current timestamp",
		},
		cli.StringFlag{
			Name:  "filetype",
			Usage: "Specify filetype for syntax highlighting",
		},
		cli.BoolFlag{
			Name:  "list",
			Usage: "List team channel names",
		},
		cli.BoolFlag{
			Name:  "noop",
			Usage: "Skip posting file to Slack. Useful for testing",
		},
		cli.BoolFlag{
			Name:  "stream, s",
			Usage: "Stream messages to Slack continuously instead of uploading a single snippet",
		},
		cli.BoolFlag{
			Name:  "tee, t",
			Usage: "Print stdin to screen before posting",
		},
	}

	app.Action = func(c *cli.Context) {
		if c.Bool("configure") {
			configureOA()
			os.Exit(0)
		}

		configPath, exists := getConfigPath()
		if !exists {
			exitErr(fmt.Errorf("missing config file at %s\nuse --configure to create", configPath))
		}
		config := ReadConfig(configPath)

		team, channel, err := config.parseChannelOpt(c.String("channel"))
		failOnError(err)

		noop = c.Bool("noop")
		fileName := c.String("filename")
		fileType := c.String("filetype")
		fileComment := c.String("comment")

		token := config.Teams[team]
		if token == "" {
			exitErr(fmt.Errorf("no such team: %s", team))
		}

		slackcat := newSlackcat(token, channel)

		if c.Bool("list") {
			fmt.Println("channels:")
			for _, n := range slackcat.listChannels() {
				fmt.Printf("  %s\n", n)
			}
			fmt.Println("groups:")
			for _, n := range slackcat.listGroups() {
				fmt.Printf("  %s\n", n)
			}
			fmt.Println("ims:")
			for _, n := range slackcat.listIms() {
				fmt.Printf("  %s\n", n)
			}
			os.Exit(0)
		}

		if len(c.Args()) > 0 {
			if c.Bool("stream") {
				output("filepath provided, ignoring stream option")
			}
			filePath := c.Args()[0]
			if fileName == "" {
				fileName = filepath.Base(filePath)
			}
			slackcat.postFile(filePath, fileName, fileType, fileComment)
			os.Exit(0)
		}

		lines := make(chan string)
		go readIn(lines, c.Bool("tee"))

		if c.Bool("stream") {
			slackcat.stream(lines)
		} else {
			filePath := writeTemp(lines)
			defer os.Remove(filePath)
			slackcat.postFile(filePath, fileName, fileType, fileComment)
			os.Exit(0)
		}
	}

	app.Run(os.Args)

}
<|endoftext|>