<commit_before>package main

import (
	"log"
	"os"

	"github.com/joho/godotenv"
	_ "github.com/joho/godotenv/autoload"
	"github.com/urfave/cli"
)

// Version set at compile-time
var Version string

func main() {
	app := cli.NewApp()
	app.Name = "Drone Discord"
	app.Usage = "Sending message to Discord channel using Webhook"
	app.Copyright = "Copyright (c) 2017 Bo-Yi Wu"
	app.Authors = []cli.Author{
		{
			Name:  "Bo-Yi Wu",
			Email: "appleboy.tw@gmail.com",
		},
	}
	app.Action = run
	app.Version = Version
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "webhook-id",
			Usage:  "discord webhook id",
			EnvVar: "PLUGIN_WEBHOOK_ID,WEBHOOK_ID,DISCORD_WEBHOOK_ID",
		},
		cli.StringFlag{
			Name:   "webhook-token",
			Usage:  "discord webhook token",
			EnvVar: "PLUGIN_WEBHOOK_TOKEN,WEBHOOK_TOKEN,DISCORD_WEBHOOK_TOKEN",
		},
		cli.StringSliceFlag{
			Name:   "message",
			Usage:  "the message contents (up to 2000 characters)",
			EnvVar: "PLUGIN_MESSAGE,MESSAGE",
		},
		cli.StringFlag{
			Name:   "color",
			Usage:  "color code of the embed",
			EnvVar: "PLUGIN_COLOR,COLOR",
		},
		cli.BoolFlag{
			Name:   "wait",
			Usage:  "waits for server confirmation of message send before response, and returns the created message body",
			EnvVar: "PLUGIN_WAIT,WAIT",
		},
		cli.BoolFlag{
			Name:   "tts",
			Usage:  "true if this is a TTS message",
			EnvVar: "PLUGIN_TTS,TTS",
		},
		cli.StringFlag{
			Name:   "username",
			Usage:  "override the default username of the webhook",
			EnvVar: "PLUGIN_USERNAME,USERNAME",
		},
		cli.StringFlag{
			Name:   "avatar-url",
			Usage:  "override the default avatar of the webhook",
			EnvVar: "PLUGIN_AVATAR_URL,AVATAR_URL",
		},
		cli.BoolFlag{
			Name:   "drone",
			Usage:  "environment is drone",
			EnvVar: "DRONE",
		},
		cli.StringFlag{
			Name:   "repo.owner",
			Usage:  "repository owner",
			EnvVar: "DRONE_REPO_OWNER",
		},
		cli.StringFlag{
			Name:   "repo.name",
			Usage:  "repository name",
			EnvVar: "DRONE_REPO_NAME",
		},
		cli.StringFlag{
			Name:   "commit.sha",
			Usage:  "git commit sha",
			EnvVar: "DRONE_COMMIT_SHA",
		},
		cli.StringFlag{
			Name:   "commit.refspec",
			Usage:  "git commit ref spec",
			EnvVar: "DRONE_COMMIT_REF",
		},
		cli.StringFlag{
			Name:   "commit.branch",
			Value:  "master",
			Usage:  "git commit branch",
			EnvVar: "DRONE_COMMIT_BRANCH",
		},
		cli.StringFlag{
			Name:   "commit.author",
			Usage:  "git author name",
			EnvVar: "DRONE_COMMIT_AUTHOR",
		},
		cli.StringFlag{
			Name:   "commit.author.email",
			Usage:  "git author email",
			EnvVar: "DRONE_COMMIT_AUTHOR_EMAIL",
		},
		cli.StringFlag{
			Name:   "commit.author.avatar",
			Usage:  "git author avatar",
			EnvVar: "DRONE_COMMIT_AUTHOR_AVATAR",
		},
		cli.StringFlag{
			Name:   "commit.message",
			Usage:  "commit message",
			EnvVar: "DRONE_COMMIT_MESSAGE",
		},
		cli.StringFlag{
			Name:   "build.event",
			Value:  "push",
			Usage:  "build event",
			EnvVar: "DRONE_BUILD_EVENT",
		},
		cli.IntFlag{
			Name:   "build.number",
			Usage:  "build number",
			EnvVar: "DRONE_BUILD_NUMBER",
		},
		cli.StringFlag{
			Name:   "build.status",
			Usage:  "build status",
			Value:  "success",
			EnvVar: "DRONE_BUILD_STATUS",
		},
		cli.StringFlag{
			Name:   "build.link",
			Usage:  "build link",
			EnvVar: "DRONE_BUILD_LINK",
		},
		cli.StringFlag{
			Name:   "build.tag",
			Usage:  "build tag",
			EnvVar: "DRONE_TAG",
		},
		cli.Float64Flag{
			Name:   "job.started",
			Usage:  "job started",
			EnvVar: "DRONE_JOB_STARTED",
		},
		cli.Float64Flag{
			Name:   "job.finished",
			Usage:  "job finished",
			EnvVar: "DRONE_JOB_FINISHED",
		},
		cli.StringFlag{
			Name:  "env-file",
			Usage: "source env file",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

func run(c *cli.Context) error {
	if c.String("env-file") != "" {
		_ = godotenv.Load(c.String("env-file"))
	}

	plugin := Plugin{
		Repo: Repo{
			Owner: c.String("repo.owner"),
			Name:  c.String("repo.name"),
		},
		Build: Build{
			Tag:      c.String("build.tag"),
			Number:   c.Int("build.number"),
			Event:    c.String("build.event"),
			Status:   c.String("build.status"),
			Commit:   c.String("commit.sha"),
			RefSpec:  c.String("commit.refspec"),
			Branch:   c.String("commit.branch"),
			Author:   c.String("commit.author"),
			Email:    c.String("commit.author.email"),
			Avatar:   c.String("commit.author.avatar"),
			Message:  c.String("commit.message"),
			Link:     c.String("build.link"),
			Started:  c.Float64("job.started"),
			Finished: c.Float64("job.finished"),
		},
		Config: Config{
			WebhookID:    c.String("webhook-id"),
			WebhookToken: c.String("webhook-token"),
			Message:      c.StringSlice("message"),
			Color:        c.String("color"),
			Drone:        c.Bool("drone"),
		},
		Payload: Payload{
			Wait:      c.Bool("wait"),
			Username:  c.String("username"),
			AvatarURL: c.String("avatar-url"),
			TTS:       c.Bool("tts"),
		},
	}

	return plugin.Exec()
}
<commit_msg>add year in copyright<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/joho/godotenv"
	_ "github.com/joho/godotenv/autoload"
	"github.com/urfave/cli"
)

// Version set at compile-time
var Version string

func main() {
	year := fmt.Sprintf("%v", time.Now().Year())
	app := cli.NewApp()
	app.Name = "Drone Discord"
	app.Usage = "Sending message to Discord channel using Webhook"
	app.Copyright = "Copyright (c) " + year + " Bo-Yi Wu"
	app.Authors = []cli.Author{
		{
			Name:  "Bo-Yi Wu",
			Email: "appleboy.tw@gmail.com",
		},
	}
	app.Action = run
	app.Version = Version
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "webhook-id",
			Usage:  "discord webhook id",
			EnvVar: "PLUGIN_WEBHOOK_ID,WEBHOOK_ID,DISCORD_WEBHOOK_ID",
		},
		cli.StringFlag{
			Name:   "webhook-token",
			Usage:  "discord webhook token",
			EnvVar: "PLUGIN_WEBHOOK_TOKEN,WEBHOOK_TOKEN,DISCORD_WEBHOOK_TOKEN",
		},
		cli.StringSliceFlag{
			Name:   "message",
			Usage:  "the message contents (up to 2000 characters)",
			EnvVar: "PLUGIN_MESSAGE,MESSAGE",
		},
		cli.StringFlag{
			Name:   "color",
			Usage:  "color code of the embed",
			EnvVar: "PLUGIN_COLOR,COLOR",
		},
		cli.BoolFlag{
			Name:   "wait",
			Usage:  "waits for server confirmation of message send before response, and returns the created message body",
			EnvVar: "PLUGIN_WAIT,WAIT",
		},
		cli.BoolFlag{
			Name:   "tts",
			Usage:  "true if this is a TTS message",
			EnvVar: "PLUGIN_TTS,TTS",
		},
		cli.StringFlag{
			Name:   "username",
			Usage:  "override the default username of the webhook",
			EnvVar: "PLUGIN_USERNAME,USERNAME",
		},
		cli.StringFlag{
			Name:   "avatar-url",
			Usage:  "override the default avatar of the webhook",
			EnvVar: "PLUGIN_AVATAR_URL,AVATAR_URL",
		},
		cli.BoolFlag{
			Name:   "drone",
			Usage:  "environment is drone",
			EnvVar: "DRONE",
		},
		cli.StringFlag{
			Name:   "repo.owner",
			Usage:  "repository owner",
			EnvVar: "DRONE_REPO_OWNER",
		},
		cli.StringFlag{
			Name:   "repo.name",
			Usage:  "repository name",
			EnvVar: "DRONE_REPO_NAME",
		},
		cli.StringFlag{
			Name:   "commit.sha",
			Usage:  "git commit sha",
			EnvVar: "DRONE_COMMIT_SHA",
		},
		cli.StringFlag{
			Name:   "commit.refspec",
			Usage:  "git commit ref spec",
			EnvVar: "DRONE_COMMIT_REF",
		},
		cli.StringFlag{
			Name:   "commit.branch",
			Value:  "master",
			Usage:  "git commit branch",
			EnvVar: "DRONE_COMMIT_BRANCH",
		},
		cli.StringFlag{
			Name:   "commit.author",
			Usage:  "git author name",
			EnvVar: "DRONE_COMMIT_AUTHOR",
		},
		cli.StringFlag{
			Name:   "commit.author.email",
			Usage:  "git author email",
			EnvVar: "DRONE_COMMIT_AUTHOR_EMAIL",
		},
		cli.StringFlag{
			Name:   "commit.author.avatar",
			Usage:  "git author avatar",
			EnvVar: "DRONE_COMMIT_AUTHOR_AVATAR",
		},
		cli.StringFlag{
			Name:   "commit.message",
			Usage:  "commit message",
			EnvVar: "DRONE_COMMIT_MESSAGE",
		},
		cli.StringFlag{
			Name:   "build.event",
			Value:  "push",
			Usage:  "build event",
			EnvVar: "DRONE_BUILD_EVENT",
		},
		cli.IntFlag{
			Name:   "build.number",
			Usage:  "build number",
			EnvVar: "DRONE_BUILD_NUMBER",
		},
		cli.StringFlag{
			Name:   "build.status",
			Usage:  "build status",
			Value:  "success",
			EnvVar: "DRONE_BUILD_STATUS",
		},
		cli.StringFlag{
			Name:   "build.link",
			Usage:  "build link",
			EnvVar: "DRONE_BUILD_LINK",
		},
		cli.StringFlag{
			Name:   "build.tag",
			Usage:  "build tag",
			EnvVar: "DRONE_TAG",
		},
		cli.Float64Flag{
			Name:   "job.started",
			Usage:  "job started",
			EnvVar: "DRONE_JOB_STARTED",
		},
		cli.Float64Flag{
			Name:   "job.finished",
			Usage:  "job finished",
			EnvVar: "DRONE_JOB_FINISHED",
		},
		cli.StringFlag{
			Name:  "env-file",
			Usage: "source env file",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

func run(c *cli.Context) error {
	if c.String("env-file") != "" {
		_ = godotenv.Load(c.String("env-file"))
	}

	plugin := Plugin{
		Repo: Repo{
			Owner: c.String("repo.owner"),
			Name:  c.String("repo.name"),
		},
		Build: Build{
			Tag:      c.String("build.tag"),
			Number:   c.Int("build.number"),
			Event:    c.String("build.event"),
			Status:   c.String("build.status"),
			Commit:   c.String("commit.sha"),
			RefSpec:  c.String("commit.refspec"),
			Branch:   c.String("commit.branch"),
			Author:   c.String("commit.author"),
			Email:    c.String("commit.author.email"),
			Avatar:   c.String("commit.author.avatar"),
			Message:  c.String("commit.message"),
			Link:     c.String("build.link"),
			Started:  c.Float64("job.started"),
			Finished: c.Float64("job.finished"),
		},
		Config: Config{
			WebhookID:    c.String("webhook-id"),
			WebhookToken: c.String("webhook-token"),
			Message:      c.StringSlice("message"),
			Color:        c.String("color"),
			Drone:        c.Bool("drone"),
		},
		Payload: Payload{
			Wait:      c.Bool("wait"),
			Username:  c.String("username"),
			AvatarURL: c.String("avatar-url"),
			TTS:       c.Bool("tts"),
		},
	}

	return plugin.Exec()
}
<|endoftext|>


<commit_before>package main

import (
  "fmt"
  "net"
  "net/http"
  "net/http/fcgi"
  "os"
)

type FastCGIServer struct{}

func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
  resp.Write([]byte("{}"))
}

func main() {
  listener, err := net.Listen("tcp", "127.0.0.1:9000")
  if err != nil {
    fmt.Fprint(os.Stderr, "Failed to open socket 9000: ", err)
  }

  srv := new(FastCGIServer)
  err = fcgi.Serve(listener, srv)
  if err != nil {
    fmt.Fprint(os.Stderr, "Server crashed: ", err)
  }
}
<commit_msg>Exit on failure and log server start/stop<commit_after>package main

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

type FastCGIServer struct{}

func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
  resp.Write([]byte("{}"))
}

func main() {
  fmt.Fprintln(os.Stderr, "Server started at ", time.Now().String())

  listener, err := net.Listen("tcp", "127.0.0.1:9000")
  if err != nil {
    fmt.Fprintln(os.Stderr, "Failed to open socket 9000: ", err)
    os.Exit(1)
  }

  srv := new(FastCGIServer)
  err = fcgi.Serve(listener, srv)
  if err != nil {
    fmt.Fprintln(os.Stderr, "Server crashed: ", err)
    os.Exit(1)
  }

  fmt.Fprintln(os.Stderr, "Server stopped at ", time.Now().String())
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/appleboy/gopush/gopush"
	"github.com/sideshow/apns2/certificate"
	apns "github.com/sideshow/apns2"
	"log"
)

func main() {
	version := flag.Bool("v", false, "gopush version")
	confPath := flag.String("c", "", "yaml configuration file path for gopush")
	port := flag.String("p", "", "port number for gopush")

	flag.Parse()

	if *version {
		gopush.PrintGoPushVersion()
		return
	}

	var err error

	// set default parameters.
	gopush.PushConf = gopush.BuildDefaultPushConf()

	// load user define config.
	if *confPath != "" {
		gopush.PushConf, err = gopush.LoadConfYaml(*confPath)

		if err != nil {
			log.Printf("Unable to load yaml config file: '%v'", err)

			return
		}
	}

	if gopush.PushConf.Ios.Enabled {
		gopush.CertificatePemIos, err = certificate.FromPemFile(gopush.PushConf.Ios.PemKeyPath, "")

		if err != nil {
			log.Println("Cert Error:", err)

			return
		}

		if gopush.PushConf.Ios.Production {
			gopush.ApnsClient = apns.NewClient(gopush.CertificatePemIos).Production()
		} else {
			gopush.ApnsClient = apns.NewClient(gopush.CertificatePemIos).Development()
		}
	}

	// overwrite server port
	if *port != "" {
		gopush.PushConf.Core.Port = *port
	}

	gopush.RunHTTPServer()
}
<commit_msg>support android key and ios cert path flag.<commit_after>package main

import (
	"flag"
	"github.com/appleboy/gopush/gopush"
	"github.com/sideshow/apns2/certificate"
	apns "github.com/sideshow/apns2"
	"log"
)

func main() {
	version := flag.Bool("v", false, "gopush version")
	confPath := flag.String("c", "", "yaml configuration file path for gopush")
	certificateKeyPath := flag.String("i", "", "iOS certificate key file path for gopush")
	apiKey := flag.String("k", "", "Android api key configuration for gopush")
	port := flag.String("p", "", "port number for gopush")

	flag.Parse()

	if *version {
		gopush.PrintGoPushVersion()
		return
	}

	var err error

	// set default parameters.
	gopush.PushConf = gopush.BuildDefaultPushConf()

	// load user define config.
	if *confPath != "" {
		gopush.PushConf, err = gopush.LoadConfYaml(*confPath)

		if err != nil {
			log.Printf("Unable to load yaml config file: '%v'", err)

			return
		}
	}

	if gopush.PushConf.Ios.Enabled {

		if *certificateKeyPath != "" {
			gopush.PushConf.Ios.PemKeyPath = *certificateKeyPath
		}

		if gopush.PushConf.Ios.PemKeyPath == "" {
			log.Println("iOS certificate path not define")

			return
		}

		gopush.CertificatePemIos, err = certificate.FromPemFile(gopush.PushConf.Ios.PemKeyPath, "")

		if err != nil {
			log.Println("Cert Error:", err)

			return
		}

		if gopush.PushConf.Ios.Production {
			gopush.ApnsClient = apns.NewClient(gopush.CertificatePemIos).Production()
		} else {
			gopush.ApnsClient = apns.NewClient(gopush.CertificatePemIos).Development()
		}
	}

	// check andorid api key exist
	if gopush.PushConf.Android.Enabled {

		if *apiKey != "" {
			gopush.PushConf.Android.ApiKey = *apiKey
		}

		if gopush.PushConf.Android.ApiKey == "" {
			log.Println("Android API Key not define")

			return
		}
	}

	// overwrite server port
	if *port != "" {
		gopush.PushConf.Core.Port = *port
	}

	gopush.RunHTTPServer()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"
	"log"
	"net/http"
	"os"
	"time"
)

var config = Configuration{
	SlackName:       os.Getenv("SLACK_NAME"),
	SlackIconUrl:    os.Getenv("SLACK_ICON_URL"),
	SlackWebHookUrl: os.Getenv("SLACK_WEBHOOK_URL"),
	SlackChannel:    os.Getenv("SLACK_CHANNEL"),
	Host:            os.Getenv("HOST"),
	VoicemailAudio:  os.Getenv("VOICEMAIL_AUDIO"),
	ElksUserName:    os.Getenv("ELKS_USERNAME"),
	ElksPassword:    os.Getenv("ELKS_PASSWORD"),
	AWSAccessKey:    os.Getenv("AWS_ACCESS_KEY"),
	AWSSecretKey:    os.Getenv("AWS_SECRET_KEY"),
	S3BucketName:    os.Getenv("S3_BUCKET_NAME"),
}

func main() {
	AWSAuth := aws.Auth{
		AccessKey: config.AWSAccessKey,
		SecretKey: config.AWSSecretKey,
	}
	region := aws.EUWest

	http.HandleFunc("/incoming_call", func(w http.ResponseWriter, r *http.Request) {

		i := IncomingResponse{
			Play: config.VoicemailAudio,
			Next: struct {
				Record string `json:"record"`
			}{
				Record: config.Host + "/voicemail",
			},
		}
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		json.NewEncoder(w).Encode(i)
	})

	http.HandleFunc("/voicemail", func(w http.ResponseWriter, r *http.Request) {
		log.Print("Incoming message...")
		err := r.ParseForm()
		check(err)
		from := r.FormValue("from")
		wav := r.FormValue("wav")
		if len(wav) == 0 || len(from) == 0 {
			log.Print("Bad request")
			http.Error(w, "", http.StatusBadRequest)
			return
		}

		log.Print("Incoming message from " + from)
		log.Print("Retrieving audio file " + wav + "...")
		req, err := http.NewRequest("GET", wav, nil)
		req.SetBasicAuth(config.ElksUserName, config.ElksPassword)
		client := &http.Client{}
		resp, err := client.Do(req)
		check(err)
		defer resp.Body.Close()

		log.Print("Downloading...")
		filebytes := make([]byte, resp.ContentLength)
		buffer := bufio.NewReader(resp.Body)
		_, err = buffer.Read(filebytes)
		check(err)

		log.Print("Connecting to AWS S3...")
		connection := s3.New(AWSAuth, region)
		bucket := connection.Bucket(config.S3BucketName)

		path := "voicemail/" + time.Now().Format("20060102150405") + ".wav"
		log.Print("Writing " + path + " to " + config.S3BucketName + "...")
		err = bucket.Put(path, filebytes, "audio/wav", s3.ACL("public-read"))
		check(err)

		log.Print("Posting message to Slack, channel " + config.SlackChannel + "...")
		payload := SlackPayload{
			UserName: config.SlackName,
			IconUrl:  config.SlackIconUrl,
			Text:     "New voice message from " + from + " <" + bucket.URL(path) + ">!",
			Channel:  config.SlackChannel,
		}

		json_payload, _ := json.Marshal(payload)
		slack_req, err := http.NewRequest("POST", config.SlackWebHookUrl, bytes.NewBuffer([]byte(json_payload)))
		req.Header.Set("Content-Type", "application/json")

		slack_client := &http.Client{}
		slack_resp, err := slack_client.Do(slack_req)
		check(err)
		defer slack_resp.Body.Close()

		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Write(nil)
	})

	port := os.Getenv("PORT")
	if len(port) == 0 {
		port = "3000"
	}

	http.ListenAndServe(":"+port, nil)
}

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

type Configuration struct {
	SlackName       string
	SlackIconUrl    string
	SlackWebHookUrl string
	SlackChannel    string
	Host            string
	VoicemailAudio  string
	ElksUserName    string
	ElksPassword    string
	AWSAccessKey    string
	AWSSecretKey    string
	S3BucketName    string
}

type SlackPayload struct {
	UserName string `json:"username"`
	IconUrl  string `json:"icon_url"`
	Text     string `json:"text"`
	Channel  string `json:"channel"`
}

type IncomingResponse struct {
	Play string `json:"play"`
	Next struct {
		Record string `json:"record"`
	} `json:"next"`
}
<commit_msg>Fix issue that mangles the downloaded .WAV file.<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"
)

var config = Configuration{
	SlackName:       os.Getenv("SLACK_NAME"),
	SlackIconUrl:    os.Getenv("SLACK_ICON_URL"),
	SlackWebHookUrl: os.Getenv("SLACK_WEBHOOK_URL"),
	SlackChannel:    os.Getenv("SLACK_CHANNEL"),
	Host:            os.Getenv("HOST"),
	VoicemailAudio:  os.Getenv("VOICEMAIL_AUDIO"),
	ElksUserName:    os.Getenv("ELKS_USERNAME"),
	ElksPassword:    os.Getenv("ELKS_PASSWORD"),
	AWSAccessKey:    os.Getenv("AWS_ACCESS_KEY"),
	AWSSecretKey:    os.Getenv("AWS_SECRET_KEY"),
	S3BucketName:    os.Getenv("S3_BUCKET_NAME"),
}

func main() {
	AWSAuth := aws.Auth{
		AccessKey: config.AWSAccessKey,
		SecretKey: config.AWSSecretKey,
	}
	region := aws.EUWest

	http.HandleFunc("/incoming_call", func(w http.ResponseWriter, r *http.Request) {

		i := IncomingResponse{
			Play: config.VoicemailAudio,
			Next: struct {
				Record string `json:"record"`
			}{
				Record: config.Host + "/voicemail",
			},
		}
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		json.NewEncoder(w).Encode(i)
	})

	http.HandleFunc("/voicemail", func(w http.ResponseWriter, r *http.Request) {
		log.Print("Incoming message...")
		err := r.ParseForm()
		check(err)
		from := r.FormValue("from")
		wav := r.FormValue("wav")
		if len(wav) == 0 || len(from) == 0 {
			log.Print("Bad request")
			http.Error(w, "", http.StatusBadRequest)
			return
		}

		log.Print("Incoming message from " + from)
		log.Print("Retrieving audio file " + wav + "...")
		req, err := http.NewRequest("GET", wav, nil)
		req.SetBasicAuth(config.ElksUserName, config.ElksPassword)
		client := &http.Client{}
		resp, err := client.Do(req)
		check(err)
		defer resp.Body.Close()

		log.Print("Downloading...")
		audio, err := ioutil.ReadAll(resp.Body)
		check(err)

		log.Print("Connecting to AWS S3...")
		connection := s3.New(AWSAuth, region)
		bucket := connection.Bucket(config.S3BucketName)

		path := "voicemail/" + time.Now().Format("20060102150405") + ".wav"
		log.Print("Writing " + path + " to " + config.S3BucketName + "...")
		err = bucket.Put(path, audio, "audio/wav", s3.ACL("public-read"))
		check(err)

		log.Print("Posting message to Slack, channel " + config.SlackChannel + "...")
		payload := SlackPayload{
			UserName: config.SlackName,
			IconUrl:  config.SlackIconUrl,
			Text:     "New voice message from " + from + " <" + bucket.URL(path) + ">!",
			Channel:  config.SlackChannel,
		}

		json_payload, _ := json.Marshal(payload)
		slack_req, err := http.NewRequest("POST", config.SlackWebHookUrl, bytes.NewBuffer([]byte(json_payload)))
		req.Header.Set("Content-Type", "application/json")

		slack_client := &http.Client{}
		slack_resp, err := slack_client.Do(slack_req)
		check(err)
		defer slack_resp.Body.Close()

		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.Write(nil)
	})

	port := os.Getenv("PORT")
	if len(port) == 0 {
		port = "3000"
	}

	http.ListenAndServe(":"+port, nil)
}

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

type Configuration struct {
	SlackName       string
	SlackIconUrl    string
	SlackWebHookUrl string
	SlackChannel    string
	Host            string
	VoicemailAudio  string
	ElksUserName    string
	ElksPassword    string
	AWSAccessKey    string
	AWSSecretKey    string
	S3BucketName    string
}

type SlackPayload struct {
	UserName string `json:"username"`
	IconUrl  string `json:"icon_url"`
	Text     string `json:"text"`
	Channel  string `json:"channel"`
}

type IncomingResponse struct {
	Play string `json:"play"`
	Next struct {
		Record string `json:"record"`
	} `json:"next"`
}
<|endoftext|>


<commit_before>package main

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

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

const yuyuteiURL = "http://yuyu-tei.jp/"
const wsDeckURL = "https://wsdecks.com/"
const hoTcURL = "http://www.heartofthecards.com/code/cardlist.html?card=WS_"

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

type siteConfig struct {
	Name   string
	Filter string
}

type cardsConfig struct {
	Dir  string
	Site siteConfig
}

type cardStruc struct {
	ID          string
	Translation string
}

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

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

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

func createCardsCodeFile(dirPath string) (string, error) {
	//TODO Do nothing if file exists
	dirPath += "/"
	out, err := os.Create(dirPath + "codes.txt")
	defer out.Close()
	if err != nil {
		fmt.Println(err)
		return "", err
	}
	cardList, err := filepath.Glob(dirPath + "*.gif")
	if err != nil {
		fmt.Println(err)
		return "", err
	}
	// PI/S40-056
	for _, card := range cardList {
		card = strings.Replace(card, dirPath, "", 1)
		card = strings.Replace(card, "_", "-", 1)
		card = strings.Replace(card, "_", "/", 1)
		ex := strings.Split(card, ".")[0]
		out.WriteString(ex + "\n")
	}
	return out.Name(), nil
}

func getTranslationHotC(codesPath string) []cardStruc {
	translations := []cardStruc{}
	file, err := os.Open(codesPath + "/codes.txt")
	scanner := bufio.NewScanner(file)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("getTranslationHotC")
	for scanner.Scan() {
		// fmt.Println(scanner.Text())
		url := hoTcURL + scanner.Text()
		fmt.Println(url)
		doc, err := goquery.NewDocument(url)
		if err != nil {
			fmt.Println(err)
		}
		textHTML, err := doc.Find(".cards3").Slice(2, 3).Html()
		if err != nil {
			fmt.Println(err)
		}
		textHTML = strings.Replace(textHTML, "
", "
", -1)
		// html.UnescapeString(textHTML)
		card := cardStruc{ID: scanner.Text(), Translation: html.UnescapeString(textHTML)}

		translations = append(translations, card)
		// json.Marshal(card)
		// doc.Find(".card3").Get(2)
	}
	return translations
}

func getCardsConfig(link string) (cardsConfig, error) {
	uid := ""
	site := siteConfig{}

	if strings.Contains(link, yuyuteiURL) {
		site.Name = "yuyutei"
		site.Filter = ".card_list_box" + " .image img"
		parsedURL, _ := url.Parse(link)
		values, _ := url.ParseQuery(parsedURL.RawQuery)
		uid = values.Get("ver")
	} else if strings.Contains(link, wsDeckURL) {
		site.Name = "wsdeck"
		site.Filter = ".wscard" + " img"
		uid = filepath.Base(link)
	}
	dir := filepath.Join("static", site.Name, uid)
	cardsConfig := cardsConfig{Dir: dir, Site: site}
	if site.Filter == "" {
		return cardsConfig, fmt.Errorf("Url is not supported %v", link)
	}

	return cardsConfig, nil
}

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

	http.HandleFunc("/translationimages", func(w http.ResponseWriter, r *http.Request) {
		link := r.PostFormValue("url")
		cardsConfig, err := getCardsConfig(link)
		if err != nil {
			fmt.Println(err)
		}
		result := getTranslationHotC(cardsConfig.Dir)
		b, err := json.Marshal(result)
		if err != nil {
			fmt.Println(err)
		}
		w.Write(b)
	})

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

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

		b, err := json.Marshal(listJpg)

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

		w.Write(b)
	})

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

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

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

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

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

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

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

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

			}
			wg.Wait()
			fmt.Printf("Finish")
			createCardsCodeFile(cardsConfig.Dir)
		}

		b, err := json.Marshal(result)

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

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

	http.ListenAndServe(":8010", nil)
}
<commit_msg>Build a json cardId:yytUrl<commit_after>package main

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

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

const yuyuteiURL = "http://yuyu-tei.jp/"
const wsDeckURL = "https://wsdecks.com/"
const hoTcURL = "http://www.heartofthecards.com/code/cardlist.html?card=WS_"
const yuyuteiBase = "http://yuyu-tei.jp/game_ws"

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

type siteConfig struct {
	Name   string
	Filter string
}

type cardsConfig struct {
	Dir  string
	Site siteConfig
}

type cardStruc struct {
	ID          string
	Translation string
}

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

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

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

func createCardsCodeFile(dirPath string) (string, error) {
	//TODO Do nothing if file exists
	dirPath += "/"
	out, err := os.Create(dirPath + "codes.txt")
	defer out.Close()
	if err != nil {
		fmt.Println(err)
		return "", err
	}
	cardList, err := filepath.Glob(dirPath + "*.gif")
	if err != nil {
		fmt.Println(err)
		return "", err
	}
	// PI/S40-056
	for _, card := range cardList {
		card = strings.Replace(card, dirPath, "", 1)
		card = strings.Replace(card, "_", "-", 1)
		card = strings.Replace(card, "_", "/", 1)
		ex := strings.Split(card, ".")[0]
		out.WriteString(ex + "\n")
	}
	return out.Name(), nil
}

func getTranslationHotC(codesPath string) []cardStruc {
	translations := []cardStruc{}
	file, err := os.Open(codesPath + "/codes.txt")
	scanner := bufio.NewScanner(file)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("getTranslationHotC")
	for scanner.Scan() {
		// fmt.Println(scanner.Text())
		url := hoTcURL + scanner.Text()
		fmt.Println(url)
		doc, err := goquery.NewDocument(url)
		if err != nil {
			fmt.Println(err)
		}
		textHTML, err := doc.Find(".cards3").Slice(2, 3).Html()
		if err != nil {
			fmt.Println(err)
		}
		textHTML = strings.Replace(textHTML, "
", "
", -1)
		// html.UnescapeString(textHTML)
		card := cardStruc{ID: scanner.Text(), Translation: html.UnescapeString(textHTML)}

		translations = append(translations, card)
		// json.Marshal(card)
		// doc.Find(".card3").Get(2)
	}
	return translations
}

func getCardsConfig(link string) (cardsConfig, error) {
	uid := ""
	site := siteConfig{}

	if strings.Contains(link, yuyuteiURL) {
		site.Name = "yuyutei"
		site.Filter = ".card_list_box" + " .image img"
		parsedURL, _ := url.Parse(link)
		values, _ := url.ParseQuery(parsedURL.RawQuery)
		uid = values.Get("ver")
	} else if strings.Contains(link, wsDeckURL) {
		site.Name = "wsdeck"
		site.Filter = ".wscard" + " img"
		uid = filepath.Base(link)
	}
	dir := filepath.Join("static", site.Name, uid)
	cardsConfig := cardsConfig{Dir: dir, Site: site}
	if site.Filter == "" {
		return cardsConfig, fmt.Errorf("Url is not supported %v", link)
	}

	return cardsConfig, nil
}

func yytImages(w http.ResponseWriter, r *http.Request) {
	fmt.Println("yytImage")
	out, err := os.Create(filepath.Join("static", "yyt_image_urls.json"))
	var buffer bytes.Buffer
	defer out.Close()
	cardMap := map[string]string{}
	filter := "ul[data-class=sell] .item_single_card .nav_list_second .nav_list_third a"
	doc, err := goquery.NewDocument(yuyuteiBase)

	if err != nil {
		fmt.Println("Error in get yyt urls")
	}

	doc.Find(filter).Each(func(i int, s *goquery.Selection) {
		url, has := s.Attr("href")
		fmt.Println(url)
		if has {
			images, errCard := goquery.NewDocument(yuyuteiURL + url)
			images.Find(".card_unit").Each(func(cardI int, cardS *goquery.Selection) {
				cardURL, _:= cardS.Find(".image img").Attr("src")
				cardURL = strings.Replace(cardURL, "90_126", "front", 1)
				cardMap[strings.TrimSpace(cardS.Find(".id").Text())] = cardURL
			})
			if errCard != nil {
				fmt.Println(errCard)
			}
		}
	})
	b, errMarshal := json.Marshal(cardMap)
	if errMarshal != nil {
		fmt.Println(errMarshal)
	}
	json.Indent(&buffer, b, "", "\t")
	buffer.WriteTo(out)
	fmt.Println("finish")

}

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

	http.HandleFunc("/translationimages", func(w http.ResponseWriter, r *http.Request) {
		link := r.PostFormValue("url")
		cardsConfig, err := getCardsConfig(link)
		if err != nil {
			fmt.Println(err)
		}
		result := getTranslationHotC(cardsConfig.Dir)
		b, err := json.Marshal(result)
		if err != nil {
			fmt.Println(err)
		}
		w.Write(b)
	})

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

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

		b, err := json.Marshal(listJpg)

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

		w.Write(b)
	})

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

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

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

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

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

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

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

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

			}
			wg.Wait()
			fmt.Printf("Finish")
			createCardsCodeFile(cardsConfig.Dir)
		}

		b, err := json.Marshal(result)

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

	http.HandleFunc("/update_yyt_images", yytImages)

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

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


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"os"
)

func main() {
	app := cli.NewApp()
	app.Name = "dmlivewiki"
	app.Usage = "dmlivewiki helper"
	app.Author = `Qais "qaisjp" Patankar`
	app.Email = "me@qaisjp.com"
	app.Version = "1.0.3"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "force, f",
			Usage: "skip confirmation",
		},
		cli.BoolFlag{
			Name:  "delete",
			Usage: "instead of creating files, delete files",
		},
		cli.BoolFlag{
			Name:  "single, s",
			Usage: "parse the directory given, not the subdirectories",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:   "checksum",
			Usage:  "perform a checksum of directories",
			Action: performChecksum,
		},
		{
			Name:   "generate",
			Usage:  "generate info.txt file for the passed directory",
			Action: generateInformation,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "tour",
					Usage: "required: the tour name for this directory",
				},
				cli.StringFlag{
					Name:  "tour-file",
					Usage: "file with list of tracks with alternate vocals",
				},
			},
		},
	}

	app.Run(os.Args)
}
<commit_msg>Increment version<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"os"
)

func main() {
	app := cli.NewApp()
	app.Name = "dmlivewiki"
	app.Usage = "dmlivewiki helper"
	app.Author = `Qais "qaisjp" Patankar`
	app.Email = "me@qaisjp.com"
	app.Version = "1.0.4"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "force, f",
			Usage: "skip confirmation",
		},
		cli.BoolFlag{
			Name:  "delete",
			Usage: "instead of creating files, delete files",
		},
		cli.BoolFlag{
			Name:  "single, s",
			Usage: "parse the directory given, not the subdirectories",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:   "checksum",
			Usage:  "perform a checksum of directories",
			Action: performChecksum,
		},
		{
			Name:   "generate",
			Usage:  "generate info.txt file for the passed directory",
			Action: generateInformation,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "tour",
					Usage: "required: the tour name for this directory",
				},
				cli.StringFlag{
					Name:  "tour-file",
					Usage: "file with list of tracks with alternate vocals",
				},
			},
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>// Main.go contains settings related to the web server, such as
// template helper functions, HTTP routes and Martini settings.
package main

import (
	"html"
	"html/template"
	"os"
	"strings"
	"time"

	"github.com/go-martini/martini"
	"github.com/martini-contrib/binding"
	"github.com/martini-contrib/render"
	"github.com/martini-contrib/sessions"
	"github.com/martini-contrib/strict"
	"github.com/pkg/browser"
)

// NewServer spaws a new Vertigo server
func NewServer() *martini.ClassicMartini {

	helpers := template.FuncMap{
		// Unescape unescapes and parses HTML from database objects.
		// Used in templates such as "/post/display.tmpl"
		"unescape": func(s string) template.HTML {
			return template.HTML(html.UnescapeString(s))
		},
		// Title renders post name as a page title.
		// Otherwise it defaults to Vertigo.
		"title": func(t interface{}) string {
			post, exists := t.(Post)
			if exists {
				return post.Title
			}
			return Settings.Name
		},
		// Date helper returns unix date as more readable one in string format. Format of YYYY-MM-DD
		// https://html.spec.whatwg.org/multipage/semantics.html#datetime-value
		"date": func(d int64) string {
			return time.Unix(d, 0).Format("2006-01-02")
		},
		// Env helper returns environment variable of s.
		"env": func(s string) string {
			if s == "MAILGUN_SMTP_LOGIN" {
				return strings.TrimLeft(os.Getenv(s), "postmaster@")
			}
			return os.Getenv(s)
		},
		// Markdown returns whether user has Markdown enabled from settings.
		"Markdown": func() bool {
			if Settings.Markdown {
				return true
			}
			return false
		},
		// ReadOnly checks whether a post is safe to edit with current settings.
		"ReadOnly": func(p Post) bool {
			if Settings.Markdown && p.Markdown == "" {
				return true
			}
			return false
		},
	}

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte(Settings.CookieHash))
	m.Use(sessions.Sessions("user", store))
	m.Use(middleware())
	m.Use(sessionchecker())
	m.Use(strict.Strict)
	m.Use(martini.Static("public", martini.StaticOptions{
		SkipLogging: true,
		Expires: func() string {
			return "Cache-Control: max-age=31536000"
		},
	}))
	m.Use(render.Renderer(render.Options{
		Layout: "layout",
		Funcs:  []template.FuncMap{helpers}, // Specify helper function maps for templates to access.
	}))

	m.Get("/", Homepage)

	m.Group("/feeds", func(r martini.Router) {
		r.Get("", func(res render.Render) {
			res.Redirect("/feeds/rss", 302)
		})
		r.Get("/atom", ReadFeed)
		r.Get("/rss", ReadFeed)
	})

	m.Group("/post", func(r martini.Router) {

		// Please note that `/new` route has to be before the `/:slug` route. Otherwise the program will try
		// to fetch for Post named "new".
		// For now I'll keep it this way to streamline route naming.
		r.Get("/new", ProtectedPage, func(res render.Render) {
			res.HTML(200, "post/new", nil)
		})
		r.Get("/:slug", ReadPost)
		r.Get("/:slug/edit", ProtectedPage, EditPost)
		r.Post("/:slug/edit", ProtectedPage, strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Post{}), binding.ErrorHandler, UpdatePost)
		r.Get("/:slug/delete", ProtectedPage, DeletePost)
		r.Get("/:slug/publish", ProtectedPage, PublishPost)
		r.Post("/new", ProtectedPage, strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Post{}), binding.ErrorHandler, CreatePost)
		r.Post("/search", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Search{}), binding.ErrorHandler, SearchPost)

	})

	m.Group("/user", func(r martini.Router) {

		r.Get("", ProtectedPage, ReadUser)
		//r.Post("/delete", strict.ContentType("application/x-www-form-urlencoded"), ProtectedPage, binding.Form(User{}), DeleteUser)

		r.Get("/settings", ProtectedPage, ReadSettings)
		r.Post("/settings", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Vertigo{}), binding.ErrorHandler, ProtectedPage, UpdateSettings)

		r.Post("/installation", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Vertigo{}), binding.ErrorHandler, UpdateSettings)

		r.Get("/register", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/register", nil)
		})
		r.Post("/register", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(User{}), binding.ErrorHandler, CreateUser)

		r.Get("/recover", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/recover", nil)
		})
		r.Post("/recover", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(User{}), RecoverUser)
		r.Get("/reset/:id/:recovery", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/reset", nil)
		})
		r.Post("/reset/:id/:recovery", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(User{}), ResetUserPassword)

		r.Get("/login", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/login", nil)
		})
		r.Post("/login", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(User{}), LoginUser)
		r.Get("/logout", LogoutUser)

	})

	m.Group("/api", func(r martini.Router) {

		r.Get("", func(res render.Render) {
			res.HTML(200, "api/index", nil)
		})
		r.Get("/settings", ProtectedPage, ReadSettings)
		r.Post("/settings", strict.ContentType("application/json"), binding.Json(Vertigo{}), binding.ErrorHandler, ProtectedPage, UpdateSettings)
		r.Post("/installation", strict.ContentType("application/json"), binding.Json(Vertigo{}), binding.ErrorHandler, UpdateSettings)
		r.Get("/users", ReadUsers)
		r.Get("/user/logout", LogoutUser)
		r.Get("/user/:id", ReadUser)
		//r.Delete("/user", DeleteUser)
		r.Post("/user", strict.ContentType("application/json"), binding.Json(User{}), binding.ErrorHandler, CreateUser)
		r.Post("/user/login", strict.ContentType("application/json"), binding.Json(User{}), binding.ErrorHandler, LoginUser)
		r.Post("/user/recover", strict.ContentType("application/json"), binding.Json(User{}), RecoverUser)
		r.Post("/user/reset/:id/:recovery", strict.ContentType("application/json"), binding.Json(User{}), ResetUserPassword)

		r.Get("/posts", ReadPosts)
		r.Get("/post/:slug", ReadPost)
		r.Post("/post", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, ProtectedPage, CreatePost)
		r.Get("/post/:slug/publish", ProtectedPage, PublishPost)
		r.Post("/post/:slug/edit", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, ProtectedPage, UpdatePost)
		r.Get("/post/:slug/delete", ProtectedPage, DeletePost)
		r.Post("/post", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, ProtectedPage, CreatePost)
		r.Post("/post/search", strict.ContentType("application/json"), binding.Json(Search{}), binding.ErrorHandler, SearchPost)

	})

	m.Router.NotFound(strict.MethodNotAllowed, strict.NotFound)
	return m
}

func main() {
	server := NewServer()
	if os.Getenv("PORT") != "" {
		browser.OpenURL("http://localhost:" + os.Getenv("PORT"))
	} else {
		browser.OpenURL("http://localhost:3000")
	}
	server.Run()
}
<commit_msg>Update main.go<commit_after>// Main.go contains settings related to the web server, such as
// template helper functions, HTTP routes and Martini settings.
package main

import (
	"html"
	"html/template"
	"os"
	"strings"
	"time"

	"github.com/go-martini/martini"
	"github.com/martini-contrib/binding"
	"github.com/martini-contrib/render"
	"github.com/martini-contrib/sessions"
	"github.com/martini-contrib/strict"
	"github.com/pkg/browser"
)

// NewServer spaws a new Vertigo server
func NewServer() *martini.ClassicMartini {

	helpers := template.FuncMap{
		// Unescape unescapes and parses HTML from database objects.
		// Used in templates such as "/post/display.tmpl"
		"unescape": func(s string) template.HTML {
			return template.HTML(html.UnescapeString(s))
		},
		// Title renders post name as a page title.
		"title": func(t interface{}) string {
			post, exists := t.(Post)
			if exists {
				return post.Title
			}
			return Settings.Name
		},
		// Page Title renders page title.
		"pagetitle": func(t interface{}) string {
			if Settings.Name == "" {
				return "Vertigo"
			}
			return Settings.Name
		},
		// Description renders page description.
		"description": func(t interface{}) string {
			if Settings.Description == "" {
				return "Blog in Go"
			}
			return Settings.Description
		},
		// Hostname renders page hostname.
		"hostname": func(t interface{}) string {
			return urlHost()
		},
		// Date helper returns unix date as more readable one in string format. Format of YYYY-MM-DD
		// https://html.spec.whatwg.org/multipage/semantics.html#datetime-value
		"date": func(d int64) string {
			return time.Unix(d, 0).Format("2006-01-02")
		},
		// Env helper returns environment variable of s.
		"env": func(s string) string {
			if s == "MAILGUN_SMTP_LOGIN" {
				return strings.TrimLeft(os.Getenv(s), "postmaster@")
			}
			return os.Getenv(s)
		},
		// Markdown returns whether user has Markdown enabled from settings.
		"Markdown": func() bool {
			if Settings.Markdown {
				return true
			}
			return false
		},
		// ReadOnly checks whether a post is safe to edit with current settings.
		"ReadOnly": func(p Post) bool {
			if Settings.Markdown && p.Markdown == "" {
				return true
			}
			return false
		},
	}

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte(Settings.CookieHash))
	m.Use(sessions.Sessions("user", store))
	m.Use(middleware())
	m.Use(sessionchecker())
	m.Use(strict.Strict)
	m.Use(martini.Static("public", martini.StaticOptions{
		SkipLogging: true,
		Expires: func() string {
			return "Cache-Control: max-age=31536000"
		},
	}))
	m.Use(render.Renderer(render.Options{
		Layout: "layout",
		Funcs:  []template.FuncMap{helpers}, // Specify helper function maps for templates to access.
	}))

	m.Get("/", Homepage)

	m.Group("/feeds", func(r martini.Router) {
		r.Get("", func(res render.Render) {
			res.Redirect("/feeds/rss", 302)
		})
		r.Get("/atom", ReadFeed)
		r.Get("/rss", ReadFeed)
	})

	m.Group("/post", func(r martini.Router) {

		// Please note that `/new` route has to be before the `/:slug` route. Otherwise the program will try
		// to fetch for Post named "new".
		// For now I'll keep it this way to streamline route naming.
		r.Get("/new", ProtectedPage, func(res render.Render) {
			res.HTML(200, "post/new", nil)
		})
		r.Get("/:slug", ReadPost)
		r.Get("/:slug/edit", ProtectedPage, EditPost)
		r.Post("/:slug/edit", ProtectedPage, strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Post{}), binding.ErrorHandler, UpdatePost)
		r.Get("/:slug/delete", ProtectedPage, DeletePost)
		r.Get("/:slug/publish", ProtectedPage, PublishPost)
		r.Post("/new", ProtectedPage, strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Post{}), binding.ErrorHandler, CreatePost)
		r.Post("/search", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Search{}), binding.ErrorHandler, SearchPost)

	})

	m.Group("/user", func(r martini.Router) {

		r.Get("", ProtectedPage, ReadUser)
		//r.Post("/delete", strict.ContentType("application/x-www-form-urlencoded"), ProtectedPage, binding.Form(User{}), DeleteUser)

		r.Get("/settings", ProtectedPage, ReadSettings)
		r.Post("/settings", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Vertigo{}), binding.ErrorHandler, ProtectedPage, UpdateSettings)

		r.Post("/installation", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(Vertigo{}), binding.ErrorHandler, UpdateSettings)

		r.Get("/register", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/register", nil)
		})
		r.Post("/register", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(User{}), binding.ErrorHandler, CreateUser)

		r.Get("/recover", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/recover", nil)
		})
		r.Post("/recover", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(User{}), RecoverUser)
		r.Get("/reset/:id/:recovery", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/reset", nil)
		})
		r.Post("/reset/:id/:recovery", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(User{}), ResetUserPassword)

		r.Get("/login", SessionRedirect, func(res render.Render) {
			res.HTML(200, "user/login", nil)
		})
		r.Post("/login", strict.ContentType("application/x-www-form-urlencoded"), binding.Form(User{}), LoginUser)
		r.Get("/logout", LogoutUser)

	})

	m.Group("/api", func(r martini.Router) {

		r.Get("", func(res render.Render) {
			res.HTML(200, "api/index", nil)
		})
		r.Get("/settings", ProtectedPage, ReadSettings)
		r.Post("/settings", strict.ContentType("application/json"), binding.Json(Vertigo{}), binding.ErrorHandler, ProtectedPage, UpdateSettings)
		r.Post("/installation", strict.ContentType("application/json"), binding.Json(Vertigo{}), binding.ErrorHandler, UpdateSettings)
		r.Get("/users", ReadUsers)
		r.Get("/user/logout", LogoutUser)
		r.Get("/user/:id", ReadUser)
		//r.Delete("/user", DeleteUser)
		r.Post("/user", strict.ContentType("application/json"), binding.Json(User{}), binding.ErrorHandler, CreateUser)
		r.Post("/user/login", strict.ContentType("application/json"), binding.Json(User{}), binding.ErrorHandler, LoginUser)
		r.Post("/user/recover", strict.ContentType("application/json"), binding.Json(User{}), RecoverUser)
		r.Post("/user/reset/:id/:recovery", strict.ContentType("application/json"), binding.Json(User{}), ResetUserPassword)

		r.Get("/posts", ReadPosts)
		r.Get("/post/:slug", ReadPost)
		r.Post("/post", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, ProtectedPage, CreatePost)
		r.Get("/post/:slug/publish", ProtectedPage, PublishPost)
		r.Post("/post/:slug/edit", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, ProtectedPage, UpdatePost)
		r.Get("/post/:slug/delete", ProtectedPage, DeletePost)
		r.Post("/post", strict.ContentType("application/json"), binding.Json(Post{}), binding.ErrorHandler, ProtectedPage, CreatePost)
		r.Post("/post/search", strict.ContentType("application/json"), binding.Json(Search{}), binding.ErrorHandler, SearchPost)

	})

	m.Router.NotFound(strict.MethodNotAllowed, strict.NotFound)
	return m
}

func main() {
	server := NewServer()
	if os.Getenv("PORT") != "" {
		browser.OpenURL("http://localhost:" + os.Getenv("PORT"))
	} else {
		browser.OpenURL("http://localhost:3000")
	}
	server.Run()
}
<|endoftext|>


<commit_before>package main

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

	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/etcd/pkg"
	flagtypes "github.com/coreos/etcd/pkg/flags"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/proxy"
	"github.com/coreos/etcd/raft"
)

const (
	// the owner can make/remove files inside the directory
	privateDirMode = 0700

	version = "0.5.0-alpha"
)

var (
	name         = flag.String("name", "default", "Unique human-readable name for this node")
	dir          = flag.String("data-dir", "", "Path to the data directory")
	durl         = flag.String("discovery", "", "Discovery service used to bootstrap the cluster")
	snapCount    = flag.Uint64("snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot")
	printVersion = flag.Bool("version", false, "Print the version and exit")

	cluster      = &etcdserver.Cluster{}
	clusterState = new(etcdserver.ClusterState)

	cors      = &pkg.CORSInfo{}
	proxyFlag = new(flagtypes.Proxy)

	clientTLSInfo = transport.TLSInfo{}
	peerTLSInfo   = transport.TLSInfo{}

	ignored = []string{
		"cluster-active-size",
		"cluster-remove-delay",
		"cluster-sync-interval",
		"config",
		"force",
		"max-result-buffer",
		"max-retry-attempts",
		"peer-heartbeat-interval",
		"peer-election-timeout",
		"retry-interval",
		"snapshot",
		"v",
		"vv",
	}
)

func init() {
	flag.Var(cluster, "initial-cluster", "Initial cluster configuration for bootstrapping")
	flag.Var(clusterState, "initial-cluster-state", "Initial cluster configuration for bootstrapping")
	if err := cluster.Set("default=http://localhost:2380,default=http://localhost:7001"); err != nil {
		// Should never happen
		log.Panic(err)
	}

	flag.Var(flagtypes.NewURLsValue("http://localhost:2380,http://localhost:7001"), "advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster")
	flag.Var(flagtypes.NewURLsValue("http://localhost:2379,http://localhost:4001"), "advertise-client-urls", "List of this member's client URLs to advertise to the rest of the cluster")
	flag.Var(flagtypes.NewURLsValue("http://localhost:2380,http://localhost:7001"), "listen-peer-urls", "List of this URLs to listen on for peer traffic")
	flag.Var(flagtypes.NewURLsValue("http://localhost:2379,http://localhost:4001"), "listen-client-urls", "List of this URLs to listen on for client traffic")

	flag.Var(cors, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")

	flag.Var(proxyFlag, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(flagtypes.ProxyValues, ", ")))
	proxyFlag.Set(flagtypes.ProxyValueOff)

	flag.StringVar(&clientTLSInfo.CAFile, "ca-file", "", "Path to the client server TLS CA file.")
	flag.StringVar(&clientTLSInfo.CertFile, "cert-file", "", "Path to the client server TLS cert file.")
	flag.StringVar(&clientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.")

	flag.StringVar(&peerTLSInfo.CAFile, "peer-ca-file", "", "Path to the peer server TLS CA file.")
	flag.StringVar(&peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
	flag.StringVar(&peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")

	// backwards-compatibility with v0.4.6
	flag.Var(&flagtypes.IPAddressPort{}, "addr", "DEPRECATED: Use -advertise-client-urls instead.")
	flag.Var(&flagtypes.IPAddressPort{}, "bind-addr", "DEPRECATED: Use -listen-client-urls instead.")
	flag.Var(&flagtypes.IPAddressPort{}, "peer-addr", "DEPRECATED: Use -advertise-peer-urls instead.")
	flag.Var(&flagtypes.IPAddressPort{}, "peer-bind-addr", "DEPRECATED: Use -listen-peer-urls instead.")

	for _, f := range ignored {
		flag.Var(&pkg.IgnoredFlag{f}, f, "")
	}

	flag.Var(&pkg.DeprecatedFlag{"peers"}, "peers", "DEPRECATED: Use -bootstrap-config instead")
	flag.Var(&pkg.DeprecatedFlag{"peers-file"}, "peers-file", "DEPRECATED: Use -bootstrap-config instead")
}

func main() {
	flag.Usage = pkg.UsageWithIgnoredFlagsFunc(flag.CommandLine, ignored)
	flag.Parse()

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

	pkg.SetFlagsFromEnv(flag.CommandLine)
	if err := setClusterForDiscovery(); err != nil {
		log.Fatalf("etcd: %v", err)
	}

	if string(*proxyFlag) == flagtypes.ProxyValueOff {
		startEtcd()
	} else {
		startProxy()
	}

	// Block indefinitely
	<-make(chan struct{})
}

// startEtcd launches the etcd server and HTTP handlers for client/server communication.
func startEtcd() {
	self := cluster.FindName(*name)
	if self == nil {
		log.Fatalf("etcd: no member with name=%q exists", *name)
	}

	if self.ID == raft.None {
		log.Fatalf("etcd: cannot use None(%d) as member id", raft.None)
	}

	if *dir == "" {
		*dir = fmt.Sprintf("%v_etcd_data", self.ID)
		log.Printf("main: no data-dir provided, using default data-dir ./%s", *dir)
	}
	if err := os.MkdirAll(*dir, privateDirMode); err != nil {
		log.Fatalf("main: cannot create data directory: %v", err)
	}

	pt, err := transport.NewTransport(peerTLSInfo)
	if err != nil {
		log.Fatal(err)
	}

	acurls, err := pkg.URLsFromFlags(flag.CommandLine, "advertise-client-urls", "addr", clientTLSInfo)
	if err != nil {
		log.Fatal(err.Error())
	}
	cfg := &etcdserver.ServerConfig{
		Name:         *name,
		ClientURLs:   acurls,
		DataDir:      *dir,
		SnapCount:    int64(*snapCount),
		Cluster:      cluster,
		DiscoveryURL: *durl,
		ClusterState: *clusterState,
		Transport:    pt,
	}
	s := etcdserver.NewServer(cfg)
	s.Start()

	ch := &pkg.CORSHandler{
		Handler: etcdhttp.NewClientHandler(s),
		Info:    cors,
	}
	ph := etcdhttp.NewPeerHandler(s)

	lpurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-peer-urls", "peer-bind-addr", peerTLSInfo)
	if err != nil {
		log.Fatal(err.Error())
	}

	for _, u := range lpurls {
		l, err := transport.NewListener(u.Host, peerTLSInfo)
		if err != nil {
			log.Fatal(err)
		}

		// Start the peer server in a goroutine
		urlStr := u.String()
		go func() {
			log.Print("Listening for peers on ", urlStr)
			log.Fatal(http.Serve(l, ph))
		}()
	}

	lcurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-client-urls", "bind-addr", clientTLSInfo)
	if err != nil {
		log.Fatal(err.Error())
	}

	// Start a client server goroutine for each listen address
	for _, u := range lcurls {
		l, err := transport.NewListener(u.Host, clientTLSInfo)
		if err != nil {
			log.Fatal(err)
		}

		urlStr := u.String()
		go func() {
			log.Print("Listening for client requests on ", urlStr)
			log.Fatal(http.Serve(l, ch))
		}()
	}
}

// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
func startProxy() {
	pt, err := transport.NewTransport(clientTLSInfo)
	if err != nil {
		log.Fatal(err)
	}

	ph, err := proxy.NewHandler(pt, (*cluster).PeerURLs())
	if err != nil {
		log.Fatal(err)
	}

	ph = &pkg.CORSHandler{
		Handler: ph,
		Info:    cors,
	}

	if string(*proxyFlag) == flagtypes.ProxyValueReadonly {
		ph = proxy.NewReadonlyHandler(ph)
	}

	lcurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-client-urls", "bind-addr", clientTLSInfo)
	if err != nil {
		log.Fatal(err.Error())
	}
	// Start a proxy server goroutine for each listen address
	for _, u := range lcurls {
		l, err := transport.NewListener(u.Host, clientTLSInfo)
		if err != nil {
			log.Fatal(err)
		}

		host := u.Host
		go func() {
			log.Print("Listening for client requests on ", host)
			log.Fatal(http.Serve(l, ph))
		}()
	}
}

// setClusterForDiscovery sets cluster to a temporary value if you are using
// the discovery.
func setClusterForDiscovery() error {
	set := make(map[string]bool)
	flag.Visit(func(f *flag.Flag) {
		set[f.Name] = true
	})
	if set["discovery"] && set["bootstrap-config"] {
		return fmt.Errorf("both discovery and bootstrap-config are set")
	}
	if set["discovery"] {
		apurls, err := pkg.URLsFromFlags(flag.CommandLine, "advertise-peer-urls", "addr", peerTLSInfo)
		if err != nil {
			return err
		}
		addrs := make([]string, len(apurls))
		for i := range apurls {
			addrs[i] = fmt.Sprintf("%s=%s", *name, apurls[i].String())
		}
		if err := cluster.Set(strings.Join(addrs, ",")); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>main: use a new cluster by default<commit_after>package main

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

	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/etcd/pkg"
	flagtypes "github.com/coreos/etcd/pkg/flags"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/proxy"
	"github.com/coreos/etcd/raft"
)

const (
	// the owner can make/remove files inside the directory
	privateDirMode = 0700

	version = "0.5.0-alpha"
)

var (
	name         = flag.String("name", "default", "Unique human-readable name for this node")
	dir          = flag.String("data-dir", "", "Path to the data directory")
	durl         = flag.String("discovery", "", "Discovery service used to bootstrap the cluster")
	snapCount    = flag.Uint64("snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot")
	printVersion = flag.Bool("version", false, "Print the version and exit")

	cluster      = &etcdserver.Cluster{}
	clusterState = new(etcdserver.ClusterState)

	cors      = &pkg.CORSInfo{}
	proxyFlag = new(flagtypes.Proxy)

	clientTLSInfo = transport.TLSInfo{}
	peerTLSInfo   = transport.TLSInfo{}

	ignored = []string{
		"cluster-active-size",
		"cluster-remove-delay",
		"cluster-sync-interval",
		"config",
		"force",
		"max-result-buffer",
		"max-retry-attempts",
		"peer-heartbeat-interval",
		"peer-election-timeout",
		"retry-interval",
		"snapshot",
		"v",
		"vv",
	}
)

func init() {
	flag.Var(cluster, "initial-cluster", "Initial cluster configuration for bootstrapping")
	if err := cluster.Set("default=http://localhost:2380,default=http://localhost:7001"); err != nil {
		// Should never happen
		log.Panic(err)
	}
	flag.Var(clusterState, "initial-cluster-state", "Initial cluster configuration for bootstrapping")
	clusterState.Set(etcdserver.ClusterStateValueNew)

	flag.Var(flagtypes.NewURLsValue("http://localhost:2380,http://localhost:7001"), "advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster")
	flag.Var(flagtypes.NewURLsValue("http://localhost:2379,http://localhost:4001"), "advertise-client-urls", "List of this member's client URLs to advertise to the rest of the cluster")
	flag.Var(flagtypes.NewURLsValue("http://localhost:2380,http://localhost:7001"), "listen-peer-urls", "List of this URLs to listen on for peer traffic")
	flag.Var(flagtypes.NewURLsValue("http://localhost:2379,http://localhost:4001"), "listen-client-urls", "List of this URLs to listen on for client traffic")

	flag.Var(cors, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")

	flag.Var(proxyFlag, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(flagtypes.ProxyValues, ", ")))
	proxyFlag.Set(flagtypes.ProxyValueOff)

	flag.StringVar(&clientTLSInfo.CAFile, "ca-file", "", "Path to the client server TLS CA file.")
	flag.StringVar(&clientTLSInfo.CertFile, "cert-file", "", "Path to the client server TLS cert file.")
	flag.StringVar(&clientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.")

	flag.StringVar(&peerTLSInfo.CAFile, "peer-ca-file", "", "Path to the peer server TLS CA file.")
	flag.StringVar(&peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
	flag.StringVar(&peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")

	// backwards-compatibility with v0.4.6
	flag.Var(&flagtypes.IPAddressPort{}, "addr", "DEPRECATED: Use -advertise-client-urls instead.")
	flag.Var(&flagtypes.IPAddressPort{}, "bind-addr", "DEPRECATED: Use -listen-client-urls instead.")
	flag.Var(&flagtypes.IPAddressPort{}, "peer-addr", "DEPRECATED: Use -advertise-peer-urls instead.")
	flag.Var(&flagtypes.IPAddressPort{}, "peer-bind-addr", "DEPRECATED: Use -listen-peer-urls instead.")

	for _, f := range ignored {
		flag.Var(&pkg.IgnoredFlag{f}, f, "")
	}

	flag.Var(&pkg.DeprecatedFlag{"peers"}, "peers", "DEPRECATED: Use -bootstrap-config instead")
	flag.Var(&pkg.DeprecatedFlag{"peers-file"}, "peers-file", "DEPRECATED: Use -bootstrap-config instead")
}

func main() {
	flag.Usage = pkg.UsageWithIgnoredFlagsFunc(flag.CommandLine, ignored)
	flag.Parse()

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

	pkg.SetFlagsFromEnv(flag.CommandLine)
	if err := setClusterForDiscovery(); err != nil {
		log.Fatalf("etcd: %v", err)
	}

	if string(*proxyFlag) == flagtypes.ProxyValueOff {
		startEtcd()
	} else {
		startProxy()
	}

	// Block indefinitely
	<-make(chan struct{})
}

// startEtcd launches the etcd server and HTTP handlers for client/server communication.
func startEtcd() {
	self := cluster.FindName(*name)
	if self == nil {
		log.Fatalf("etcd: no member with name=%q exists", *name)
	}

	if self.ID == raft.None {
		log.Fatalf("etcd: cannot use None(%d) as member id", raft.None)
	}

	if *dir == "" {
		*dir = fmt.Sprintf("%v_etcd_data", self.ID)
		log.Printf("main: no data-dir provided, using default data-dir ./%s", *dir)
	}
	if err := os.MkdirAll(*dir, privateDirMode); err != nil {
		log.Fatalf("main: cannot create data directory: %v", err)
	}

	pt, err := transport.NewTransport(peerTLSInfo)
	if err != nil {
		log.Fatal(err)
	}

	acurls, err := pkg.URLsFromFlags(flag.CommandLine, "advertise-client-urls", "addr", clientTLSInfo)
	if err != nil {
		log.Fatal(err.Error())
	}
	cfg := &etcdserver.ServerConfig{
		Name:         *name,
		ClientURLs:   acurls,
		DataDir:      *dir,
		SnapCount:    int64(*snapCount),
		Cluster:      cluster,
		DiscoveryURL: *durl,
		ClusterState: *clusterState,
		Transport:    pt,
	}
	s := etcdserver.NewServer(cfg)
	s.Start()

	ch := &pkg.CORSHandler{
		Handler: etcdhttp.NewClientHandler(s),
		Info:    cors,
	}
	ph := etcdhttp.NewPeerHandler(s)

	lpurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-peer-urls", "peer-bind-addr", peerTLSInfo)
	if err != nil {
		log.Fatal(err.Error())
	}

	for _, u := range lpurls {
		l, err := transport.NewListener(u.Host, peerTLSInfo)
		if err != nil {
			log.Fatal(err)
		}

		// Start the peer server in a goroutine
		urlStr := u.String()
		go func() {
			log.Print("Listening for peers on ", urlStr)
			log.Fatal(http.Serve(l, ph))
		}()
	}

	lcurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-client-urls", "bind-addr", clientTLSInfo)
	if err != nil {
		log.Fatal(err.Error())
	}

	// Start a client server goroutine for each listen address
	for _, u := range lcurls {
		l, err := transport.NewListener(u.Host, clientTLSInfo)
		if err != nil {
			log.Fatal(err)
		}

		urlStr := u.String()
		go func() {
			log.Print("Listening for client requests on ", urlStr)
			log.Fatal(http.Serve(l, ch))
		}()
	}
}

// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
func startProxy() {
	pt, err := transport.NewTransport(clientTLSInfo)
	if err != nil {
		log.Fatal(err)
	}

	ph, err := proxy.NewHandler(pt, (*cluster).PeerURLs())
	if err != nil {
		log.Fatal(err)
	}

	ph = &pkg.CORSHandler{
		Handler: ph,
		Info:    cors,
	}

	if string(*proxyFlag) == flagtypes.ProxyValueReadonly {
		ph = proxy.NewReadonlyHandler(ph)
	}

	lcurls, err := pkg.URLsFromFlags(flag.CommandLine, "listen-client-urls", "bind-addr", clientTLSInfo)
	if err != nil {
		log.Fatal(err.Error())
	}
	// Start a proxy server goroutine for each listen address
	for _, u := range lcurls {
		l, err := transport.NewListener(u.Host, clientTLSInfo)
		if err != nil {
			log.Fatal(err)
		}

		host := u.Host
		go func() {
			log.Print("Listening for client requests on ", host)
			log.Fatal(http.Serve(l, ph))
		}()
	}
}

// setClusterForDiscovery sets cluster to a temporary value if you are using
// the discovery.
func setClusterForDiscovery() error {
	set := make(map[string]bool)
	flag.Visit(func(f *flag.Flag) {
		set[f.Name] = true
	})
	if set["discovery"] && set["bootstrap-config"] {
		return fmt.Errorf("both discovery and bootstrap-config are set")
	}
	if set["discovery"] {
		apurls, err := pkg.URLsFromFlags(flag.CommandLine, "advertise-peer-urls", "addr", peerTLSInfo)
		if err != nil {
			return err
		}
		addrs := make([]string, len(apurls))
		for i := range apurls {
			addrs[i] = fmt.Sprintf("%s=%s", *name, apurls[i].String())
		}
		if err := cluster.Set(strings.Join(addrs, ",")); err != nil {
			return err
		}
	}
	return nil
}
<|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-dead-line",
				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-dead-line:", c.Int("read-dead-line"))
			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"))
			// init services
			startup(c)

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

			//setup net param
			readDeadline=c.Duration("read-dead-line")
			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")

			// 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 init<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 (
	// 网络拥塞控制和削峰
	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-dead-line",
				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-dead-line:", c.Int("read-dead-line"))
			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-dead-line")
			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)
	}
}
<|endoftext|>