<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/evolsnow/robot/conn"
	"golang.org/x/net/websocket"
	"log"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"
	"unicode"
)

var messages = make(chan string)

func main() {
	var configFile string
	var debug bool
	flag.StringVar(&configFile, "c", "config.json", "specify config file")
	flag.BoolVar(&debug, "d", false, "debug mode")

	flag.Parse()
	config, err := ParseConfig(configFile)
	if err != nil {
		log.Fatal("a vailid json config file must exist")
	}

	redisPort := strconv.Itoa(config.RedisPort)
	redisServer := net.JoinHostPort(config.RedisAddress, redisPort)
	if !conn.Ping(redisServer, config.RedisPassword) {
		log.Fatal("connect to redis server failed")
	}
	conn.Pool = conn.NewPool(redisServer, config.RedisPassword, config.RedisDB)
	robot := newRobot(config.RobotToken, config.RobotName, config.WebHookUrl)
	robot.bot.Debug = debug
	go robot.run()
	srvPort := strconv.Itoa(config.Port)
	http.HandleFunc("/ajax", ajax)
	http.Handle("/websocket", websocket.Handler(socketHandler))
	//	log.Fatal(http.ListenAndServe(net.JoinHostPort(config.Server, srvPort), nil))
	log.Fatal(http.ListenAndServeTLS(net.JoinHostPort(config.Server, srvPort), config.Cert, config.CertKey, nil))

}

//used for web samaritan robot
func socketHandler(ws *websocket.Conn) {
	for {
		var in string
		var ret []string
		if err := websocket.Message.Receive(ws, &in); err != nil {
			log.Println(err)
			return
		}
		ret = receive(in)

		for i := range ret {
			websocket.Message.Send(ws, ret[i])
			time.Sleep(time.Second)
		}
		websocket.Message.Send(ws, "")
	}
}
func ajax(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")

	if r.Method == "GET" {
		log.Printf("new conn")
		for {
			w.Write([]byte(<-messages))
		}

	} else {
		body := r.FormValue("text")
		if body != "" {
			go func(string) {
				ret := receive(body)
				for i := range ret {
					messages <- ret[i]
					time.Sleep(time.Second)
				}
				messages <- ""
			}(body)
		}
	}
}

func receive(in string) (ret []string) {
	fmt.Printf("Received: %s\n", in)
	var response string
	sf := func(c rune) bool {
		return c == ',' || c == '，' || c == ';' || c == '。' || c == '.' || c == '？' || c == '?'
	}
	zh := false
	for _, r := range in {
		if unicode.Is(unicode.Scripts["Han"], r) {
			log.Printf(in)
			zh = true
			break
		}
	}
	if zh {
		response = tlAI(in)
		// Separate into fields with func.
		ret = strings.FieldsFunc(response, sf)

	} else {
		response = mitAI(in)
		ret = strings.FieldsFunc(response, sf)
	}
	return
}
<commit_msg>format<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/evolsnow/robot/conn"
	"golang.org/x/net/websocket"
	"log"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"
	"unicode"
)

var messages = make(chan string)

func main() {
	var configFile string
	var debug bool
	flag.StringVar(&configFile, "c", "config.json", "specify config file")
	flag.BoolVar(&debug, "d", false, "debug mode")

	flag.Parse()
	config, err := ParseConfig(configFile)
	if err != nil {
		log.Fatal("a vailid json config file must exist")
	}

	redisPort := strconv.Itoa(config.RedisPort)
	redisServer := net.JoinHostPort(config.RedisAddress, redisPort)
	if !conn.Ping(redisServer, config.RedisPassword) {
		log.Fatal("connect to redis server failed")
	}
	conn.Pool = conn.NewPool(redisServer, config.RedisPassword, config.RedisDB)
	robot := newRobot(config.RobotToken, config.RobotName, config.WebHookUrl)
	robot.bot.Debug = debug
	go robot.run()
	srvPort := strconv.Itoa(config.Port)
	http.HandleFunc("/ajax", ajax)
	http.Handle("/websocket", websocket.Handler(socketHandler))
	//	log.Fatal(http.ListenAndServe(net.JoinHostPort(config.Server, srvPort), nil))
	log.Fatal(http.ListenAndServeTLS(net.JoinHostPort(config.Server, srvPort), config.Cert, config.CertKey, nil))

}

//used for web samaritan robot
func socketHandler(ws *websocket.Conn) {
	for {
		var in string
		var ret []string
		if err := websocket.Message.Receive(ws, &in); err != nil {
			log.Println(err)
			return
		}
		ret = receive(in)

		for i := range ret {
			websocket.Message.Send(ws, ret[i])
			time.Sleep(time.Second)
		}
		websocket.Message.Send(ws, "")
	}
}
func ajax(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")

	if r.Method == "GET" {
		log.Printf("new conn")
		w.Write([]byte(<-messages))

	} else {
		body := r.FormValue("text")
		if body != "" {
			go func(string) {
				ret := receive(body)
				for i := range ret {
					messages <- ret[i]
					time.Sleep(time.Second)
				}
				messages <- ""
			}(body)
		}
	}
}

func receive(in string) (ret []string) {
	fmt.Printf("Received: %s\n", in)
	var response string
	sf := func(c rune) bool {
		return c == ',' || c == '，' || c == ';' || c == '。' || c == '.' || c == '？' || c == '?'
	}
	zh := false
	for _, r := range in {
		if unicode.Is(unicode.Scripts["Han"], r) {
			log.Printf(in)
			zh = true
			break
		}
	}
	if zh {
		response = tlAI(in)
		// Separate into fields with func.
		ret = strings.FieldsFunc(response, sf)

	} else {
		response = mitAI(in)
		ret = strings.FieldsFunc(response, sf)
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strings"
)

const usageMessage = `
'ghkeys' uses the GitHub API to get the SSH keys of individual users and/or
members of teams and either print them or write them to authorized_keys files.

Pass a single 'username' argument to only print/write keys for that user.

Usage:
  ghkeys [-config="/path/to/ghkeys.yml"] [-write] [username]
`

var (
	// VERSION swapped out by `go build -ldflags "-X main.VERSION 1.2.3"`
	VERSION        = "0.0.3"
	configFilename = flag.String("config", "ghkeys.yml", "Path to yaml config file")
	debug          = flag.Bool("d", false, "Add debugging output")
	showVersion    = flag.Bool("version", false, "Display the version number")
	writeToFile    = flag.Bool("write", false, "Write keys to users' authorized_keys files")
)

func printVersion() {
	fmt.Printf("ghkeys version %s\n", VERSION)
}

func usage() {
	printVersion()
	fmt.Println(usageMessage + "\nFlags:")
	flag.PrintDefaults()
}

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

type usernameKeys struct {
	username string
	keys     []string
}

func getUsernamesKeys(config config, client *githubClient, singleUsername string) map[string][]string {
	usernamesKeys := make(map[string][]string)
	usernameCount := 0
	usernameKeysChan := make(chan usernameKeys)
	for _, user := range config.Users {
		if singleUsername == "" || singleUsername == user.Username {
			usernameCount++
			go func(username string, users, teams []string) {
				keys := client.getKeysOfUsersAndTeams(users, teams)
				usernameKeysChan <- usernameKeys{username: username, keys: keys}
			}(user.Username, user.GithubUsers, user.GithubTeams)
		}
	}
	for i := 0; i < usernameCount; i++ {
		tempUsernameKeys := <-usernameKeysChan
		usernamesKeys[tempUsernameKeys.username] = tempUsernameKeys.keys
	}
	return usernamesKeys
}

func getAuthorizedKeysFilename(userHomeDir string) string {
	authorizedKeysFilename := filepath.Join(userHomeDir, ".ssh", "authorized_keys")
	return authorizedKeysFilename
}

func getKeysOutput(keys []string) string {
	keysOutput := strings.Join(keys, "\n")
	return keysOutput
}

func writeKeysToUserAuthorizedKeysFile(keys []string, userHomeDir string) error {
	authorizedKeysFilename := getAuthorizedKeysFilename(userHomeDir)
	authorizedKeysFile, err := os.Create(authorizedKeysFilename)
	if err != nil {
		return err
	}
	defer authorizedKeysFile.Close()
	keysOutput := getKeysOutput(keys) + "\n"
	_, err = authorizedKeysFile.WriteString(keysOutput)
	return err
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if *showVersion {
		printVersion()
		os.Exit(0)
	}

	config, err := newConfig(*configFilename)
	check(err)
	// TODO: validate config values

	client := newGithubClient(config.GithubToken)

	if *debug {
		if rate, _, err := client.RateLimit(); err == nil {
			log.Printf("GitHub API Rate Limit: %#v\n", rate)
		}
	}

	usernamesKeys := getUsernamesKeys(config, client, flag.Arg(0))

	for username, keys := range usernamesKeys {
		if *writeToFile {
			if user, err := user.Lookup(username); err == nil {
				err = writeKeysToUserAuthorizedKeysFile(keys, user.HomeDir)
				check(err)
			} else {
				log.Printf("User '%s' not found, no keys written.\n", username)
			}
		} else {
			fmt.Println(getKeysOutput(keys))
		}
	}
}
<commit_msg>Bump version to 0.0.4<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strings"
)

const usageMessage = `
'ghkeys' uses the GitHub API to get the SSH keys of individual users and/or
members of teams and either print them or write them to authorized_keys files.

Pass a single 'username' argument to only print/write keys for that user.

Usage:
  ghkeys [-config="/path/to/ghkeys.yml"] [-write] [username]
`

var (
	// VERSION swapped out by `go build -ldflags "-X main.VERSION 1.2.3"`
	VERSION        = "0.0.4"
	configFilename = flag.String("config", "ghkeys.yml", "Path to yaml config file")
	debug          = flag.Bool("d", false, "Add debugging output")
	showVersion    = flag.Bool("version", false, "Display the version number")
	writeToFile    = flag.Bool("write", false, "Write keys to users' authorized_keys files")
)

func printVersion() {
	fmt.Printf("ghkeys version %s\n", VERSION)
}

func usage() {
	printVersion()
	fmt.Println(usageMessage + "\nFlags:")
	flag.PrintDefaults()
}

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

type usernameKeys struct {
	username string
	keys     []string
}

func getUsernamesKeys(config config, client *githubClient, singleUsername string) map[string][]string {
	usernamesKeys := make(map[string][]string)
	usernameCount := 0
	usernameKeysChan := make(chan usernameKeys)
	for _, user := range config.Users {
		if singleUsername == "" || singleUsername == user.Username {
			usernameCount++
			go func(username string, users, teams []string) {
				keys := client.getKeysOfUsersAndTeams(users, teams)
				usernameKeysChan <- usernameKeys{username: username, keys: keys}
			}(user.Username, user.GithubUsers, user.GithubTeams)
		}
	}
	for i := 0; i < usernameCount; i++ {
		tempUsernameKeys := <-usernameKeysChan
		usernamesKeys[tempUsernameKeys.username] = tempUsernameKeys.keys
	}
	return usernamesKeys
}

func getAuthorizedKeysFilename(userHomeDir string) string {
	authorizedKeysFilename := filepath.Join(userHomeDir, ".ssh", "authorized_keys")
	return authorizedKeysFilename
}

func getKeysOutput(keys []string) string {
	keysOutput := strings.Join(keys, "\n")
	return keysOutput
}

func writeKeysToUserAuthorizedKeysFile(keys []string, userHomeDir string) error {
	authorizedKeysFilename := getAuthorizedKeysFilename(userHomeDir)
	authorizedKeysFile, err := os.Create(authorizedKeysFilename)
	if err != nil {
		return err
	}
	defer authorizedKeysFile.Close()
	keysOutput := getKeysOutput(keys) + "\n"
	_, err = authorizedKeysFile.WriteString(keysOutput)
	return err
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if *showVersion {
		printVersion()
		os.Exit(0)
	}

	config, err := newConfig(*configFilename)
	check(err)
	// TODO: validate config values

	client := newGithubClient(config.GithubToken)

	if *debug {
		if rate, _, err := client.RateLimit(); err == nil {
			log.Printf("GitHub API Rate Limit: %#v\n", rate)
		}
	}

	usernamesKeys := getUsernamesKeys(config, client, flag.Arg(0))

	for username, keys := range usernamesKeys {
		if *writeToFile {
			if user, err := user.Lookup(username); err == nil {
				err = writeKeysToUserAuthorizedKeysFile(keys, user.HomeDir)
				check(err)
			} else {
				log.Printf("User '%s' not found, no keys written.\n", username)
			}
		} else {
			fmt.Println(getKeysOutput(keys))
		}
	}
}
<|endoftext|>


<commit_before>package main

// TODO Fix installpath
// TODO ParseDownloadREsults is a mess, nedds simplifying

import (
	"github.com/yannh/r10k-go/git"
	"log"
	"os"
	"path"
	"strconv"
	"sync"
	"time"
)

// ForgeModule, GitModule, GithubTarballModule, ....
type PuppetModule interface {
	CacheableModule
	Name() string
	Download(string, *Cache) DownloadError
	Folder() string
	SetModulesFolder(to string)
	ModulesFolder() string
	Hash() string
	Processed()
}

type CacheableModule interface {
	IsUpToDate() bool
}

// Can be a PuppetFile or a metadata.json file
type moduleFile interface {
	Filename() string
	Process(modules chan<- PuppetModule) error
	Close()
}

type DownloadError struct {
	error
	retryable bool
}

type DownloadResult struct {
	err       DownloadError
	skipped   bool
	retryable bool
}

func downloadModule(m PuppetModule, cache *Cache) DownloadResult {
	if m.IsUpToDate() {
		return DownloadResult{err: DownloadError{nil, false}, skipped: true, retryable: false}
	}

	if err := os.RemoveAll(m.Folder()); err != nil {
		log.Fatalf("Error removing folder: %s", m.Folder())
	}

	if derr := m.Download(m.Folder(), cache); derr.error != nil {
		return DownloadResult{err: derr, skipped: false, retryable: true}
	}

	return DownloadResult{err: DownloadError{nil, false}, skipped: false, retryable: false}
}

func downloadModules(modules chan PuppetModule, cache *Cache, downloadDeps bool, wg *sync.WaitGroup, errorsCount chan<- int) {
	maxTries := 3
	retryDelay := 5 * time.Second
	errors := 0

	for m := range modules {
		cache.LockModule(m.Hash())

		dres := downloadModule(m, cache)
		for i := 1; dres.err.error != nil && dres.err.retryable && i < maxTries; i++ {
			log.Printf("failed downloading %s: %v... Retrying\n", m.Name(), dres.err)
			time.Sleep(retryDelay)
			dres = downloadModule(m, cache)
		}

		if dres.err.error == nil {
			if downloadDeps {
				if mf := NewMetadataFile(path.Join(m.Folder(), "metadata.json"), m.ModulesFolder()); mf != nil {
					wg.Add(1)
					go func() {
						processModuleFile(mf, modules)
						mf.Close()
						wg.Done()
					}()
				}
			}

			if !dres.skipped {
				log.Println("Downloaded " + m.Name() + " to " + m.ModulesFolder())
			}
		} else {
			log.Printf("failed downloading %s: %v. Giving up!\n", m.Name(), dres.err)
			errors++
		}

		m.Processed()
		cache.UnlockModule(m.Hash())
	}

	errorsCount <- errors
}

func processModuleFile(mf moduleFile, modules chan PuppetModule) {
	if err := mf.Process(modules); err != nil {
		if serr, ok := err.(ErrMalformedPuppetfile); ok {
			log.Fatal(serr)
		} else {
			log.Printf("failed parsing %s: %v\n", mf.Filename(), err)
		}
	}
}

func main() {
	var err error
	var numWorkers int
	var cache *Cache

	cliOpts := cli()

	if cliOpts["--workers"] == nil {
		numWorkers = 4
	} else {
		numWorkers, err = strconv.Atoi(cliOpts["--workers"].(string))
		if err != nil {
			log.Fatalf("Parameter --workers should be an integer")
		}
	}

	cacheDir := ".cache"
	var puppetFiles []*PuppetFile

	if cache, err = NewCache(cacheDir); err != nil {
		log.Fatal(err)
	}

	if cliOpts["deploy"] == true {
		r10kFile := "r10k.yml"
		r10kConfig, err := NewR10kConfig(r10kFile)
		if err != nil {
			log.Fatalf("Error parsing r10k configuration file %s: %v", r10kFile, err)
		}

		if r10kConfig.Cachedir != "" {
			cacheDir = r10kConfig.Cachedir
		}

		// Find in which source the environment is
		// TODO: render deterministic
		for _, envName := range cliOpts["<env>"].([]string) {
			environmentSource := ""

			for sourceName, sourceOpts := range r10kConfig.Sources {
				if git.RepoHasBranch(sourceOpts.Remote, envName) {
					environmentSource = sourceName
					break
				}
			}

			sourceCacheFolder := path.Join(cacheDir, environmentSource)

			// Clone if environment doesnt exist, fetch otherwise
			if err := git.RevParse(sourceCacheFolder); err != nil {
				if err := git.Clone(r10kConfig.Sources[environmentSource].Remote, git.Ref{Branch: envName}, sourceCacheFolder); err != nil {
					log.Fatalf("failed downloading environment: %v", err)
				}
			} else {
				git.Fetch(sourceCacheFolder)
			}

			git.WorktreeAdd(sourceCacheFolder, git.Ref{Branch: envName}, path.Join(r10kConfig.Sources[environmentSource].Basedir, envName))
			puppetfile := path.Join(r10kConfig.Sources[environmentSource].Basedir, envName, "Puppetfile")

			pf := NewPuppetFile(puppetfile, path.Join(path.Dir(puppetfile), "modules"))
			if pf == nil {
				log.Fatalf("no such file or directory %s", puppetfile)
			}
			puppetFiles = append(puppetFiles, pf)
		}
	}

	if cliOpts["install"] == true {
		puppetfile := ""
		if cliOpts["--puppetfile"] == nil {
			wd, _ := os.Getwd()
			puppetfile = path.Join(wd, "Puppetfile")
		} else {
			puppetfile = cliOpts["--puppetfile"].(string)
		}

		pf := NewPuppetFile(puppetfile, path.Join(path.Dir(puppetfile), "modules"))
		if pf == nil {
			log.Fatalf("no such file or directory %s", puppetfile)
		}

		puppetFiles = append(puppetFiles, pf)
	}

	if cliOpts["install"] == true || cliOpts["deploy"] == true {
		modules := make(chan PuppetModule)

		var wg sync.WaitGroup
		errorCount := make(chan int)

		for w := 1; w <= numWorkers; w++ {
			go downloadModules(modules, cache, !cliOpts["--no-deps"].(bool), &wg, errorCount)
		}

		for _, pf := range puppetFiles {
			wg.Add(1)
			go func(pf moduleFile, modules chan PuppetModule) {
				processModuleFile(pf, modules)
				pf.Close()
				wg.Done()
			}(pf, modules)
		}

		wg.Wait()
		close(modules)

		nErr := 0
		for w := 1; w <= numWorkers; w++ {
			nErr += <-errorCount
		}

		close(errorCount)

		os.Exit(nErr)
	}
}
<commit_msg>retryable should be on error, not on download result<commit_after>package main

// TODO Fix installpath
// TODO ParseDownloadREsults is a mess, nedds simplifying

import (
	"github.com/yannh/r10k-go/git"
	"log"
	"os"
	"path"
	"strconv"
	"sync"
	"time"
)

// ForgeModule, GitModule, GithubTarballModule, ....
type PuppetModule interface {
	IsUpToDate() bool
	Name() string
	Download(string, *Cache) DownloadError
	Folder() string
	SetModulesFolder(to string)
	ModulesFolder() string
	Hash() string
	Processed()
}

// Can be a PuppetFile or a metadata.json file
type moduleFile interface {
	Filename() string
	Process(modules chan<- PuppetModule) error
	Close()
}

type DownloadError struct {
	error
	retryable bool
}

type DownloadResult struct {
	err     DownloadError
	skipped bool
}

func downloadModule(m PuppetModule, cache *Cache) DownloadResult {
	if m.IsUpToDate() {
		return DownloadResult{err: DownloadError{nil, false}, skipped: true}
	}

	if err := os.RemoveAll(m.Folder()); err != nil {
		log.Fatalf("Error removing folder: %s", m.Folder())
	}

	if derr := m.Download(m.Folder(), cache); derr.error != nil {
		return DownloadResult{err: derr, skipped: false}
	}

	return DownloadResult{err: DownloadError{nil, false}, skipped: false}
}

func downloadModules(modules chan PuppetModule, cache *Cache, downloadDeps bool, wg *sync.WaitGroup, errorsCount chan<- int) {
	maxTries := 3
	retryDelay := 5 * time.Second
	errors := 0

	for m := range modules {
		cache.LockModule(m.Hash())

		dres := downloadModule(m, cache)
		for i := 1; dres.err.error != nil && dres.err.retryable && i < maxTries; i++ {
			log.Printf("failed downloading %s: %v... Retrying\n", m.Name(), dres.err)
			time.Sleep(retryDelay)
			dres = downloadModule(m, cache)
		}

		if dres.err.error == nil {
			if downloadDeps {
				if mf := NewMetadataFile(path.Join(m.Folder(), "metadata.json"), m.ModulesFolder()); mf != nil {
					wg.Add(1)
					go func() {
						processModuleFile(mf, modules)
						mf.Close()
						wg.Done()
					}()
				}
			}

			if !dres.skipped {
				log.Println("Downloaded " + m.Name() + " to " + m.ModulesFolder())
			}
		} else {
			log.Printf("failed downloading %s: %v. Giving up!\n", m.Name(), dres.err)
			errors++
		}

		m.Processed()
		cache.UnlockModule(m.Hash())
	}

	errorsCount <- errors
}

func processModuleFile(mf moduleFile, modules chan PuppetModule) {
	if err := mf.Process(modules); err != nil {
		if serr, ok := err.(ErrMalformedPuppetfile); ok {
			log.Fatal(serr)
		} else {
			log.Printf("failed parsing %s: %v\n", mf.Filename(), err)
		}
	}
}

func main() {
	var err error
	var numWorkers int
	var cache *Cache

	cliOpts := cli()

	if cliOpts["--workers"] == nil {
		numWorkers = 4
	} else {
		numWorkers, err = strconv.Atoi(cliOpts["--workers"].(string))
		if err != nil {
			log.Fatalf("Parameter --workers should be an integer")
		}
	}

	cacheDir := ".cache"
	var puppetFiles []*PuppetFile

	if cache, err = NewCache(cacheDir); err != nil {
		log.Fatal(err)
	}

	if cliOpts["deploy"] == true {
		r10kFile := "r10k.yml"
		r10kConfig, err := NewR10kConfig(r10kFile)
		if err != nil {
			log.Fatalf("Error parsing r10k configuration file %s: %v", r10kFile, err)
		}

		if r10kConfig.Cachedir != "" {
			cacheDir = r10kConfig.Cachedir
		}

		// Find in which source the environment is
		// TODO: render deterministic
		for _, envName := range cliOpts["<env>"].([]string) {
			environmentSource := ""

			for sourceName, sourceOpts := range r10kConfig.Sources {
				if git.RepoHasBranch(sourceOpts.Remote, envName) {
					environmentSource = sourceName
					break
				}
			}

			sourceCacheFolder := path.Join(cacheDir, environmentSource)

			// Clone if environment doesnt exist, fetch otherwise
			if err := git.RevParse(sourceCacheFolder); err != nil {
				if err := git.Clone(r10kConfig.Sources[environmentSource].Remote, git.Ref{Branch: envName}, sourceCacheFolder); err != nil {
					log.Fatalf("failed downloading environment: %v", err)
				}
			} else {
				git.Fetch(sourceCacheFolder)
			}

			git.WorktreeAdd(sourceCacheFolder, git.Ref{Branch: envName}, path.Join(r10kConfig.Sources[environmentSource].Basedir, envName))
			puppetfile := path.Join(r10kConfig.Sources[environmentSource].Basedir, envName, "Puppetfile")

			pf := NewPuppetFile(puppetfile, path.Join(path.Dir(puppetfile), "modules"))
			if pf == nil {
				log.Fatalf("no such file or directory %s", puppetfile)
			}
			puppetFiles = append(puppetFiles, pf)
		}
	}

	if cliOpts["install"] == true {
		puppetfile := ""
		if cliOpts["--puppetfile"] == nil {
			wd, _ := os.Getwd()
			puppetfile = path.Join(wd, "Puppetfile")
		} else {
			puppetfile = cliOpts["--puppetfile"].(string)
		}

		pf := NewPuppetFile(puppetfile, path.Join(path.Dir(puppetfile), "modules"))
		if pf == nil {
			log.Fatalf("no such file or directory %s", puppetfile)
		}

		puppetFiles = append(puppetFiles, pf)
	}

	if cliOpts["install"] == true || cliOpts["deploy"] == true {
		modules := make(chan PuppetModule)

		var wg sync.WaitGroup
		errorCount := make(chan int)

		for w := 1; w <= numWorkers; w++ {
			go downloadModules(modules, cache, !cliOpts["--no-deps"].(bool), &wg, errorCount)
		}

		for _, pf := range puppetFiles {
			wg.Add(1)
			go func(pf moduleFile, modules chan PuppetModule) {
				processModuleFile(pf, modules)
				pf.Close()
				wg.Done()
			}(pf, modules)
		}

		wg.Wait()
		close(modules)

		nErr := 0
		for w := 1; w <= numWorkers; w++ {
			nErr += <-errorCount
		}

		close(errorCount)

		os.Exit(nErr)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"math/rand"
	"net"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	version   = "0.5.0"
	github    = "https://github.com/ekalinin/awsping"
	useragent = fmt.Sprintf("AwsPing/%s (+%s)", version, github)
)

var (
	repeats = flag.Int("repeats", 1, "Number of repeats")
	useHTTP = flag.Bool("http", false, "Use http transport (default is tcp)")
	showVer = flag.Bool("v", false, "Show version")
	verbose = flag.Int("verbose", 0, "Verbosity level")
	service = flag.String("service", "dynamodb", "AWS Service: ec2, sdb, sns, sqs, ...")
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

// Duration2ms converts time.Duration to ms (float64)
func Duration2ms(d time.Duration) float64 {
	if d.Nanoseconds() == nil {
		return 0.0
	}
	return float64(d.Nanoseconds()) / 1000 / 1000
}

func mkRandoString(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

// AWSRegion description of the AWS EC2 region
type AWSRegion struct {
	Name      string
	Code      string
	Service   string
	Latencies []time.Duration
	Error     error
}

// CheckLatencyHTTP Test Latency via HTTP
func (r *AWSRegion) CheckLatencyHTTP(wg *sync.WaitGroup) {
	defer wg.Done()
	url := fmt.Sprintf("http://%s.%s.amazonaws.com/ping?x=%s", r.Service,
		r.Code, mkRandoString(13))
	client := &http.Client{}

	req, err := http.NewRequest("GET", url, nil)
	req.Header.Set("User-Agent", useragent)

	start := time.Now()
	resp, err := client.Do(req)
	r.Latencies = append(r.Latencies, time.Since(start))
	defer resp.Body.Close()

	r.Error = err
}

// CheckLatencyTCP Test Latency via TCP
func (r *AWSRegion) CheckLatencyTCP(wg *sync.WaitGroup) {
	defer wg.Done()
	tcpURI := fmt.Sprintf("%s.%s.amazonaws.com:80", r.Service, r.Code)
	tcpAddr, err := net.ResolveTCPAddr("tcp4", tcpURI)
	if err != nil {
		r.Error = err
		return
	}
	start := time.Now()
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	r.Latencies = append(r.Latencies, time.Since(start))
	defer conn.Close()

	r.Error = err
}

// GetLatency returns Latency in ms
func (r *AWSRegion) GetLatency() float64 {
	sum := float64(0)
	for _, l := range r.Latencies {
		sum += Duration2ms(l)
	}
	return sum / float64(len(r.Latencies))
}

// AWSRegions slice of the AWSRegion
type AWSRegions []AWSRegion

func (rs AWSRegions) Len() int {
	return len(rs)
}

func (rs AWSRegions) Less(i, j int) bool {
	return rs[i].GetLatency() < rs[j].GetLatency()
}

func (rs AWSRegions) Swap(i, j int) {
	rs[i], rs[j] = rs[j], rs[i]
}

// CalcLatency returns list of aws regions sorted by Latency
func CalcLatency(repeats int, useHTTP bool, service string) *AWSRegions {
	regions := AWSRegions{
		{Service: service, Name: "US-East (Virginia)", Code: "us-east-1"},
		{Service: service, Name: "US-East (Ohio)", Code: "us-east-2"},
		{Service: service, Name: "US-West (California)", Code: "us-west-1"},
		{Service: service, Name: "US-West (Oregon)", Code: "us-west-2"},
		{Service: service, Name: "Asia Pacific (Mumbai)", Code: "ap-south-1"},
		{Service: service, Name: "Asia Pacific (Seoul)", Code: "ap-northeast-2"},
		{Service: service, Name: "Asia Pacific (Singapore)", Code: "ap-southeast-1"},
		{Service: service, Name: "Asia Pacific (Sydney)", Code: "ap-southeast-2"},
		{Service: service, Name: "Asia Pacific (Tokyo)", Code: "ap-northeast-1"},
		{Service: service, Name: "Europe (Ireland)", Code: "eu-west-1"},
		{Service: service, Name: "Europe (Frankfurt)", Code: "eu-central-1"},
		{Service: service, Name: "South America (São Paulo)", Code: "sa-east-1"},
		//{Name: "China (Beijing)", Code: "cn-north-1"},
	}
	var wg sync.WaitGroup

	for n := 1; n <= repeats; n++ {
		wg.Add(len(regions))
		for i := range regions {
			if useHTTP {
				go regions[i].CheckLatencyHTTP(&wg)
			} else {
				go regions[i].CheckLatencyTCP(&wg)
			}
		}
		wg.Wait()
	}

	sort.Sort(regions)
	return &regions
}

// LatencyOutput prints data into console
type LatencyOutput struct {
	Level int
}

func (lo *LatencyOutput) show0(regions *AWSRegions) {
	for _, r := range *regions {
		fmt.Printf("%-25s %20s\n", r.Name,
			fmt.Sprintf("%.2f ms", r.GetLatency()))
	}
}

func (lo *LatencyOutput) show1(regions *AWSRegions) {
	outFmt := "%5v %-15s %-30s %20s\n"
	fmt.Printf(outFmt, "", "Code", "Region", "Latency")
	for i, r := range *regions {
		ms := fmt.Sprintf("%.2f ms", r.GetLatency())
		fmt.Printf(outFmt, i, r.Code, r.Name, ms)
	}
}

func (lo *LatencyOutput) show2(regions *AWSRegions) {
	// format
	outFmt := "%5v %-15s %-25s"
	outFmt += strings.Repeat(" %15s", *repeats) + " %15s\n"
	// header
	outStr := []interface{}{"", "Code", "Region"}
	for i := 0; i < *repeats; i++ {
		outStr = append(outStr, "Try #"+strconv.Itoa(i+1))
	}
	outStr = append(outStr, "Avg Latency")

	// show header
	fmt.Printf(outFmt, outStr...)

	// each region stats
	for i, r := range *regions {
		outData := []interface{}{strconv.Itoa(i), r.Code, r.Name}
		for n := 0; n < *repeats; n++ {
			outData = append(outData, fmt.Sprintf("%.2f ms",
				Duration2ms(r.Latencies[n])))
		}
		outData = append(outData, fmt.Sprintf("%.2f ms", r.GetLatency()))
		fmt.Printf(outFmt, outData...)
	}
}

// Show print data
func (lo *LatencyOutput) Show(regions *AWSRegions) {
	switch lo.Level {
	case 0:
		lo.show0(regions)
	case 1:
		lo.show1(regions)
	case 2:
		lo.show2(regions)
	}
}

func main() {

	flag.Parse()

	if *showVer {
		fmt.Println(version)
		os.Exit(0)
	}

	regions := CalcLatency(*repeats, *useHTTP, *service)
	lo := LatencyOutput{*verbose}
	lo.Show(regions)
}

func init() {
	rand.Seed(time.Now().UnixNano())
}
<commit_msg>fixed typo<commit_after>package main

import (
	"flag"
	"fmt"
	"math/rand"
	"net"
	"net/http"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	version   = "0.5.0"
	github    = "https://github.com/ekalinin/awsping"
	useragent = fmt.Sprintf("AwsPing/%s (+%s)", version, github)
)

var (
	repeats = flag.Int("repeats", 1, "Number of repeats")
	useHTTP = flag.Bool("http", false, "Use http transport (default is tcp)")
	showVer = flag.Bool("v", false, "Show version")
	verbose = flag.Int("verbose", 0, "Verbosity level")
	service = flag.String("service", "dynamodb", "AWS Service: ec2, sdb, sns, sqs, ...")
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

// Duration2ms converts time.Duration to ms (float64)
func Duration2ms(d time.Duration) float64 {
	return float64(d.Nanoseconds()) / 1000 / 1000
}

func mkRandoString(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

// AWSRegion description of the AWS EC2 region
type AWSRegion struct {
	Name      string
	Code      string
	Service   string
	Latencies []time.Duration
	Error     error
}

// CheckLatencyHTTP Test Latency via HTTP
func (r *AWSRegion) CheckLatencyHTTP(wg *sync.WaitGroup) {
	defer wg.Done()
	url := fmt.Sprintf("http://%s.%s.amazonaws.com/ping?x=%s", r.Service,
		r.Code, mkRandoString(13))
	client := &http.Client{}

	req, err := http.NewRequest("GET", url, nil)
	req.Header.Set("User-Agent", useragent)

	start := time.Now()
	resp, err := client.Do(req)
	r.Latencies = append(r.Latencies, time.Since(start))
	defer resp.Body.Close()

	r.Error = err
}

// CheckLatencyTCP Test Latency via TCP
func (r *AWSRegion) CheckLatencyTCP(wg *sync.WaitGroup) {
	defer wg.Done()
	tcpURI := fmt.Sprintf("%s.%s.amazonaws.com:80", r.Service, r.Code)
	tcpAddr, err := net.ResolveTCPAddr("tcp4", tcpURI)
	if err != nil {
		r.Error = err
		return
	}
	start := time.Now()
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	r.Latencies = append(r.Latencies, time.Since(start))
	defer conn.Close()

	r.Error = err
}

// GetLatency returns Latency in ms
func (r *AWSRegion) GetLatency() float64 {
	sum := float64(0)
	for _, l := range r.Latencies {
		sum += Duration2ms(l)
	}
	return sum / float64(len(r.Latencies))
}

// AWSRegions slice of the AWSRegion
type AWSRegions []AWSRegion

func (rs AWSRegions) Len() int {
	return len(rs)
}

func (rs AWSRegions) Less(i, j int) bool {
	return rs[i].GetLatency() < rs[j].GetLatency()
}

func (rs AWSRegions) Swap(i, j int) {
	rs[i], rs[j] = rs[j], rs[i]
}

// CalcLatency returns list of aws regions sorted by Latency
func CalcLatency(repeats int, useHTTP bool, service string) *AWSRegions {
	regions := AWSRegions{
		{Service: service, Name: "US-East (Virginia)", Code: "us-east-1"},
		{Service: service, Name: "US-East (Ohio)", Code: "us-east-2"},
		{Service: service, Name: "US-West (California)", Code: "us-west-1"},
		{Service: service, Name: "US-West (Oregon)", Code: "us-west-2"},
		{Service: service, Name: "Asia Pacific (Mumbai)", Code: "ap-south-1"},
		{Service: service, Name: "Asia Pacific (Seoul)", Code: "ap-northeast-2"},
		{Service: service, Name: "Asia Pacific (Singapore)", Code: "ap-southeast-1"},
		{Service: service, Name: "Asia Pacific (Sydney)", Code: "ap-southeast-2"},
		{Service: service, Name: "Asia Pacific (Tokyo)", Code: "ap-northeast-1"},
		{Service: service, Name: "Europe (Ireland)", Code: "eu-west-1"},
		{Service: service, Name: "Europe (Frankfurt)", Code: "eu-central-1"},
		{Service: service, Name: "South America (São Paulo)", Code: "sa-east-1"},
		//{Name: "China (Beijing)", Code: "cn-north-1"},
	}
	var wg sync.WaitGroup

	for n := 1; n <= repeats; n++ {
		wg.Add(len(regions))
		for i := range regions {
			if useHTTP {
				go regions[i].CheckLatencyHTTP(&wg)
			} else {
				go regions[i].CheckLatencyTCP(&wg)
			}
		}
		wg.Wait()
	}

	sort.Sort(regions)
	return &regions
}

// LatencyOutput prints data into console
type LatencyOutput struct {
	Level int
}

func (lo *LatencyOutput) show0(regions *AWSRegions) {
	for _, r := range *regions {
		fmt.Printf("%-25s %20s\n", r.Name,
			fmt.Sprintf("%.2f ms", r.GetLatency()))
	}
}

func (lo *LatencyOutput) show1(regions *AWSRegions) {
	outFmt := "%5v %-15s %-30s %20s\n"
	fmt.Printf(outFmt, "", "Code", "Region", "Latency")
	for i, r := range *regions {
		ms := fmt.Sprintf("%.2f ms", r.GetLatency())
		fmt.Printf(outFmt, i, r.Code, r.Name, ms)
	}
}

func (lo *LatencyOutput) show2(regions *AWSRegions) {
	// format
	outFmt := "%5v %-15s %-25s"
	outFmt += strings.Repeat(" %15s", *repeats) + " %15s\n"
	// header
	outStr := []interface{}{"", "Code", "Region"}
	for i := 0; i < *repeats; i++ {
		outStr = append(outStr, "Try #"+strconv.Itoa(i+1))
	}
	outStr = append(outStr, "Avg Latency")

	// show header
	fmt.Printf(outFmt, outStr...)

	// each region stats
	for i, r := range *regions {
		outData := []interface{}{strconv.Itoa(i), r.Code, r.Name}
		for n := 0; n < *repeats; n++ {
			outData = append(outData, fmt.Sprintf("%.2f ms",
				Duration2ms(r.Latencies[n])))
		}
		outData = append(outData, fmt.Sprintf("%.2f ms", r.GetLatency()))
		fmt.Printf(outFmt, outData...)
	}
}

// Show print data
func (lo *LatencyOutput) Show(regions *AWSRegions) {
	switch lo.Level {
	case 0:
		lo.show0(regions)
	case 1:
		lo.show1(regions)
	case 2:
		lo.show2(regions)
	}
}

func main() {

	flag.Parse()

	if *showVer {
		fmt.Println(version)
		os.Exit(0)
	}

	regions := CalcLatency(*repeats, *useHTTP, *service)
	lo := LatencyOutput{*verbose}
	lo.Show(regions)
}

func init() {
	rand.Seed(time.Now().UnixNano())
}
<|endoftext|>


<commit_before>package main

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

const (
	usernameAndPasswordRegexString = `^[a-zA-Z]\w*[a-zA-Z]$` // 1st and last characters must be letters.
	emailRegexString               = `^.*\@.*$`              // As long as it has an '@' symbol in it I don't care.

	db             = "repo-reviews" // Mongodb database name
	privateKeyPath = "app.rsa"      // Command: openssl genrsa -out app.rsa 1024
	publicKeyPath  = "app.rsa.pub"  // Command: openssl rsa -in app.rsa -pubout > app.rsa.pub
)

var (
	session            *mgo.Session
	verifyKey, signKey []byte
	signingMethod      jwt.SigningMethod

	usernameAndPasswordRegex *regexp.Regexp // Compiled regex for quicker matching.
	emailRegex               *regexp.Regexp // Compiled regex for quicker matching.
)

// User is someone who has registered on the site.
type User struct {
	ID           bson.ObjectId `bson:"_id" json:"_id"`
	Username     string        `bson:"username" json:"username"`
	PasswordHash string        `bson:"password_hash" json:"password_hash"`
	Email        string        `bson:"email" json:"email"`
	PasswordSalt string
}

// A Review is created by a User to express their feelings of a particular Repository
type Review struct {
	ID         bson.ObjectId `bson:"_id" json:"_id"`
	From       bson.ObjectId `bson:"from" json:"from"`
	Repository bson.ObjectId `bson:"repository" json:"repository"`
	Content    string        `bson:"content" json:"content"`
	Rating     int           `bson:"rating" json:"rating"`
}

// Repository is the representation of a git project on Rr
type Repository struct {
	ID   bson.ObjectId `bson:"_id" json:"_id"`
	Host string        `bson:"host" json:"host"`
	User string        `bson:"user" json:"user"`
	Name string        `bson:"name" json:"name"`
}

// Token is a container used to send a User their access_token
type Token struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

func init() {
	var err error

	signKey, err = ioutil.ReadFile(privateKeyPath)

	if err != nil {
		fmt.Println("Could not find your private key!")
		panic(err)
	}

	verifyKey, err = ioutil.ReadFile(publicKeyPath)

	if err != nil {
		fmt.Println("Could not find your public key!")
		panic(err)
	}

	signingMethod = jwt.GetSigningMethod("RS256")

	usernameAndPasswordRegex, err = regexp.Compile(usernameAndPasswordRegexString)

	if err != nil {
		panic(err)
	}

	emailRegex, err = regexp.Compile(emailRegexString)

	if err != nil {
		panic(err)
	}

}

func main() {
	var err error

	session, err = mgo.Dial("localhost")
	if err != nil {
		panic(err)
	}

	defer session.Close()

	r := mux.NewRouter()
	api := r.PathPrefix("/api").Subrouter()

	api.HandleFunc("/user/create", Headers(NewUserHandler)).Methods("POST")

	api.HandleFunc("/user/login", Headers(LoginUserHandler)).Methods("POST")

	api.HandleFunc("/user", Headers(Restrict(GetCurrentUserHandler))).Methods("GET")

	api.HandleFunc("/user/{username}", Headers(GetUserHandler)).Methods("GET")

	api.HandleFunc("/repo/{host}/{user}/{name}/review", Headers(Restrict(NewReviewHandler))).Methods("POST")

	api.HandleFunc("/repo/{host}/{user}/{name}/{review}", Headers(GetReviewHandler)).Methods("GET")

	api.HandleFunc("/repo/{host}/{user}/{name}", Headers(GetRepository)).Methods("GET")

	api.HandleFunc("/repo/{host}/{user}/{name}", Headers(Restrict(NewRepository))).Methods("POST")

	r.HandleFunc("/secret", Headers(Restrict(GetSecretHandler))).Methods("GET")

	// Serve ALL the static files!
	r.PathPrefix("/").Handler(http.FileServer(http.Dir("static/")))

	http.Handle("/", r)

	fmt.Println("Loading http server on :8080...")

	fmt.Println(http.ListenAndServe(":8080", nil))

}

// GetSecretHandler is a test handler to check if access_tokens work.
// 		GET /secret
func GetSecretHandler(w http.ResponseWriter, r *http.Request, t *jwt.Token) {
	fmt.Fprintln(w, "NCSS IS ILLUMINATTI")
}

// NewUserHandler creates a new user.
// 		POST /api/user/create?username=paked&pasword=pw
func NewUserHandler(w http.ResponseWriter, r *http.Request) {
	username, email, password := r.FormValue("username"), r.FormValue("email"), r.FormValue("password")
	uRe, eRe, pRe := usernameAndPasswordRegex.FindString(username), emailRegex.FindString(email), usernameAndPasswordRegex.FindString(username)

	if uRe == "" || eRe == "" || pRe == "" {
		fmt.Fprintln(w, "Username, password or email is not valid")
		return
	}

	c := session.DB(db).C("users")
	var u User

	if c.Find(bson.M{"username": username}).One(&u); u != (User{}) {
		fmt.Fprint(w, "That user already exists!")
		return
	}

	u = User{ID: bson.NewObjectId(), Username: username, Email: email, PasswordHash: password}

	if err := c.Insert(u); err != nil {
		fmt.Fprintln(w, "Unable to create that user at this time.")
		return
	}

	fmt.Fprintf(w, "%v", u)
}

// GetUserHandler retrieves a User from the database
// 		GET /api/user/{username}
func GetUserHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	c := session.DB(db).C("users")
	var u User

	if c.Find(bson.M{"username": vars["username"]}).One(&u); u == (User{}) {
		fmt.Fprintln(w, "that user doesnt exist")
		return
	}

	fmt.Fprintln(w, "We found that user!", u)
}

// LoginUserHandler checks the provided login credentials and if valid return an access_token.
//		POST /api/user/login?username=paked&password=pw
func LoginUserHandler(w http.ResponseWriter, r *http.Request) {
	username, password := r.FormValue("username"), r.FormValue("password")

	if username == "" || password == "" {
		fmt.Fprintln(w, "your username and password don't have anything in them")
		return
	}

	c := session.DB(db).C("users")

	var u User

	if c.Find(bson.M{"username": username, "password_hash": password}).One(&u); u == (User{}) {
		fmt.Fprintln(w, "That user doesnt exist")
		return
	}

	t := jwt.New(signingMethod)

	t.Claims["AccessToken"] = "1"
	t.Claims["User"] = u.ID
	t.Claims["Expires"] = time.Now().Add(time.Minute * 15).Unix()

	tokenString, err := t.SignedString(signKey)

	if err != nil {
		fmt.Fprintln(w, "Error signing that token")
		return
	}

	json.NewEncoder(w).Encode(Token{Value: tokenString})
}

// GetCurrentUserHandler retrieves the User currently logged in.
// 		GET /api/user?api_token=xxx
func GetCurrentUserHandler(w http.ResponseWriter, r *http.Request, t *jwt.Token) {
	id, ok := t.Claims["User"].(string)

	if !ok {
		fmt.Fprintln(w, "Could not cast interface to bson.ObjectId!")
		return
	}

	c := session.DB(db).C("users")
	var u User

	if c.Find(bson.M{"_id": bson.ObjectIdHex(id)}).One(&u); u == (User{}) {
		fmt.Fprintln(w, "Could not find that user!")
		return
	}

	json.NewEncoder(w).Encode(u)
}

// NewRepository creates a new Repository link.
// 		POST /api/repo/{host}/{user}/{name}?access_token=xxx
func NewRepository(w http.ResponseWriter, r *http.Request, t *jwt.Token) {
	vars := mux.Vars(r)
	host, user, name := vars["host"], vars["user"], vars["name"]

	c := session.DB(db).C("repositories")
	var re Repository

	if c.Find(bson.M{"host": host, "user": user, "name": name}).One(&re); re != (Repository{}) {
		fmt.Fprintln(w, "That repo already exist")
		return
	}

	re = Repository{ID: bson.NewObjectId(), Host: host, User: user, Name: name}

	if err := c.Insert(re); err != nil {
		fmt.Fprintln(w, "Currently unable to create that new repo")
		return
	}

	json.NewEncoder(w).Encode(re)
}

// NewReviewHandler creates a new Review on a Repository.
// 		POST /api/repo/{repository}/review?text=This+sucks&rating=2&access_token=xxx
func NewReviewHandler(w http.ResponseWriter, r *http.Request, t *jwt.Token) {
	vars := mux.Vars(r)
	host, user, name, review := vars["host"], vars["user"], vars["name"], r.FormValue("review")

	if review == "" {
		fmt.Fprintln(w, "Please let your review have some content?")
		return
	}

	c := session.DB(db).C("repositories")
	var rep Repository

	if c.Find(bson.M{"host": host, "user": user, "name": name}).One(&rep); rep == (Repository{}) {
		fmt.Fprintln(w, "a repo with that url doesnt exist...")
		return
	}

	c = session.DB(db).C("users")
	var u User

	if c.Find(bson.M{"_id": bson.ObjectIdHex(t.Claims["User"].(string))}).One(&u); u == (User{}) {
		fmt.Fprintln(w, "a user with that id doesnt exist...")
		return
	}

	c = session.DB(db).C("reviews")
	rev := Review{ID: bson.NewObjectId(), Content: review, From: u.ID, Repository: rep.ID}

	if err := c.Insert(rev); err != nil {
		fmt.Fprintln(w, "something went wrong while inserting the new review!")
		return
	}

	json.NewEncoder(w).Encode(rev)
}

// GetReviewHandler retrieves a Review.
// 		GET /repo/{host}/{user}/{name}/{review}
func GetReviewHandler(w http.ResponseWriter, r *http.Request) {

}

// GetRepository retrieves a Repository.
// 		GET /api/repo/{host}/{user}/{name}
func GetRepository(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	host, user, name := vars["host"], vars["user"], vars["name"]

	c := session.DB(db).C("repositories")
	var re Repository

	if c.Find(bson.M{"host": host, "user": user, "name": name}).One(&re); re == (Repository{}) {
		fmt.Fprintln(w, "that repo doesnt exist")
		return
	}

	json.NewEncoder(w).Encode(re)
}

// Headers adds JSON headers onto a request.
func Headers(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")

		fn(w, r)
	}
}

// Restrict checks if a provided access_token is valid, if it is continue the request.
func Restrict(fn func(http.ResponseWriter, *http.Request, *jwt.Token)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		tokenString := r.FormValue("access_token")

		token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
			return verifyKey, nil
		})

		if err != nil {
			fmt.Fprintln(w, "That is not a valid token")
			fmt.Println(err)
			return
		}

		if !token.Valid {
			fmt.Fprintln(w, "Something obscurely strange happened to your token")
		}

		fn(w, r, token)
	}
}
<commit_msg>main: use JSON on *all* responses, yeah cause apparently that wasnt a thing!<commit_after>package main

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

const (
	usernameAndPasswordRegexString = `^[a-zA-Z]\w*[a-zA-Z]$` // 1st and last characters must be letters.
	emailRegexString               = `^.*\@.*$`              // As long as it has an '@' symbol in it I don't care.

	db             = "repo-reviews" // Mongodb database name
	privateKeyPath = "app.rsa"      // Command: openssl genrsa -out app.rsa 1024
	publicKeyPath  = "app.rsa.pub"  // Command: openssl rsa -in app.rsa -pubout > app.rsa.pub
)

var (
	session            *mgo.Session
	verifyKey, signKey []byte
	signingMethod      jwt.SigningMethod

	usernameAndPasswordRegex *regexp.Regexp // Compiled regex for quicker matching.
	emailRegex               *regexp.Regexp // Compiled regex for quicker matching.
)

// User is someone who has registered on the site.
type User struct {
	ID           bson.ObjectId `bson:"_id" json:"_id"`
	Username     string        `bson:"username" json:"username"`
	PasswordHash string        `bson:"password_hash" json:"password_hash"`
	Email        string        `bson:"email" json:"email"`
	PasswordSalt string
}

// A Review is created by a User to express their feelings of a particular Repository
type Review struct {
	ID         bson.ObjectId `bson:"_id" json:"_id"`
	From       bson.ObjectId `bson:"from" json:"from"`
	Repository bson.ObjectId `bson:"repository" json:"repository"`
	Content    string        `bson:"content" json:"content"`
	Rating     int           `bson:"rating" json:"rating"`
}

// Repository is the representation of a git project on Rr
type Repository struct {
	ID   bson.ObjectId `bson:"_id" json:"_id"`
	Host string        `bson:"host" json:"host"`
	User string        `bson:"user" json:"user"`
	Name string        `bson:"name" json:"name"`
}

// Token is a container used to send a User their access_token
type Token struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

// MessageResponse is used as a general response for JSON rest requests
type MessageResponse struct {
	Status  Status      `json:"status"`
	Message string      `json:"message"`
	Data    interface{} `json:"data,omitempty"`
}

// Status represents a general http error
type Status struct {
	Code    int    `bson:"code" json:"code"`
	Message string `bson:"message" json:"message"`
	Error   bool   `bson:"error" json:"error"`
}

// NewOKStatus returns a new Status object with no errors.
func NewOKStatus() Status {
	return Status{http.StatusOK, "Everything is awesome!", false}
}

// NewFailedStatus returns a new Status object saying a request failed.
func NewFailedStatus() Status {
	return Status{http.StatusConflict, "Well this is awkward...", true}
}

// NewForbiddenStatus returns a new Status object detailing a failure of authorization.
func NewForbiddenStatus() Status {
	return Status{http.StatusForbidden, "You can't go here :)", true}
}

// NewServerErrorStatus returns a new Status object saying a server error has occured
func NewServerErrorStatus() Status {
	return Status{http.StatusInternalServerError, "Something bad has happened, we're sending the calvalry.", true}
}

func init() {
	var err error

	signKey, err = ioutil.ReadFile(privateKeyPath)

	if err != nil {
		fmt.Println("Could not find your private key!")
		panic(err)
	}

	verifyKey, err = ioutil.ReadFile(publicKeyPath)

	if err != nil {
		fmt.Println("Could not find your public key!")
		panic(err)
	}

	signingMethod = jwt.GetSigningMethod("RS256")

	usernameAndPasswordRegex, err = regexp.Compile(usernameAndPasswordRegexString)

	if err != nil {
		panic(err)
	}

	emailRegex, err = regexp.Compile(emailRegexString)

	if err != nil {
		panic(err)
	}

}

func main() {
	var err error

	session, err = mgo.Dial("localhost")
	if err != nil {
		panic(err)
	}

	defer session.Close()

	r := mux.NewRouter()
	api := r.PathPrefix("/api").Subrouter()

	api.HandleFunc("/user/create", Headers(NewUserHandler)).Methods("POST")

	api.HandleFunc("/user/login", Headers(LoginUserHandler)).Methods("POST")

	api.HandleFunc("/user", Headers(Restrict(GetCurrentUserHandler))).Methods("GET")

	api.HandleFunc("/user/{username}", Headers(GetUserHandler)).Methods("GET")

	api.HandleFunc("/repo/{host}/{user}/{name}/review", Headers(Restrict(NewReviewHandler))).Methods("POST")

	api.HandleFunc("/repo/{host}/{user}/{name}/{review}", Headers(GetReviewHandler)).Methods("GET")

	api.HandleFunc("/repo/{host}/{user}/{name}", Headers(GetRepository)).Methods("GET")

	api.HandleFunc("/repo/{host}/{user}/{name}", Headers(Restrict(NewRepository))).Methods("POST")

	r.HandleFunc("/secret", Headers(Restrict(GetSecretHandler))).Methods("GET")

	// Serve ALL the static files!
	r.PathPrefix("/").Handler(http.FileServer(http.Dir("static/")))

	http.Handle("/", r)

	fmt.Println("Loading http server on :8080...")

	fmt.Println(http.ListenAndServe(":8080", nil))

}

// GetSecretHandler is a test handler to check if access_tokens work.
// 		GET /secret
func GetSecretHandler(w http.ResponseWriter, r *http.Request, t *jwt.Token) {
	json.NewEncoder(w).Encode(MessageResponse{Message: "NCSS IS ILLUMINATTI", Status: NewOKStatus()})
}

// NewUserHandler creates a new user.
// 		POST /api/user/create?username=paked&pasword=pw
func NewUserHandler(w http.ResponseWriter, r *http.Request) {
	username, email, password := r.FormValue("username"), r.FormValue("email"), r.FormValue("password")
	uRe, eRe, pRe := usernameAndPasswordRegex.FindString(username), emailRegex.FindString(email), usernameAndPasswordRegex.FindString(username)
	e := json.NewEncoder(w)

	if uRe == "" || eRe == "" || pRe == "" {
		e.Encode(MessageResponse{Message: "Your username, password or email is not valid.", Status: NewFailedStatus()})
		return
	}

	c := session.DB(db).C("users")
	var u User

	if c.Find(bson.M{"username": username}).One(&u); u != (User{}) {
		e.Encode(MessageResponse{Message: "That user already exists!", Status: NewFailedStatus()})
		return
	}

	u = User{ID: bson.NewObjectId(), Username: username, Email: email, PasswordHash: password}

	if err := c.Insert(u); err != nil {
		e.Encode(MessageResponse{Message: "Could not submit that user", Status: NewServerErrorStatus()})
		return
	}

	e.Encode(MessageResponse{Message: "Here is your user!", Status: NewOKStatus(), Data: u})
}

// GetUserHandler retrieves a User from the database
// 		GET /api/user/{username}
func GetUserHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	e := json.NewEncoder(w)

	c := session.DB(db).C("users")
	var u User

	if c.Find(bson.M{"username": vars["username"]}).One(&u); u == (User{}) {
		e.Encode(MessageResponse{Message: "That user does not exist", Status: NewFailedStatus()})
		return
	}

	e.Encode(MessageResponse{Message: "We found that user!", Status: NewOKStatus(), Data: u})
}

// LoginUserHandler checks the provided login credentials and if valid return an access_token.
//		POST /api/user/login?username=paked&password=pw
func LoginUserHandler(w http.ResponseWriter, r *http.Request) {
	username, password := r.FormValue("username"), r.FormValue("password")
	e := json.NewEncoder(w)

	if username == "" || password == "" {
		e.Encode(MessageResponse{Message: "That is not a valid username or password", Status: NewFailedStatus()})
		return
	}

	c := session.DB(db).C("users")

	var u User

	if c.Find(bson.M{"username": username, "password_hash": password}).One(&u); u == (User{}) {
		e.Encode(MessageResponse{Message: "A user with that username and password combination does not exist.", Status: NewFailedStatus()})
		return
	}

	t := jwt.New(signingMethod)

	t.Claims["AccessToken"] = "1"
	t.Claims["User"] = u.ID
	t.Claims["Expires"] = time.Now().Add(time.Minute * 15).Unix()

	tokenString, err := t.SignedString(signKey)

	if err != nil {
		e.Encode(MessageResponse{Message: "We could not sign the token made for you", Status: NewServerErrorStatus()})
		return
	}

	json.NewEncoder(w).Encode(MessageResponse{Message: "Here is your token!", Status: NewOKStatus(), Data: tokenString})
}

// GetCurrentUserHandler retrieves the User currently logged in.
// 		GET /api/user?api_token=xxx
func GetCurrentUserHandler(w http.ResponseWriter, r *http.Request, t *jwt.Token) {
	id, ok := t.Claims["User"].(string)
	e := json.NewEncoder(w)

	if !ok {
		e.Encode(MessageResponse{Message: "Could not cast interface to that bson.ObjectId!", Status: NewServerErrorStatus()})
		return
	}

	c := session.DB(db).C("users")
	var u User

	if c.Find(bson.M{"_id": bson.ObjectIdHex(id)}).One(&u); u == (User{}) {
		e.Encode(MessageResponse{Message: "Could not find that user!", Status: NewFailedStatus()})
		return
	}

	json.NewEncoder(w).Encode(MessageResponse{Message: "Here is you!", Status: NewOKStatus(), Data: u})
}

// NewRepository creates a new Repository link.
// 		POST /api/repo/{host}/{user}/{name}?access_token=xxx
func NewRepository(w http.ResponseWriter, r *http.Request, t *jwt.Token) {
	vars := mux.Vars(r)
	host, user, name := vars["host"], vars["user"], vars["name"]
	e := json.NewEncoder(w)

	c := session.DB(db).C("repositories")
	var re Repository

	if c.Find(bson.M{"host": host, "user": user, "name": name}).One(&re); re != (Repository{}) {
		e.Encode(MessageResponse{Message: "That repo already exists", Status: NewFailedStatus()})
		return
	}

	re = Repository{ID: bson.NewObjectId(), Host: host, User: user, Name: name}

	if err := c.Insert(re); err != nil {
		e.Encode(MessageResponse{Message: "Could not insert that repository", Status: NewServerErrorStatus()})
		return
	}

	json.NewEncoder(w).Encode(MessageResponse{Message: "We created that new repository!", Status: NewOKStatus(), Data: re})
}

// NewReviewHandler creates a new Review on a Repository.
// 		POST /api/repo/{repository}/review?text=This+sucks&rating=2&access_token=xxx
func NewReviewHandler(w http.ResponseWriter, r *http.Request, t *jwt.Token) {
	vars := mux.Vars(r)
	host, user, name, review := vars["host"], vars["user"], vars["name"], r.FormValue("review")
	e := json.NewEncoder(w)

	if review == "" {
		e.Encode(MessageResponse{Message: "Please say something in your review :)", Status: NewFailedStatus()})
		return
	}

	c := session.DB(db).C("repositories")
	var rep Repository

	if c.Find(bson.M{"host": host, "user": user, "name": name}).One(&rep); rep == (Repository{}) {
		e.Encode(MessageResponse{Message: "A repo with that URL doesn't exist :/", Status: NewFailedStatus()})
		return
	}

	c = session.DB(db).C("users")
	var u User

	if c.Find(bson.M{"_id": bson.ObjectIdHex(t.Claims["User"].(string))}).One(&u); u == (User{}) {
		e.Encode(MessageResponse{Message: "A user with that id doesnt exist!", Status: NewFailedStatus()})
		return
	}

	c = session.DB(db).C("reviews")
	rev := Review{ID: bson.NewObjectId(), Content: review, From: u.ID, Repository: rep.ID}

	if err := c.Insert(rev); err != nil {
		e.Encode(MessageResponse{Message: "Could not insert that review!", Status: NewServerErrorStatus()})
		return
	}

	e.Encode(MessageResponse{Message: "Congrats you made a review!", Status: NewOKStatus(), Data: rev})
}

// GetReviewHandler retrieves a Review.
// 		GET /repo/{host}/{user}/{name}/{review}
func GetReviewHandler(w http.ResponseWriter, r *http.Request) {

}

// GetRepository retrieves a Repository.
// 		GET /api/repo/{host}/{user}/{name}
func GetRepository(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	host, user, name := vars["host"], vars["user"], vars["name"]
	e := json.NewEncoder(w)

	c := session.DB(db).C("repositories")
	var re Repository

	if c.Find(bson.M{"host": host, "user": user, "name": name}).One(&re); re == (Repository{}) {
		e.Encode(MessageResponse{Message: "That repository does not exist", Status: NewFailedStatus()})
		return
	}

	json.NewEncoder(w).Encode(MessageResponse{Message: "Here is your repo", Status: NewOKStatus(), Data: re})
}

// Headers adds JSON headers onto a request.
func Headers(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")

		fn(w, r)
	}
}

// Restrict checks if a provided access_token is valid, if it is continue the request.
func Restrict(fn func(http.ResponseWriter, *http.Request, *jwt.Token)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		tokenString := r.FormValue("access_token")

		token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
			return verifyKey, nil
		})

		if err != nil {
			fmt.Fprintln(w, "That is not a valid token")
			fmt.Println(err)
			return
		}

		if !token.Valid {
			fmt.Fprintln(w, "Something obscurely strange happened to your token")
		}

		fn(w, r, token)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/volatiletech/abcweb/cmd"
)

const abcwebVersion = "2.1.5"

func main() {
	// Too much happens between here and cobra's argument handling, for
	// something so simple. Just do it immediately.
	if len(os.Args) > 1 && os.Args[1] == "--version" {
		fmt.Println("ABCWeb v" + abcwebVersion)
		return
	}

	cmd.Execute()
}
<commit_msg>Bump version to 2.1.6 (deps cmd fix)<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/volatiletech/abcweb/cmd"
)

const abcwebVersion = "2.1.6"

func main() {
	// Too much happens between here and cobra's argument handling, for
	// something so simple. Just do it immediately.
	if len(os.Args) > 1 && os.Args[1] == "--version" {
		fmt.Println("ABCWeb v" + abcwebVersion)
		return
	}

	cmd.Execute()
}
<|endoftext|>


<commit_before>package gophermail

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/mail"
	"net/textproto"
	"path/filepath"
	"strings"

	"github.com/sloonz/go-qprintable"
)

// Message Lint: http://tools.ietf.org/tools/msglint/

const crlf = "\r\n"

var ErrMissingRecipient = errors.New("No recipient specified. At one To, Cc, or Bcc recipient is required.")
var ErrMissingFromAddress = errors.New("No from address specified.")

// A Message represents an email message.
// Addresses may be of any form permitted by RFC 5322.
type Message struct {
	// TODO(JPOEHLS): Add support for specifying the Sender header.

	// Technically this could be a list of addresses but we don't support that. See RFC 2822 s3.6.2.
	From mail.Address

	// Technically this could be a list of addresses but we don't support that. See RFC 2822 s3.6.2.
	ReplyTo mail.Address // optional

	To, Cc, Bcc []mail.Address

	Subject string // optional

	Body     string // optional
	HTMLBody string // optional

	Attachments []Attachment // optional

	// Extra mail headers.
	Headers mail.Header
}

// appendMailAddresses parses any number of addresses and appends them to a
// destination slice. If any of the addresses fail to parse, none of them are
// appended.
func appendMailAddresses(dest *[]mail.Address, addresses ...string) error {
	var parsedAddresses []mail.Address
	var err error

	for _, address := range addresses {
		parsed, err := mail.ParseAddress(address)
		if err != nil {
			return err
		}
		parsedAddresses = append(parsedAddresses, *parsed)
	}

	*dest = append(*dest, parsedAddresses...)
	return err
}

// setMailAddress parses an address and sets it to a destination mail address.
func setMailAddress(dest *mail.Address, address string) error {
	parsed, err := mail.ParseAddress(address)
	if err != nil {
		return err
	}
	*dest = *parsed
	return nil
}

// SetFrom creates a mail.Address and assigns it to the message's From
// field.
func (m *Message) SetFrom(address string) error {
	return setMailAddress(&m.From, address)
}

// SetReplyTo creates a mail.Address and assigns it to the message's ReplyTo
// field.
func (m *Message) SetReplyTo(address string) error {
	return setMailAddress(&m.ReplyTo, address)
}

// AddTo creates a mail.Address and adds it to the list of To addresses in the
// message
func (m *Message) AddTo(addresses ...string) error {
	return appendMailAddresses(&m.To, addresses...)
}

// AddCc creates a mail.Address and adds it to the list of Cc addresses in the
// message
func (m *Message) AddCc(addresses ...string) error {
	return appendMailAddresses(&m.Cc, addresses...)
}

// AddBcc creates a mail.Address and adds it to the list of Bcc addresses in the
// message
func (m *Message) AddBcc(addresses ...string) error {
	return appendMailAddresses(&m.Bcc, addresses...)
}

// An Attachment represents an email attachment.
type Attachment struct {
	// Name must be set to a valid file name.
	Name string

	// Optional.
	// Uses mime.TypeByExtension and falls back
	// to application/octet-stream if unknown.
	ContentType string

	Data io.Reader
}

// Bytes gets the encoded MIME message.
func (m *Message) Bytes() ([]byte, error) {
	var buffer = &bytes.Buffer{}

	header := textproto.MIMEHeader{}

	var err error

	// Require To, Cc, or Bcc
	// We'll parse the slices into a list of addresses
	// and then make sure that list isn't empty.
	toAddrs := getAddressListString(m.To)
	ccAddrs := getAddressListString(m.Cc)
	bccAddrs := getAddressListString(m.Bcc)

	var hasTo = toAddrs != ""
	var hasCc = ccAddrs != ""
	var hasBcc = bccAddrs != ""

	if !hasTo && !hasCc && !hasBcc {
		return nil, ErrMissingRecipient
	}

	if hasTo {
		header.Add("To", toAddrs)
	}
	if hasCc {
		header.Add("Cc", ccAddrs)
	}
	// BCC header is excluded on purpose.
	// BCC recipients aren't included in the message
	// headers and are only used at the SMTP level.

	var emptyAddress mail.Address
	// Require From address
	if m.From == emptyAddress {
		return nil, ErrMissingFromAddress
	}
	header.Add("From", m.From.String())

	// Optional ReplyTo
	if m.ReplyTo != emptyAddress {
		header.Add("Reply-To", m.ReplyTo.String())
	}

	// Optional Subject
	if m.Subject != "" {
		quotedSubject := qEncodeAndWrap(m.Subject, 9 /* len("Subject: ") */)
		if quotedSubject[0] == '"' {
			// qEncode used simple quoting, which adds quote
			// characters to email subjects.
			quotedSubject = quotedSubject[1 : len(quotedSubject)-2]
		}
		header.Add("Subject", quotedSubject)
	}

	for k, v := range m.Headers {
		header[k] = v
	}

	// Top level multipart writer for our `multipart/mixed` body.
	mixedw := multipart.NewWriter(buffer)

	header.Add("MIME-Version", "1.0")
	header.Add("Content-Type", fmt.Sprintf("multipart/mixed;%s boundary=%s", crlf, mixedw.Boundary()))

	err = writeHeader(buffer, header)
	if err != nil {
		return nil, err
	}

	// Write the start of our `multipart/mixed` body.
	_, err = fmt.Fprintf(buffer, "--%s%s", mixedw.Boundary(), crlf)
	if err != nil {
		return nil, err
	}

	// Does the message have a body?
	if m.Body != "" || m.HTMLBody != "" {

		// Nested multipart writer for our `multipart/alternative` body.
		altw := multipart.NewWriter(buffer)

		header = textproto.MIMEHeader{}
		header.Add("Content-Type", fmt.Sprintf("multipart/alternative;%s boundary=%s", crlf, altw.Boundary()))
		err := writeHeader(buffer, header)
		if err != nil {
			return nil, err
		}

		if m.Body != "" {
			header = textproto.MIMEHeader{}
			header.Add("Content-Type", "text/plain; charset=utf-8")
			header.Add("Content-Transfer-Encoding", "quoted-printable")
			//header.Add("Content-Transfer-Encoding", "base64")

			partw, err := altw.CreatePart(header)
			if err != nil {
				return nil, err
			}

			bodyBytes := []byte(m.Body)
			//encoder := NewBase64MimeEncoder(partw)
			encoder := qprintable.NewEncoder(qprintable.DetectEncoding(m.Body), partw)
			_, err = encoder.Write(bodyBytes)
			if err != nil {
				return nil, err
			}
			err = encoder.Close()
			if err != nil {
				return nil, err
			}
		}

		if m.HTMLBody != "" {
			header = textproto.MIMEHeader{}
			header.Add("Content-Type", "text/html; charset=utf-8")
			//header.Add("Content-Transfer-Encoding", "quoted-printable")
			header.Add("Content-Transfer-Encoding", "base64")

			partw, err := altw.CreatePart(header)
			if err != nil {
				return nil, err
			}

			htmlBodyBytes := []byte(m.HTMLBody)
			encoder := NewBase64MimeEncoder(partw)
			//encoder := qprintable.NewEncoder(qprintable.DetectEncoding(m.HTMLBody), partw)
			_, err = encoder.Write(htmlBodyBytes)
			if err != nil {
				return nil, err
			}
			err = encoder.Close()
			if err != nil {
				return nil, err
			}
		}

		altw.Close()
	}

	if m.Attachments != nil && len(m.Attachments) > 0 {

		for _, attachment := range m.Attachments {

			contentType := attachment.ContentType
			if contentType == "" {
				contentType = mime.TypeByExtension(filepath.Ext(attachment.Name))
				if contentType == "" {
					contentType = "application/octet-stream"
				}
			}

			header := textproto.MIMEHeader{}
			header.Add("Content-Type", contentType)
			header.Add("Content-Disposition", fmt.Sprintf(`attachment;%s filename="%s"`, crlf, attachment.Name))
			header.Add("Content-Transfer-Encoding", "base64")

			attachmentPart, err := mixedw.CreatePart(header)
			if err != nil {
				return nil, err
			}

			if attachment.Data != nil {
				encoder := NewBase64MimeEncoder(attachmentPart)
				_, err = io.Copy(encoder, attachment.Data)
				if err != nil {
					return nil, err
				}
				err = encoder.Close()
				if err != nil {
					return nil, err
				}
			}
		}

	}

	mixedw.Close()

	return buffer.Bytes(), nil
}

// writeHeader writes the specified MIMEHeader to the io.Writer.
// Header values will be trimmed but otherwise left alone.
// Headers with multiple values are not supported and will return an error.
func writeHeader(w io.Writer, header textproto.MIMEHeader) error {
	for k, vs := range header {
		_, err := fmt.Fprintf(w, "%s: ", k)
		if err != nil {
			return err
		}

		for i, v := range vs {
			v = textproto.TrimString(v)

			_, err := fmt.Fprintf(w, "%s", v)
			if err != nil {
				return err
			}

			if i < len(vs)-1 {
				return errors.New("Multiple header values are not supported.")
			}
		}

		_, err = fmt.Fprint(w, crlf)
		if err != nil {
			return err
		}
	}

	// Write a blank line as a spacer
	_, err := fmt.Fprint(w, crlf)
	if err != nil {
		return err
	}

	return nil
}

// qEncode encodes a string with Q encoding defined as an 'encoded-word' in RFC 2047.
// The maximum encoded word length of 75 characters is not accounted for.
// Use qEncodeAndWrap if you need that.
//
// Inspired by https://gist.github.com/andelf/5004821
func qEncode(input string) string {
	// use mail's rfc2047 to encode any string
	addr := mail.Address{Name: input, Address: ""}
	s := addr.String()
	return s[:len(s)-3]
}

// qEncodeAndWrap encodes the input as potentially multiple 'encoded-words'
// with CRLF SPACE line breaks between them to (as best as possible)
// guarantee that each encoded-word is no more than 75 characters
// and, padding included, each line is no longer than 76 characters.
// See RFC 2047 s2.
func qEncodeAndWrap(input string, padding int) string {

	// Split at any whitespace but prefer "; " or  ", " or " >" or "> " which
	// denotes a clear semantic break.
	// Remember that the qEncoded input isn't guaranteed to have the same
	// length as the unencoded input (obvious). Example: http://play.golang.org/p/dXA5IJnL22

	// Increase the padding to account for
	// the encoded-word 'envelop' tokens.
	// "?" charset (utf-8 is always assumed) "?" encoding "?" encoded-text "?="
	padding += 11

	// Tokenization included, the encoded word must not
	// be longer than 75 characters.
	const maxEncodedWordLength = 75

	var firstTry = qEncode(input)
	if len(firstTry) > maxEncodedWordLength-padding {

		// TODO(JPOEHLS): Implement an algorithm to break the input into multiple encoded-words.

		return firstTry
	} else {
		return firstTry
	}
}

// getAddressListString encodes a slice of email addresses into
// a string value suitable for a MIME header. Each address is
// Q encoded and wrapped onto its own line to help ensure that
// the header line doesn't cross the 78 character maximum.
func getAddressListString(addresses []mail.Address) string {
	var addressStrings []string

	for _, address := range addresses {
		addressStrings = append(addressStrings, address.String())
	}
	return strings.Join(addressStrings, ","+crlf+" ")
}
<commit_msg>custom parser for addresses<commit_after>package gophermail

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/mail"
	"net/textproto"
	"path/filepath"
	"strings"

	"github.com/sloonz/go-qprintable"
)

// Message Lint: http://tools.ietf.org/tools/msglint/

const crlf = "\r\n"

var ErrMissingRecipient = errors.New("No recipient specified. At one To, Cc, or Bcc recipient is required.")
var ErrMissingFromAddress = errors.New("No from address specified.")

// A Message represents an email message.
// Addresses may be of any form permitted by RFC 5322.
type Message struct {
	// TODO(JPOEHLS): Add support for specifying the Sender header.

	// Technically this could be a list of addresses but we don't support that. See RFC 2822 s3.6.2.
	From mail.Address

	// Technically this could be a list of addresses but we don't support that. See RFC 2822 s3.6.2.
	ReplyTo mail.Address // optional

	To, Cc, Bcc []mail.Address

	Subject string // optional

	Body     string // optional
	HTMLBody string // optional

	Attachments []Attachment // optional

	// Extra mail headers.
	Headers mail.Header
}

func parseStringIntoAddress(str string) (address *mail.Address, err error) {
	// init addr
	address = &mail.Address{}

	// do split on space, and remove blanks and quotes
	cleaned := strings.Replace(str, "\"", "", -1)
	cleaned = strings.Replace(cleaned, "<", "", -1)
	cleaned = strings.Replace(cleaned, ">", "", -1)

	spl := strings.Split(cleaned, " ")
	var arrToCheck []string

	for _, val := range spl {
		newVal := strings.TrimSpace(val)

		if newVal != "" {
			arrToCheck = append(arrToCheck, newVal)
		}
	}

	if len(arrToCheck) == 0 {
		err = errors.New("Provided address is invalid.")
		return
	}

	// extract email
	address.Address = arrToCheck[len(arrToCheck)-1]

	// extract name
	if len(arrToCheck) > 1 {
		// set FullName
		address.Name = strings.Join(arrToCheck[:len(arrToCheck)-1], " ")
	}

	return
}

// appendMailAddresses parses any number of addresses and appends them to a
// destination slice. If any of the addresses fail to parse, none of them are
// appended.
func appendMailAddresses(dest *[]mail.Address, addresses ...string) error {
	var parsedAddresses []mail.Address
	var err error

	for _, address := range addresses {
		parsed, err := parseStringIntoAddress(address)
		if err != nil {
			return err
		}
		parsedAddresses = append(parsedAddresses, *parsed)
	}

	*dest = append(*dest, parsedAddresses...)
	return err
}

// setMailAddress parses an address and sets it to a destination mail address.
func setMailAddress(dest *mail.Address, address string) error {
	parsed, err := parseStringIntoAddress(address)
	if err != nil {
		return err
	}
	*dest = *parsed
	return nil
}

// SetFrom creates a mail.Address and assigns it to the message's From
// field.
func (m *Message) SetFrom(address string) error {
	return setMailAddress(&m.From, address)
}

// SetReplyTo creates a mail.Address and assigns it to the message's ReplyTo
// field.
func (m *Message) SetReplyTo(address string) error {
	return setMailAddress(&m.ReplyTo, address)
}

// AddTo creates a mail.Address and adds it to the list of To addresses in the
// message
func (m *Message) AddTo(addresses ...string) error {
	return appendMailAddresses(&m.To, addresses...)
}

// AddCc creates a mail.Address and adds it to the list of Cc addresses in the
// message
func (m *Message) AddCc(addresses ...string) error {
	return appendMailAddresses(&m.Cc, addresses...)
}

// AddBcc creates a mail.Address and adds it to the list of Bcc addresses in the
// message
func (m *Message) AddBcc(addresses ...string) error {
	return appendMailAddresses(&m.Bcc, addresses...)
}

// An Attachment represents an email attachment.
type Attachment struct {
	// Name must be set to a valid file name.
	Name string

	// Optional.
	// Uses mime.TypeByExtension and falls back
	// to application/octet-stream if unknown.
	ContentType string

	Data io.Reader
}

// Bytes gets the encoded MIME message.
func (m *Message) Bytes() ([]byte, error) {
	var buffer = &bytes.Buffer{}

	header := textproto.MIMEHeader{}

	var err error

	// Require To, Cc, or Bcc
	// We'll parse the slices into a list of addresses
	// and then make sure that list isn't empty.
	toAddrs := getAddressListString(m.To)
	ccAddrs := getAddressListString(m.Cc)
	bccAddrs := getAddressListString(m.Bcc)

	var hasTo = toAddrs != ""
	var hasCc = ccAddrs != ""
	var hasBcc = bccAddrs != ""

	if !hasTo && !hasCc && !hasBcc {
		return nil, ErrMissingRecipient
	}

	if hasTo {
		header.Add("To", toAddrs)
	}
	if hasCc {
		header.Add("Cc", ccAddrs)
	}
	// BCC header is excluded on purpose.
	// BCC recipients aren't included in the message
	// headers and are only used at the SMTP level.

	var emptyAddress mail.Address
	// Require From address
	if m.From == emptyAddress {
		return nil, ErrMissingFromAddress
	}
	header.Add("From", m.From.String())

	// Optional ReplyTo
	if m.ReplyTo != emptyAddress {
		header.Add("Reply-To", m.ReplyTo.String())
	}

	// Optional Subject
	if m.Subject != "" {
		quotedSubject := qEncodeAndWrap(m.Subject, 9 /* len("Subject: ") */)
		if quotedSubject[0] == '"' {
			// qEncode used simple quoting, which adds quote
			// characters to email subjects.
			quotedSubject = quotedSubject[1 : len(quotedSubject)-2]
		}
		header.Add("Subject", quotedSubject)
	}

	for k, v := range m.Headers {
		header[k] = v
	}

	// Top level multipart writer for our `multipart/mixed` body.
	mixedw := multipart.NewWriter(buffer)

	header.Add("MIME-Version", "1.0")
	header.Add("Content-Type", fmt.Sprintf("multipart/mixed;%s boundary=%s", crlf, mixedw.Boundary()))

	err = writeHeader(buffer, header)
	if err != nil {
		return nil, err
	}

	// Write the start of our `multipart/mixed` body.
	_, err = fmt.Fprintf(buffer, "--%s%s", mixedw.Boundary(), crlf)
	if err != nil {
		return nil, err
	}

	// Does the message have a body?
	if m.Body != "" || m.HTMLBody != "" {

		// Nested multipart writer for our `multipart/alternative` body.
		altw := multipart.NewWriter(buffer)

		header = textproto.MIMEHeader{}
		header.Add("Content-Type", fmt.Sprintf("multipart/alternative;%s boundary=%s", crlf, altw.Boundary()))
		err := writeHeader(buffer, header)
		if err != nil {
			return nil, err
		}

		if m.Body != "" {
			header = textproto.MIMEHeader{}
			header.Add("Content-Type", "text/plain; charset=utf-8")
			header.Add("Content-Transfer-Encoding", "quoted-printable")
			//header.Add("Content-Transfer-Encoding", "base64")

			partw, err := altw.CreatePart(header)
			if err != nil {
				return nil, err
			}

			bodyBytes := []byte(m.Body)
			//encoder := NewBase64MimeEncoder(partw)
			encoder := qprintable.NewEncoder(qprintable.DetectEncoding(m.Body), partw)
			_, err = encoder.Write(bodyBytes)
			if err != nil {
				return nil, err
			}
			err = encoder.Close()
			if err != nil {
				return nil, err
			}
		}

		if m.HTMLBody != "" {
			header = textproto.MIMEHeader{}
			header.Add("Content-Type", "text/html; charset=utf-8")
			//header.Add("Content-Transfer-Encoding", "quoted-printable")
			header.Add("Content-Transfer-Encoding", "base64")

			partw, err := altw.CreatePart(header)
			if err != nil {
				return nil, err
			}

			htmlBodyBytes := []byte(m.HTMLBody)
			encoder := NewBase64MimeEncoder(partw)
			//encoder := qprintable.NewEncoder(qprintable.DetectEncoding(m.HTMLBody), partw)
			_, err = encoder.Write(htmlBodyBytes)
			if err != nil {
				return nil, err
			}
			err = encoder.Close()
			if err != nil {
				return nil, err
			}
		}

		altw.Close()
	}

	if m.Attachments != nil && len(m.Attachments) > 0 {

		for _, attachment := range m.Attachments {

			contentType := attachment.ContentType
			if contentType == "" {
				contentType = mime.TypeByExtension(filepath.Ext(attachment.Name))
				if contentType == "" {
					contentType = "application/octet-stream"
				}
			}

			header := textproto.MIMEHeader{}
			header.Add("Content-Type", contentType)
			header.Add("Content-Disposition", fmt.Sprintf(`attachment;%s filename="%s"`, crlf, attachment.Name))
			header.Add("Content-Transfer-Encoding", "base64")

			attachmentPart, err := mixedw.CreatePart(header)
			if err != nil {
				return nil, err
			}

			if attachment.Data != nil {
				encoder := NewBase64MimeEncoder(attachmentPart)
				_, err = io.Copy(encoder, attachment.Data)
				if err != nil {
					return nil, err
				}
				err = encoder.Close()
				if err != nil {
					return nil, err
				}
			}
		}

	}

	mixedw.Close()

	return buffer.Bytes(), nil
}

// writeHeader writes the specified MIMEHeader to the io.Writer.
// Header values will be trimmed but otherwise left alone.
// Headers with multiple values are not supported and will return an error.
func writeHeader(w io.Writer, header textproto.MIMEHeader) error {
	for k, vs := range header {
		_, err := fmt.Fprintf(w, "%s: ", k)
		if err != nil {
			return err
		}

		for i, v := range vs {
			v = textproto.TrimString(v)

			_, err := fmt.Fprintf(w, "%s", v)
			if err != nil {
				return err
			}

			if i < len(vs)-1 {
				return errors.New("Multiple header values are not supported.")
			}
		}

		_, err = fmt.Fprint(w, crlf)
		if err != nil {
			return err
		}
	}

	// Write a blank line as a spacer
	_, err := fmt.Fprint(w, crlf)
	if err != nil {
		return err
	}

	return nil
}

// qEncode encodes a string with Q encoding defined as an 'encoded-word' in RFC 2047.
// The maximum encoded word length of 75 characters is not accounted for.
// Use qEncodeAndWrap if you need that.
//
// Inspired by https://gist.github.com/andelf/5004821
func qEncode(input string) string {
	// use mail's rfc2047 to encode any string
	addr := mail.Address{Name: input, Address: ""}
	s := addr.String()
	return s[:len(s)-3]
}

// qEncodeAndWrap encodes the input as potentially multiple 'encoded-words'
// with CRLF SPACE line breaks between them to (as best as possible)
// guarantee that each encoded-word is no more than 75 characters
// and, padding included, each line is no longer than 76 characters.
// See RFC 2047 s2.
func qEncodeAndWrap(input string, padding int) string {

	// Split at any whitespace but prefer "; " or  ", " or " >" or "> " which
	// denotes a clear semantic break.
	// Remember that the qEncoded input isn't guaranteed to have the same
	// length as the unencoded input (obvious). Example: http://play.golang.org/p/dXA5IJnL22

	// Increase the padding to account for
	// the encoded-word 'envelop' tokens.
	// "?" charset (utf-8 is always assumed) "?" encoding "?" encoded-text "?="
	padding += 11

	// Tokenization included, the encoded word must not
	// be longer than 75 characters.
	const maxEncodedWordLength = 75

	var firstTry = qEncode(input)
	if len(firstTry) > maxEncodedWordLength-padding {

		// TODO(JPOEHLS): Implement an algorithm to break the input into multiple encoded-words.

		return firstTry
	} else {
		return firstTry
	}
}

// getAddressListString encodes a slice of email addresses into
// a string value suitable for a MIME header. Each address is
// Q encoded and wrapped onto its own line to help ensure that
// the header line doesn't cross the 78 character maximum.
func getAddressListString(addresses []mail.Address) string {
	var addressStrings []string

	for _, address := range addresses {
		addressStrings = append(addressStrings, address.String())
	}
	return strings.Join(addressStrings, ","+crlf+" ")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"
)

var (
	OptTrigger string
	OptAbort   bool
	OptVerbose bool
	Triggers   []string
	Failed     bool
)

func main() {
	flag.StringVar(&OptTrigger, "match", "", "Space seperate strings to match. If a match is found the exit code will be 0")
	flag.BoolVar(&OptAbort, "abort", false, "Abort a running command if a match is found. If abort is not passed the command is allowed to run to completion")
	flag.BoolVar(&OptVerbose, "v", false, "Verbose. Print the reason why we failed the command.")
	flag.Parse()

	// Parse the triggers
	Triggers = strings.Split(OptTrigger, " ")

	if args := flag.Args(); len(args) != 0 {
		root := args[0]
		rest := args[1:]
		cmd := exec.Command(root, rest...)
		stderrPipe, err := cmd.StderrPipe()
		if err != nil {
			log.Fatal(err)
		}
		stdoutPipe, err := cmd.StdoutPipe()
		if err != nil {
			log.Fatal(err)
		}
		err = cmd.Start()
		if err != nil {
			log.Fatal(err)
		}

		// Collect stdout and stderr
		stdoutbuff := make([]byte, 1024)
		stderrbuff := make([]byte, 1024)

		cmd.Run()

		for {
			//@@TODO: Move this to buffio.Scanner and two seperate co-running go-routines

			done := false

			// stdout
			n, err := stdoutPipe.Read(stdoutbuff)
			if err != nil {
				if err == io.EOF {
					done = true
				} else {
					log.Fatal(err)
				}
			}
			os.Stdout.Write(stdoutbuff[:n])

			// stderr
			n, err = stderrPipe.Read(stderrbuff)
			if err != nil {
				if err == io.EOF {
					done = true
				} else {
					log.Fatal(err)
				}
			}
			os.Stdout.Write(stderrbuff[:n])

			// If stdout contains a trigger, log it and possibly abort
			for _, Trigger := range Triggers {
				if strings.Contains(string(stdoutbuff), Trigger) {
					Failed = true
					if OptVerbose {
						log.Println("Command Failed. Found \"" + Trigger + "\"")
					}
					if OptAbort {
						if !done {
							cmd.Process.Kill() // abort the running command
						}
						done = true
					}
				}
			}

			// If stderr contains a trigger, log it and possibly abort
			for _, Trigger := range Triggers {
				if strings.Contains(string(stderrbuff), Trigger) {
					Failed = true
					if OptVerbose {
						log.Println("Command Failed. Found \"" + Trigger + "\"")
					}
					if OptAbort {
						if !done {
							cmd.Process.Kill() // abort the running command
						}
						done = true
					}
				}
			}

			if done {
				break
			}
		}

		err = cmd.Wait()
		if err != nil { // If we have an error abort everything
			if err.Error() == "signal: killed" { // We killed the process intentionally, don't report it
				os.Exit(1)
			} else {
				log.Fatal(err)
			}
		}

		// Command complete, report pass / fail via exit status
		if Failed {
			os.Exit(1)
		} else {
			os.Exit(0)
		}
	} else {
		fmt.Println("checkfail let's you fail a program with an exit status of 1 if a string appears in it's output (either stderr or stdout). Use `checkfail --help` to see a list of available options.")
	}
}
<commit_msg>Refactoring ahead of splitting off a library<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"
	"syscall"
)

var (
	OptTrigger string
	OptAbort   bool
	OptVerbose bool
	Triggers   []string
	Failed     bool
)

func main() {
	flag.StringVar(&OptTrigger, "match", "", "Space seperate strings to match. If a match is found the exit code will be 0")
	flag.BoolVar(&OptAbort, "abort", false, "Abort a running command if a match is found. If abort is not passed the command is allowed to run to completion")
	flag.BoolVar(&OptVerbose, "v", false, "Verbose. Print the reason why we failed the command.")
	flag.Parse()

	// Parse the triggers
	Triggers = strings.Split(OptTrigger, " ")

	args := flag.Args()

	if len(args) == 0 {
		fmt.Println("triggerfail let's you fail a program with an exit status of 1 if a string appears in it's output (either stderr or stdout). Use `checkfail --help` to see a list of available options.")
		os.Exit(0)
	}

	root := args[0]
	rest := args[1:]
	cmd := exec.Command(root, rest...)

	found, err := RunCommand(cmd, Triggers, os.Stdout, os.Stderr, OptAbort)
	if OptVerbose && len(found) != 0 {
		for _, trig := range found {
			fmt.Println("Found trigger " + trig)
		}
	}
	if err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			// The program has exited with an exit code != 0
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				os.Exit(status.ExitStatus())
			} else {
				// Unknown non-zero exit-status, exit with status 1
				os.Exit(1)
			}
		} else {
			// triggerfail failed internally for some reason
			log.Fatal(err.Error())
		}
	}
	if len(found) == 0 {
		os.Exit(0)
	} else {
		os.Exit(1)
	}
}

// Run a command, checking if triggers were found in it's output
// It returns a list of triggers found in the stdout and stderr of the running command
// This command takes the following arguments:
//   - *exec.Cmd - a pointer to an exec.Cmd (usually created by the exec.Command func). It can be configured as usual with the exception of stdin and stdout which should be left as is.
//   - []string  - A list of trigger strings. The command will be failed if it's stdout or stderr contains these strings.
//   - io.Writer - After consuming the stdout of the command to check for triggers, we pass along the results to this writer. Usually it would be set to os.Stdout or os.DevNull
//   - io.Writer - After consuming the stderr of the command to check for triggers, we pass along the results to this writer. Usually it would be set to os.Stderr or os.DevNull
//   - bool      - Abort the running commmand if a trigger word is found. If set to false the command will be allowed to run to completion, even with triggers.
func RunCommand(cmd *exec.Cmd, triggers []string, stdout io.Writer, stderr io.Writer, abort bool) ([]string, error) {
	found := make([]string, 0)

	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		return found, err
	}
	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return found, err
	}
	err = cmd.Start()
	if err != nil {
		return found, err
	}

	// Collect stdout and stderr
	stdoutbuff := make([]byte, 1024)
	stderrbuff := make([]byte, 1024)

	cmd.Run()

	for {
		//@@TODO: Move this to buffio.Scanner and two seperate co-running go-routines

		done := false

		// stdout
		n, err := stdoutPipe.Read(stdoutbuff)
		if err != nil {
			if err == io.EOF {
				done = true
			} else {
				return found, err
			}
		}
		stdout.Write(stdoutbuff[:n])

		// stderr
		n, err = stderrPipe.Read(stderrbuff)
		if err != nil {
			if err == io.EOF {
				done = true
			} else {
				return found, err
			}
		}
		stderr.Write(stderrbuff[:n])

		// If stdout contains a trigger, log it and possibly abort
		for _, Trigger := range Triggers {
			if strings.Contains(string(stdoutbuff), Trigger) {
				found = append(found, Trigger) //@@TODO: mutex
				if abort {
					if !done {
						cmd.Process.Kill() // abort the running command
					}
					done = true
				}
			}
		}

		// If stderr contains a trigger, log it and possibly abort
		for _, Trigger := range Triggers {
			if strings.Contains(string(stderrbuff), Trigger) {
				found = append(found, Trigger) //@@TODO: mutex
				if abort {
					if !done {
						cmd.Process.Kill() // abort the running command
					}
					done = true
				}
			}
		}

		if done {
			break
		}
	}

	err = cmd.Wait()
	if err != nil { // If we have an error abort everything
		if err.Error() == "signal: killed" { // We killed the process intentionally, don't report the error
			return found, nil
		} else {
			return found, err
		}
	}

	// Command complete
	return found, nil
}
<|endoftext|>